<!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>[164432] trunk/Source/WebInspectorUI</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/164432">164432</a></dd>
<dt>Author</dt> <dd>graouts@webkit.org</dd>
<dt>Date</dt> <dd>2014-02-20 09:27:16 -0800 (Thu, 20 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Popover should animate its frame to display its refreshed content
https://bugs.webkit.org/show_bug.cgi?id=129088

Reviewed by Timothy Hatcher.

When calling .update(), we now check whether we can animate the change of frame provided the
new computed frame to best fit the current content still matches the edge the popover uses
to attach to the target frame. If we find that we can do so, we animate the background frame
of the popover while ensuring the anchor point remains stable during the animation such that
only the popover's frame seems to animate.

* UserInterface/Geometry.js:
(WebInspector.Rect.prototype.round):
Returns a new Rect with rounded values, using a floor for the position and a ceil for the size.

* UserInterface/Main.html:
Link to the new UnitBezier.js source file.

* UserInterface/Popover.js:
(WebInspector.Popover):
Make ._anchorPoint an ivar such that we don't need to pass a reference to the anchorPoint into
the various calls leading to an update of the popover's background drawing.

(WebInspector.Popover.prototype.set frame):
We no longer round the values of the frame here, instead calling the new .round() method on Rect
in places where we compute a new frame.

(WebInspector.Popover.prototype.set content):
(WebInspector.Popover.prototype.update):
Update the calls to ._update() to set the new shouldAnimate flag to true in situations where the
popover is already visible.

(WebInspector.Popover.prototype._update):
In the situation where there is a preference to animate the frame, as set by the new shouldAnimate
parameter, check that we can indeed animate by ensuring that the edge the popover uses to attach to
the target frame remains the same upon computing the new best metrics for the new content size. If
we can indeed animate, call _animateFrame(), otherwise set the new frame, anchor point and frame
drawing discretely like we used to.

(WebInspector.Popover.prototype._setAnchorPoint):
New method to ensure we floor the position of the anchor point to ensure, when animating, that the
anchor point remains stationary.

(WebInspector.Popover.prototype._animateFrame):
Using the new UnitBezier class, animate the popover frame from its previous value to its newly computed
value while ensuring the anchor point remains, at all times, the same absolute position such that it
remains stationary during the animation. The spline used to animate the frame is the same that a CSS
transition set with an &quot;ease&quot; timing-function (default value) would use.

(WebInspector.Popover.prototype._drawBackground):
(WebInspector.Popover.prototype._drawFrame):
Adopt new ._edge and ._anchorPoint ivars.

* UserInterface/UnitBezier.js: Added.
New class used to perform animations using a timing function specified with a cubic Bézier curve. The code
is directly adapted from the Source/WebCore/platform/graphics/UnitBezier.h file.

(WebInspector.UnitBezier):
(WebInspector.UnitBezier.prototype.solve):
(WebInspector.UnitBezier.prototype._sampleCurveX):
(WebInspector.UnitBezier.prototype._sampleCurveY):
(WebInspector.UnitBezier.prototype._sampleCurveDerivativeX):
(WebInspector.UnitBezier.prototype._solveCurveX):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceGeometryjs">trunk/Source/WebInspectorUI/UserInterface/Geometry.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceMainhtml">trunk/Source/WebInspectorUI/UserInterface/Main.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfacePopoverjs">trunk/Source/WebInspectorUI/UserInterface/Popover.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceUnitBezierjs">trunk/Source/WebInspectorUI/UserInterface/UnitBezier.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (164431 => 164432)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2014-02-20 17:19:18 UTC (rev 164431)
+++ trunk/Source/WebInspectorUI/ChangeLog        2014-02-20 17:27:16 UTC (rev 164432)
</span><span class="lines">@@ -1,5 +1,71 @@
</span><span class="cx"> 2014-02-20  Antoine Quint  &lt;graouts@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        Web Inspector: Popover should animate its frame to display its refreshed content
+        https://bugs.webkit.org/show_bug.cgi?id=129088
+
+        Reviewed by Timothy Hatcher.
+
+        When calling .update(), we now check whether we can animate the change of frame provided the
+        new computed frame to best fit the current content still matches the edge the popover uses
+        to attach to the target frame. If we find that we can do so, we animate the background frame
+        of the popover while ensuring the anchor point remains stable during the animation such that
+        only the popover's frame seems to animate.
+
+        * UserInterface/Geometry.js:
+        (WebInspector.Rect.prototype.round):
+        Returns a new Rect with rounded values, using a floor for the position and a ceil for the size.
+
+        * UserInterface/Main.html:
+        Link to the new UnitBezier.js source file.
+
+        * UserInterface/Popover.js:
+        (WebInspector.Popover):
+        Make ._anchorPoint an ivar such that we don't need to pass a reference to the anchorPoint into
+        the various calls leading to an update of the popover's background drawing.
+
+        (WebInspector.Popover.prototype.set frame):
+        We no longer round the values of the frame here, instead calling the new .round() method on Rect
+        in places where we compute a new frame.
+
+        (WebInspector.Popover.prototype.set content):
+        (WebInspector.Popover.prototype.update):
+        Update the calls to ._update() to set the new shouldAnimate flag to true in situations where the
+        popover is already visible.
+
+        (WebInspector.Popover.prototype._update):
+        In the situation where there is a preference to animate the frame, as set by the new shouldAnimate
+        parameter, check that we can indeed animate by ensuring that the edge the popover uses to attach to
+        the target frame remains the same upon computing the new best metrics for the new content size. If
+        we can indeed animate, call _animateFrame(), otherwise set the new frame, anchor point and frame
+        drawing discretely like we used to.
+
+        (WebInspector.Popover.prototype._setAnchorPoint):
+        New method to ensure we floor the position of the anchor point to ensure, when animating, that the
+        anchor point remains stationary.
+
+        (WebInspector.Popover.prototype._animateFrame):
+        Using the new UnitBezier class, animate the popover frame from its previous value to its newly computed
+        value while ensuring the anchor point remains, at all times, the same absolute position such that it
+        remains stationary during the animation. The spline used to animate the frame is the same that a CSS
+        transition set with an &quot;ease&quot; timing-function (default value) would use.
+
+        (WebInspector.Popover.prototype._drawBackground):
+        (WebInspector.Popover.prototype._drawFrame):
+        Adopt new ._edge and ._anchorPoint ivars.
+
+        * UserInterface/UnitBezier.js: Added.
+        New class used to perform animations using a timing function specified with a cubic Bézier curve. The code
+        is directly adapted from the Source/WebCore/platform/graphics/UnitBezier.h file.
+
+        (WebInspector.UnitBezier):
+        (WebInspector.UnitBezier.prototype.solve):
+        (WebInspector.UnitBezier.prototype._sampleCurveX):
+        (WebInspector.UnitBezier.prototype._sampleCurveY):
+        (WebInspector.UnitBezier.prototype._sampleCurveDerivativeX):
+        (WebInspector.UnitBezier.prototype._solveCurveX):
+
+2014-02-20  Antoine Quint  &lt;graouts@webkit.org&gt;
+
</ins><span class="cx">         Web Inspector: content using a CSS transition within a popover causes the popover to disappear
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=129089
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceGeometryjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Geometry.js (164431 => 164432)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Geometry.js        2014-02-20 17:19:18 UTC (rev 164431)
+++ trunk/Source/WebInspectorUI/UserInterface/Geometry.js        2014-02-20 17:27:16 UTC (rev 164432)
</span><span class="lines">@@ -182,6 +182,16 @@
</span><span class="cx">         intersection.origin.y = y1;
</span><span class="cx">         intersection.size.height = y2 - y1;
</span><span class="cx">         return intersection;
</span><ins>+    },
+    
+    round: function()
+    {
+        return new WebInspector.Rect(
+            Math.floor(this.origin.x),
+            Math.floor(this.origin.y),
+            Math.ceil(this.size.width),
+            Math.ceil(this.size.height)
+        );
</ins><span class="cx">     }
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceMainhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (164431 => 164432)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-02-20 17:19:18 UTC (rev 164431)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-02-20 17:27:16 UTC (rev 164432)
</span><span class="lines">@@ -260,6 +260,7 @@
</span><span class="cx">     &lt;script src=&quot;CSSKeywordCompletions.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;SyntaxHighlightingSupport.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Geometry.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;UnitBezier.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Popover.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;TextEditor.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;EventHandler.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfacePopoverjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Popover.js (164431 => 164432)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Popover.js        2014-02-20 17:19:18 UTC (rev 164431)
+++ trunk/Source/WebInspectorUI/UserInterface/Popover.js        2014-02-20 17:27:16 UTC (rev 164432)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx">     this._frame = new WebInspector.Rect;
</span><span class="cx">     this._content = null;
</span><span class="cx">     this._targetFrame = new WebInspector.Rect;
</span><ins>+    this._anchorPoint = new WebInspector.Point;
</ins><span class="cx">     this._preferredEdges = null;
</span><span class="cx"> 
</span><span class="cx">     this._contentNeedsUpdate = false;
</span><span class="lines">@@ -78,11 +79,11 @@
</span><span class="cx"> 
</span><span class="cx">     set frame(frame)
</span><span class="cx">     {
</span><del>-        this._element.style.left = Math.round(frame.origin.x) + &quot;px&quot;;
-        this._element.style.top = Math.round(frame.origin.y) + &quot;px&quot;;
-        this._element.style.width = Math.ceil(frame.size.width) + &quot;px&quot;;
-        this._element.style.height = Math.ceil(frame.size.height) + &quot;px&quot;;
-        this._element.style.backgroundSize = Math.ceil(frame.size.width) + &quot;px &quot; + Math.ceil(frame.size.height) + &quot;px&quot;;
</del><ins>+        this._element.style.left = frame.minX() + &quot;px&quot;;
+        this._element.style.top = frame.minY() + &quot;px&quot;;
+        this._element.style.width = frame.size.width + &quot;px&quot;;
+        this._element.style.height = frame.size.height + &quot;px&quot;;
+        this._element.style.backgroundSize = frame.size.width + &quot;px &quot; + frame.size.height + &quot;px&quot;;
</ins><span class="cx">         this._frame = frame;
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -96,7 +97,7 @@
</span><span class="cx">         this._contentNeedsUpdate = true;
</span><span class="cx"> 
</span><span class="cx">         if (this.visible)
</span><del>-            this._update();
</del><ins>+            this._update(true);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     update: function()
</span><span class="lines">@@ -107,7 +108,7 @@
</span><span class="cx">         var previouslyFocusedElement = document.activeElement;
</span><span class="cx"> 
</span><span class="cx">         this._contentNeedsUpdate = true;
</span><del>-        this._update();
</del><ins>+        this._update(true);
</ins><span class="cx"> 
</span><span class="cx">         if (previouslyFocusedElement)
</span><span class="cx">             previouslyFocusedElement.focus();
</span><span class="lines">@@ -167,8 +168,11 @@
</span><span class="cx"> 
</span><span class="cx">     // Private
</span><span class="cx"> 
</span><del>-    _update: function()
</del><ins>+    _update: function(shouldAnimate)
</ins><span class="cx">     {
</span><ins>+        if (shouldAnimate)
+            var previousEdge = this._edge;
+
</ins><span class="cx">         var targetFrame = this._targetFrame;
</span><span class="cx">         var preferredEdges = this._preferredEdges;
</span><span class="cx"> 
</span><span class="lines">@@ -232,12 +236,11 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         var anchorPoint;
</span><del>-        var bestFrame = bestMetrics.frame;
</del><ins>+        var bestFrame = bestMetrics.frame.round();
</ins><span class="cx"> 
</span><del>-        this.frame = bestFrame;
</del><span class="cx">         this._edge = bestEdge;
</span><span class="cx"> 
</span><del>-        if (this.frame === WebInspector.Rect.ZERO_RECT) {
</del><ins>+        if (bestFrame === WebInspector.Rect.ZERO_RECT) {
</ins><span class="cx">             // The target for the popover is offscreen.
</span><span class="cx">             this.dismiss();
</span><span class="cx">         } else {
</span><span class="lines">@@ -258,7 +261,13 @@
</span><span class="cx"> 
</span><span class="cx">             this._element.classList.add(this._cssClassNameForEdge());
</span><span class="cx"> 
</span><del>-            this._drawBackground(bestEdge, anchorPoint);
</del><ins>+            if (shouldAnimate &amp;&amp; this._edge === previousEdge)
+                this._animateFrame(bestFrame);
+            else {
+                 this.frame = bestFrame;
+                 this._setAnchorPoint(anchorPoint);
+                 this._drawBackground();
+            }
</ins><span class="cx"> 
</span><span class="cx">             // Make sure content is centered in case either of the dimension is smaller than the minimal bounds.
</span><span class="cx">             if (this._preferredSize.width &lt; WebInspector.Popover.MinWidth || this._preferredSize.height &lt; WebInspector.Popover.MinHeight)
</span><span class="lines">@@ -293,8 +302,58 @@
</span><span class="cx">         return &quot;arrow-up&quot;;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _drawBackground: function(edge, anchorPoint)
</del><ins>+    _setAnchorPoint: function(anchorPoint) {
+        anchorPoint.x = Math.floor(anchorPoint.x);
+        anchorPoint.y = Math.floor(anchorPoint.y);
+        this._anchorPoint = anchorPoint;
+    },
+
+    _animateFrame: function(toFrame)
</ins><span class="cx">     {
</span><ins>+        var startTime = Date.now();
+        var duration = 350;
+        var epsilon = 1 / (200 * duration);
+        var spline = new WebInspector.UnitBezier(0.25, 0.1, 0.25, 1);
+
+        var fromFrame = this._frame.copy();
+
+        var absoluteAnchorPoint = new WebInspector.Point(
+            fromFrame.minX() + this._anchorPoint.x,
+            fromFrame.minY() + this._anchorPoint.y
+        );
+
+        function animatedValue(from, to, progress)
+        {
+            return from + (to - from) * progress;
+        }
+
+        function drawBackground()
+        {
+            var progress = spline.solve(Math.min((Date.now() - startTime) / duration, 1), epsilon);
+
+            this.frame = new WebInspector.Rect(
+                animatedValue(fromFrame.minX(), toFrame.minX(), progress),
+                animatedValue(fromFrame.minY(), toFrame.minY(), progress),
+                animatedValue(fromFrame.size.width, toFrame.size.width, progress),
+                animatedValue(fromFrame.size.height, toFrame.size.height, progress)
+            ).round();
+
+            this._setAnchorPoint(new WebInspector.Point(
+                absoluteAnchorPoint.x - this._frame.minX(),
+                absoluteAnchorPoint.y - this._frame.minY()
+            ));
+
+            this._drawBackground();
+
+            if (progress &lt; 1)
+                window.requestAnimationFrame(drawBackground.bind(this));
+        }
+
+        drawBackground.call(this);
+    },
+
+    _drawBackground: function()
+    {
</ins><span class="cx">         var scaleFactor = window.devicePixelRatio;
</span><span class="cx"> 
</span><span class="cx">         var width = this._frame.size.width;
</span><span class="lines">@@ -315,7 +374,7 @@
</span><span class="cx">         // of the content contained within the frame.
</span><span class="cx">         var bounds;
</span><span class="cx">         var arrowHeight = WebInspector.Popover.AnchorSize.height;
</span><del>-        switch (edge) {
</del><ins>+        switch (this._edge) {
</ins><span class="cx">         case WebInspector.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
</span><span class="cx">             bounds = new WebInspector.Rect(0, 0, width - arrowHeight, height);
</span><span class="cx">             break;
</span><span class="lines">@@ -334,7 +393,7 @@
</span><span class="cx"> 
</span><span class="cx">         // Clip the frame.
</span><span class="cx">         ctx.fillStyle = &quot;black&quot;;
</span><del>-        this._drawFrame(ctx, bounds, edge, anchorPoint);
</del><ins>+        this._drawFrame(ctx, bounds, this._edge, this._anchorPoint);
</ins><span class="cx">         ctx.clip();
</span><span class="cx"> 
</span><span class="cx">         // Gradient fill, top-to-bottom.
</span><span class="lines">@@ -347,7 +406,7 @@
</span><span class="cx">         // Stroke.
</span><span class="cx">         ctx.strokeStyle = &quot;rgba(0, 0, 0, 0.25)&quot;;
</span><span class="cx">         ctx.lineWidth = 2;
</span><del>-        this._drawFrame(ctx, bounds, edge, anchorPoint);
</del><ins>+        this._drawFrame(ctx, bounds, this._edge, this._anchorPoint);
</ins><span class="cx">         ctx.stroke();
</span><span class="cx"> 
</span><span class="cx">         // Draw the popover into the final context with a drop shadow.
</span><span class="lines">@@ -428,10 +487,11 @@
</span><span class="cx">         };
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _drawFrame: function(ctx, bounds, anchorEdge, anchorPoint)
</del><ins>+    _drawFrame: function(ctx, bounds, anchorEdge)
</ins><span class="cx">     {
</span><span class="cx">         var r = WebInspector.Popover.CornerRadius;
</span><span class="cx">         var arrowHalfLength = WebInspector.Popover.AnchorSize.width / 2;
</span><ins>+        var anchorPoint = this._anchorPoint;
</ins><span class="cx"> 
</span><span class="cx">         ctx.beginPath();
</span><span class="cx">         switch (anchorEdge) {
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceUnitBezierjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/UnitBezier.js (0 => 164432)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/UnitBezier.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/UnitBezier.js        2014-02-20 17:27:16 UTC (rev 164432)
</span><span class="lines">@@ -0,0 +1,108 @@
</span><ins>+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.UnitBezier = function(x1, y1, x2, y2)
+{
+    WebInspector.Object.call(this);
+
+    // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
+    this._cx = 3.0 * x1;
+    this._bx = 3.0 * (x2 - x1) - this._cx;
+    this._ax = 1.0 - this._cx - this._bx;
+
+    this._cy = 3.0 * y1;
+    this._by = 3.0 * (y2 - y1) - this._cy;
+    this._ay = 1.0 - this._cy - this._by;
+};
+
+WebInspector.UnitBezier.prototype = {
+    constructor: WebInspector.UnitBezier,
+
+    // Public
+
+    solve: function(x, epsilon)
+    {
+        return this._sampleCurveY(this._solveCurveX(x, epsilon));
+    },
+
+    // Private
+
+    _sampleCurveX: function(t)
+    {
+        // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+        return ((this._ax * t + this._bx) * t + this._cx) * t;
+    },
+
+    _sampleCurveY: function(t)
+    {
+        return ((this._ay * t + this._by) * t + this._cy) * t;
+    },
+
+    _sampleCurveDerivativeX: function(t)
+    {
+        return (3.0 * this._ax * t + 2.0 * this._bx) * t + this._cx;
+    },
+
+    // Given an x value, find a parametric value it came from.
+    _solveCurveX: function(x, epsilon)
+    {
+        var t0, t1, t2, x2, d2, i;
+
+        // First try a few iterations of Newton's method -- normally very fast.
+        for (t2 = x, i = 0; i &lt; 8; i++) {
+            x2 = this._sampleCurveX(t2) - x;
+            if (Math.abs(x2) &lt; epsilon)
+                return t2;
+            d2 = this._sampleCurveDerivativeX(t2);
+            if (Math.abs(d2) &lt; 1e-6)
+                break;
+            t2 = t2 - x2 / d2;
+        }
+
+        // Fall back to the bisection method for reliability.
+        t0 = 0.0;
+        t1 = 1.0;
+        t2 = x;
+
+        if (t2 &lt; t0)
+            return t0;
+        if (t2 &gt; t1)
+            return t1;
+
+        while (t0 &lt; t1) {
+            x2 = this._sampleCurveX(t2);
+            if (Math.abs(x2 - x) &lt; epsilon)
+                return t2;
+            if (x &gt; x2)
+                t0 = t2;
+            else
+                t1 = t2;
+            t2 = (t1 - t0) * 0.5 + t0;
+        }
+
+        // Failure.
+        return t2;
+    }
+};
</ins></span></pre>
</div>
</div>

</body>
</html>