<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[212946] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/212946">212946</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-02-23 22:57:04 -0800 (Thu, 23 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>New sampling algorithm shows very few points when zoomed out
https://bugs.webkit.org/show_bug.cgi?id=168813

Reviewed by Saam Barati.

When a chart is zoomed out to a large time interval, the new sampling algorithm introduced in <a href="http://trac.webkit.org/projects/webkit/changeset/212853">r212853</a> can
hide most of the data points because the difference between the preceding point's time and the succeeding
point's time of most points will be below the threshold we computed.

Instead, rank each data point based on the aforementioned time interval difference, and pick the first M data
points when M data points are to be shown.

This makes the new algorithm behave like our old algorithm while keeping it stable still. Note that this
algorithm still biases data points without a close neighboring point but this seems to work out in practice
because such a point tends to be an important sample anyway, and we don't have a lot of space between
data points since we aim to show about one point per pixel.

* browser-tests/index.html:
(CanvasTest.canvasContainsColor): Extracted from one of the test cases and generalized. Returns true when
the specified region of the canvas contains a specified color (alpha is optional).
* browser-tests/time-series-chart-tests.js: Added a test case for sampling. It checks that sampling happens
and that we always show some data point even when zoomed out to a large time interval.
(createChartWithSampleCluster):

* public/v3/components/interactive-time-series-chart.js:
(InteractiveTimeSeriesChart.prototype._sampleTimeSeries):
* public/v3/components/time-series-chart.js:
(TimeSeriesChart.prototype._ensureSampledTimeSeries): M, the number of data points we pick must be computed
based on the width of data points we're about to draw constrained by the canvas size. e.g. when the canvas
is only half filled, we shouldn't be showing two points per pixel in the filled region.
(TimeSeriesChart.prototype._sampleTimeSeries): Refined the algorithm. First, compute the time difference or
the rank for each N data points. Sort those ranks in descending order (in the order we prefer), and include
all data points above the M-th rank in the sample.
(TimeSeriesChart.prototype.computeTimeGrid): Revert the inadvertent change in <a href="http://trac.webkit.org/projects/webkit/changeset/212935">r212935</a>.

* public/v3/models/time-series.js:
(TimeSeriesView.prototype.filter): Fixed a bug that the indices passed onto the callback were shifted by the
starting index.
* unit-tests/time-series-tests.js: Added a test case to ensure callbacks are called with correct data points
and indices.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsindexhtml">trunk/Websites/perf.webkit.org/browser-tests/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowserteststimeseriescharttestsjs">trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentsinteractivetimeserieschartjs">trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentstimeserieschartjs">trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3modelstimeseriesjs">trunk/Websites/perf.webkit.org/public/v3/models/time-series.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgunitteststimeseriestestsjs">trunk/Websites/perf.webkit.org/unit-tests/time-series-tests.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -1,5 +1,48 @@
</span><span class="cx"> 2017-02-23  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        New sampling algorithm shows very few points when zoomed out
+        https://bugs.webkit.org/show_bug.cgi?id=168813
+
+        Reviewed by Saam Barati.
+
+        When a chart is zoomed out to a large time interval, the new sampling algorithm introduced in r212853 can
+        hide most of the data points because the difference between the preceding point's time and the succeeding
+        point's time of most points will be below the threshold we computed.
+
+        Instead, rank each data point based on the aforementioned time interval difference, and pick the first M data
+        points when M data points are to be shown.
+
+        This makes the new algorithm behave like our old algorithm while keeping it stable still. Note that this
+        algorithm still biases data points without a close neighboring point but this seems to work out in practice
+        because such a point tends to be an important sample anyway, and we don't have a lot of space between
+        data points since we aim to show about one point per pixel.
+
+        * browser-tests/index.html:
+        (CanvasTest.canvasContainsColor): Extracted from one of the test cases and generalized. Returns true when
+        the specified region of the canvas contains a specified color (alpha is optional).
+        * browser-tests/time-series-chart-tests.js: Added a test case for sampling. It checks that sampling happens
+        and that we always show some data point even when zoomed out to a large time interval.
+        (createChartWithSampleCluster):
+
+        * public/v3/components/interactive-time-series-chart.js:
+        (InteractiveTimeSeriesChart.prototype._sampleTimeSeries):
+        * public/v3/components/time-series-chart.js:
+        (TimeSeriesChart.prototype._ensureSampledTimeSeries): M, the number of data points we pick must be computed
+        based on the width of data points we're about to draw constrained by the canvas size. e.g. when the canvas
+        is only half filled, we shouldn't be showing two points per pixel in the filled region.
+        (TimeSeriesChart.prototype._sampleTimeSeries): Refined the algorithm. First, compute the time difference or
+        the rank for each N data points. Sort those ranks in descending order (in the order we prefer), and include
+        all data points above the M-th rank in the sample.
+        (TimeSeriesChart.prototype.computeTimeGrid): Revert the inadvertent change in r212935.
+
+        * public/v3/models/time-series.js:
+        (TimeSeriesView.prototype.filter): Fixed a bug that the indices passed onto the callback were shifted by the
+        starting index.
+        * unit-tests/time-series-tests.js: Added a test case to ensure callbacks are called with correct data points
+        and indices.
+
+2017-02-23  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         REGRESSION(r212542): Make TimeSeriesChart.computeTimeGrid stops x-axis grid prematurely
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=168812
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/index.html (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -163,6 +163,23 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     canvasImageData(canvas) { return canvasImageData(canvas); },
</span><ins>+
+    canvasContainsColor(canvas, color, rect = {})
+    {
+        const content = canvas.getContext('2d').getImageData(rect.x || 0, rect.y || 0, rect.width || canvas.width, rect.height || canvas.height);
+        let found = false;
+        const data = content.data;
+        for (let startOfPixel = 0; startOfPixel &lt; data.length; startOfPixel += 4) {
+            let r = data[startOfPixel];
+            let g = data[startOfPixel + 1];
+            let b = data[startOfPixel + 2];
+            let a = data[startOfPixel + 3];
+            if (r == color.r &amp;&amp; g == color.g &amp;&amp; b == color.b &amp;&amp; (color.a == undefined || a == color.a))
+                return true;
+        }
+        return false;
+    },
+
</ins><span class="cx">     expectCanvasesMatch(canvas1, canvas2) { return canvasRefTest(canvas1, canvas2, true); },
</span><span class="cx">     expectCanvasesMismatch(canvas1, canvas2) { return canvasRefTest(canvas1, canvas2, false); },
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowserteststimeseriescharttestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -77,9 +77,11 @@
</span><span class="cx">     const chart = new TimeSeriesChart([
</span><span class="cx">         {
</span><span class="cx">             type: 'current',
</span><ins>+            lineStyle: options.lineStyle || '#666',
</ins><span class="cx">             measurementSet: MeasurementSet.findSet(1, 1, 0),
</span><span class="cx">             interactive: options.interactive || false,
</span><del>-            includeOutliers: options.includeOutliers || false
</del><ins>+            includeOutliers: options.includeOutliers || false,
+            sampleData: options.sampleData || false,
</ins><span class="cx">         }], chartOptions);
</span><span class="cx">     const element = chart.element();
</span><span class="cx">     element.style.width = options.width || '300px';
</span><span class="lines">@@ -541,7 +543,6 @@
</span><span class="cx"> 
</span><span class="cx">                 expect(chart.content().querySelector('canvas')).to.be(null);
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><del>-                    console.log('done')
</del><span class="cx">                     expect(chart.content().querySelector('canvas')).to.not.be(null);
</span><span class="cx">                 });
</span><span class="cx">             });
</span><span class="lines">@@ -828,23 +829,54 @@
</span><span class="cx">                     CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1);
</span><span class="cx">                     CanvasTest.expectCanvasesMismatch(canvasWithYAxis1, canvasWithYAxis2);
</span><span class="cx"> 
</span><del>-                    let content1 = CanvasTest.canvasImageData(canvasWithYAxis1);
-                    let foundGridLine = false;
-                    for (let y = 0; y &lt; content1.height; y++) {
-                        let endOfY = content1.width * 4 * y;
-                        let r = content1.data[endOfY - 4];
-                        let g = content1.data[endOfY - 3];
-                        let b = content1.data[endOfY - 2];
-                        if (r == 204 &amp;&amp; g == 204 &amp;&amp; b == 204) {
-                            foundGridLine = true;
-                            break;
-                        }
-                    }
-                    expect(foundGridLine).to.be(true);
</del><ins>+                    expect(CanvasTest.canvasContainsColor(canvasWithYAxis1, {r: 204, g: 204, b: 204},
+                        {x: canvasWithYAxis1.width - 1, width: 1, y: 0, height: canvasWithYAxis1.height})).to.be(true);
</ins><span class="cx">                 });
</span><span class="cx">             });
</span><span class="cx">         });
</span><span class="cx"> 
</span><ins>+        it('should render the sampled time series', () =&gt; {
+            const context = new BrowsingContext();
+            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
+                const chartWithoutSampling = createChartWithSampleCluster(context, {}, {lineStyle: 'rgb(0, 128, 255)', width: '100px', height: '100px', sampleData: false});
+                const chartWithSampling = createChartWithSampleCluster(context, {}, {lineStyle: 'rgb(0, 128, 255)', width: '100px', height: '100px', sampleData: true});
+
+                chartWithoutSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
+                chartWithoutSampling.fetchMeasurementSets();
+                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                chartWithSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
+                chartWithSampling.fetchMeasurementSets();
+
+                let canvasWithSampling;
+                let canvasWithoutSampling;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    canvasWithoutSampling = chartWithoutSampling.content().querySelector('canvas');
+                    canvasWithSampling = chartWithSampling.content().querySelector('canvas');
+
+                    CanvasTest.expectCanvasesMatch(canvasWithSampling, canvasWithoutSampling);
+                    expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, {r: 0, g: 128, b: 255})).to.be(true);
+                    expect(CanvasTest.canvasContainsColor(canvasWithSampling, {r: 0, g: 128, b: 255})).to.be(true);
+
+                    const diff = sampleCluster.endTime - sampleCluster.startTime;
+                    chartWithoutSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
+                    chartWithSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithoutSampling);
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithSampling);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithoutSampling)).to.be(true);
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithSampling)).to.be(true);
+
+                    expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, {r: 0, g: 128, b: 255})).to.be(true);
+                    expect(CanvasTest.canvasContainsColor(canvasWithSampling, {r: 0, g: 128, b: 255})).to.be(true);
+
+                    CanvasTest.expectCanvasesMismatch(canvasWithSampling, canvasWithoutSampling);
+                });
+            });
+        });
+
</ins><span class="cx">         it('should render annotations', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><span class="cx">             return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentsinteractivetimeserieschartjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -382,11 +382,11 @@
</span><span class="cx">         return metrics;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _sampleTimeSeries(data, minimumTimeDiff, excludedPoints)
</del><ins>+    _sampleTimeSeries(data, maximumNumberOfPoints, excludedPoints)
</ins><span class="cx">     {
</span><span class="cx">         if (this._indicatorID)
</span><span class="cx">             excludedPoints.add(this._indicatorID);
</span><del>-        return super._sampleTimeSeries(data, minimumTimeDiff, excludedPoints);
</del><ins>+        return super._sampleTimeSeries(data, maximumNumberOfPoints, excludedPoints);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _renderChartContent(context, metrics)
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentstimeserieschartjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -490,9 +490,6 @@
</span><span class="cx">             if (!timeSeries)
</span><span class="cx">                 return null;
</span><span class="cx"> 
</span><del>-            // A chart with X px width shouldn't have more than 2X / &lt;radius-of-points&gt; data points.
-            const maximumNumberOfPoints = 2 * metrics.chartWidth / (source.pointRadius || 2);
-
</del><span class="cx">             const pointAfterStart = timeSeries.findPointAfterTime(startTime);
</span><span class="cx">             const pointBeforeStart = (pointAfterStart ? timeSeries.previousPoint(pointAfterStart) : null) || timeSeries.firstPoint();
</span><span class="cx">             const pointAfterEnd = timeSeries.findPointAfterTime(endTime) || timeSeries.lastPoint();
</span><span class="lines">@@ -504,7 +501,11 @@
</span><span class="cx">             if (!source.sampleData)
</span><span class="cx">                 return view;
</span><span class="cx"> 
</span><del>-            return this._sampleTimeSeries(view, (endTime - startTime) / maximumNumberOfPoints, new Set);
</del><ins>+            // A chart with X px width shouldn't have more than 2X / &lt;radius-of-points&gt; data points.
+            const viewWidth = Math.min(metrics.chartWidth, metrics.timeToX(pointAfterEnd.time) - metrics.timeToX(pointBeforeStart.time));
+            const maximumNumberOfPoints = 2 * viewWidth / (source.pointRadius || 2);
+
+            return this._sampleTimeSeries(view, maximumNumberOfPoints, new Set);
</ins><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         Instrumentation.endMeasuringTime('TimeSeriesChart', 'ensureSampledTimeSeries');
</span><span class="lines">@@ -514,19 +515,27 @@
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _sampleTimeSeries(view, minimumTimeDiff, excludedPoints)
</del><ins>+    _sampleTimeSeries(view, maximumNumberOfPoints, excludedPoints)
</ins><span class="cx">     {
</span><del>-        if (view.length() &lt; 2)
</del><ins>+
+        if (view.length() &lt; 2 || maximumNumberOfPoints &gt;= view.length() || maximumNumberOfPoints &lt; 1)
</ins><span class="cx">             return view;
</span><span class="cx"> 
</span><span class="cx">         Instrumentation.startMeasuringTime('TimeSeriesChart', 'sampleTimeSeries');
</span><span class="cx"> 
</span><del>-        const sampledData = view.filter((point, i) =&gt; {
-            if (excludedPoints.has(point.id))
-                return true;
</del><ins>+        let ranks = new Array(view.length());
+        let i = 0;
+        for (let point of view) {
</ins><span class="cx">             let previousPoint = view.previousPoint(point) || point;
</span><span class="cx">             let nextPoint = view.nextPoint(point) || point;
</span><del>-            return nextPoint.time - previousPoint.time &gt;= minimumTimeDiff;
</del><ins>+            ranks[i] = nextPoint.time - previousPoint.time;
+            i++;
+        }
+
+        const sortedRanks = ranks.slice(0).sort((a, b) =&gt; b - a);
+        const minimumRank = sortedRanks[Math.floor(maximumNumberOfPoints)];
+        const sampledData = view.filter((point, i) =&gt; {
+            return excludedPoints.has(point.id) || ranks[i] &gt;= minimumRank;
</ins><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         Instrumentation.endMeasuringTime('TimeSeriesChart', 'sampleTimeSeries');
</span><span class="lines">@@ -619,7 +628,7 @@
</span><span class="cx"> 
</span><span class="cx">         let previousDate = null;
</span><span class="cx">         let previousMonth = null;
</span><del>-        while (currentTime &lt;= max) {
</del><ins>+        while (currentTime &lt;= max &amp;&amp; result.length &lt; maxLabels) {
</ins><span class="cx">             const time = new Date(currentTime);
</span><span class="cx">             const month = time.getUTCMonth() + 1;
</span><span class="cx">             const date = time.getUTCDate();
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3modelstimeseriesjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/models/time-series.js (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/models/time-series.js        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/public/v3/models/time-series.js        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -167,11 +167,12 @@
</span><span class="cx"> 
</span><span class="cx">     filter(callback)
</span><span class="cx">     {
</span><del>-        const data = this._data;
</del><span class="cx">         const filteredData = [];
</span><del>-        for (let i = this._startingIndex; i &lt; this._afterEndingIndex; i++) {
-            if (callback(data[i], i))
-                filteredData.push(data[i]);
</del><ins>+        let i = 0;
+        for (let point of this) {
+            if (callback(point, i))
+                filteredData.push(point);
+            i++;
</ins><span class="cx">         }
</span><span class="cx">         return new TimeSeriesView(this._timeSeries, 0, filteredData.length, filteredData);
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgunitteststimeseriestestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/unit-tests/time-series-tests.js (212945 => 212946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/unit-tests/time-series-tests.js        2017-02-24 06:52:02 UTC (rev 212945)
+++ trunk/Websites/perf.webkit.org/unit-tests/time-series-tests.js        2017-02-24 06:57:04 UTC (rev 212946)
</span><span class="lines">@@ -276,6 +276,20 @@
</span><span class="cx"> describe('TimeSeriesView', () =&gt; {
</span><span class="cx"> 
</span><span class="cx">     describe('filter', () =&gt; {
</span><ins>+        it('should call callback with an element in the view and its index', () =&gt; {
+            const timeSeries = new TimeSeries();
+            addPointsToSeries(timeSeries, fivePoints);
+            const originalView = timeSeries.viewBetweenPoints(fivePoints[1], fivePoints[3]);
+            const points = [];
+            const indices = [];
+            const view = originalView.filter((point, index) =&gt; {
+                points.push(point);
+                indices.push(index);
+            });
+            assert.deepEqual(points, [fivePoints[1], fivePoints[2], fivePoints[3]]);
+            assert.deepEqual(indices, [0, 1, 2]);
+        });
+
</ins><span class="cx">         it('should create a filtered view', () =&gt; {
</span><span class="cx">             const timeSeries = new TimeSeries();
</span><span class="cx">             addPointsToSeries(timeSeries, fivePoints);
</span></span></pre>
</div>
</div>

</body>
</html>