Rather than both setting the user-select to none and suppressing the selectstart
event, we now prefer to suppress the selectstart event if supported. This avoids
redundantly setting the user-select style, which can be slow. Fixes#1599.
Rather than use d3_geo_transform, parts of d3.geo that need a point-based
geometry stream transform now use d3_geo_transformPoint, which is slightly more
efficient. (This is the same as the d3_geo_streamTransform that was removed in
ac9f51fa534f12de7bb9487fb89edcb8edfa3951.)
When resampling is turned off, d3_geo_resample can return a special
transform that only performs projection of points to 2D, rather than
performing any additional computations such as conversion to 3D
coordinates for resampling.
We we’re distinguishing between the case where the cell completely surrounds the
clip extent and where the cell does not intersect the clip extent at all.
Related #1578.
If data is updated so that some nodes no longer have children, and a
custom children accessor is used, then the children array was not being
removed for such nodes, which meant that hierarchical layouts were
incorrectly taking into account these children.
Fixes#1579.
This appears to cause a significant performance regression as shown in the
rotating Ocean example (http://bl.ocks.org/mbostock/6713736): performance went
from 60 FPS to 20 FPS.
Previously it was using Date.now(), which might be a millisecond or two past the
now time of the current frame, and thus might not run until the next frame! By
using the current time, we guarantee that it runs at the end of this frame.
Rather than interleave the initialization of transition tweens with their first
tick, defer the first tick to the end of the current timer frame such that any
DOM modifications that happen during the first tick do not trigger a forced
relayout when another tween is initialized (e.g., getComputedStyle).
The d3.merge operator must always return a new array, but it wasn’t in the case
where a one-element array was passed. This implementation is still quite fast
but always returns a new array.
According to GeoJSON, the first ring in a polygon should be the
exterior, and subsequent rings should be holes that are inside the
exterior ring. This fix ensures that the rejoining of clipped segments
preserves these semantics, by generating multiple polygons if necessary
rather than generating all rings within a single polygon as it did
previously.
Fixes#1558.
Fixes#1550. By using the copy of the new scale (which we make anyway for a
snapshot) we avoid polluting the scale’s domain with the old values when using
the scale as the key function for the tick data-join.
Intersection points are sorted along the clipping region edge relative
to a particular point. In the case of antimeridian clipping, this point
is the South pole. Previously, the first intersection was assumed to be
an entering intersection, but this is not always the case.
The fix is to see whether the clip region start point (the point
relative to which sorting occurs) is inside or outside the polygon being
drawn. If it is inside, then the first intersection point must be
outside, and so forth.
The same applies to d3.geo.clipExtent. Provision was already made for
this, but this has now been optimised to use a single point instead of
picking one of the four corners.
Another optimisation was to reuse this clip region start point to
determine whether to interpolate all the way around the clip region
edge. Previously, this was done by testing an arbitrary point in the
clip region.
The above fix seemed to have broken one of the tests, and this has been
fixed by modifying the point-in-polygon routine slightly to handle
points that might lie exactly on the polygon edge.
Finally, I noticed a regression with the recent clipExtent fix, where a
polygon incorrectly being marked as “clean” (no intersections) on a
per-ring instead of per-polygon basis.
We remove the rotation step that occurred prior to clipping, so that
clipping processes unrotated polygons. Note that clip regions are
defined relative to the rotated globe, so clipping now needs a rotation
parameter.
Rather than moving the rotation step so it occurs after clipping, the
rotation occurs as part of clipping, using the rotation parameter to
rotate streamed points. Alternatively, points could be clipped against
a rotated clip region, and then subsequently rotated as a separate step,
but this seems slightly less efficient.
Fixes#1453.
If a polygon does not intersect with the extent, but has one or more
rings inside the extent, then the extent should be checked to see
whether it is inside the polygon: if so, an additional exterior ring is
generated.
Previously, this check was only made if there were no visible rings at
all.
Fixes an issue noticed in #1453.
When an ordinal scale’s range is explicitly defined as an array of values, we
can build the domain implicitly by progressively assigning values from the
range; this is commonly done with color palettes, for example.
However, when an ordinal scale’s range is rather implied by chopping a
continuous range into a series of points or bands, then the domain must be
specified explicitly: for the scale to be consistent, we need to know the
cardinality of the domain to compute the implied range values.
Thus, it only makes sense to extend the domain implicitly when the range is
specified explicitly. Fixes#1536#1535.
Previously we were using string coercion as the key function for axis ticks.
However, when the stringified value of the tick does not fully capture the
representation (such as a date with millisecond precision, whose string form
only has second precision; fixes#1529), string coercion is insufficient.
Fortunately, there is an equivalently-simple key function for tick identity: the
scale! If the scale does not return a unique position for the given tick, then
the tick would be overlapping, so it serves perfectly as the key function.
Instead of detecting if any single polygon ring winds around a pole, we
consider the cumulative winding of all polygon rings together. This is
consistent with the area calculation, which considers the cumulative
area total of all rings.
This fixes#1521: an issue with the Hammer Retroazimuthal projection,
which uses such a polygon with two rings, covering most of the globe.
In addition, drop the special handling of points at the south pole,
which might have been there to pass an incorrect test: a CCW triangle
touching the south pole, which was probably incorrectly thought to be
clockwise. This fixes an issue with a “stripe” polygon rotated so that
a point is at the south pole, mentioned in #1453.
This line doesn’t appear to do anything useful, since the parent node is
now saved in the closure. Previously, this line checked to see if
target.parentNode was null, since this would cause a crash when
computing the relative position.
Normally d3.mouse(this) is equivalent to d3.touches(this)[0] because the clientX
and clientY properties of the event and the first touch are the same. However,
on touchend the touches list is empty, and the clientX and clientY are 0! So
instead it seems safer to use the position of the first changed touch for the
default location of a touch event.
Previously, if there were globally-active touches remaining, a given
zoom gesture was not ended even if locations0 was empty. This fixes the
problem by checking to see if locations0 is empty. If so, the gesture
is ended.
This block is only necessary if there are remaining touches, because
otherwise the gesture has ended and we remove this closure along with
locations0. Thanks, @mbostock!
Unlike d3.event.scale, which is a single value provided by the browser
(usually an average based on the position of all touches), we can
compute separate distances for each zoom gesture and its target,
allowing multiple gestures to occur simultaneously.
This avoids a potential bug where a touch identifier might be reused in
another gesture, but could still be referenced in locations0, thus
causing problems for the old gesture on touchmove.
Fixes#1497, where a zoom gesture would break if a touch was started
outside the target during a gesture.
Each zoom gesture now only considers touches that were started on its
associated target element. This allows multiple zoom gestures to occur
at once without conflicts.
IE doesn’t support __proto__ patching, so if the selection.prototype.transition
method isn’t yet defined at the time d3_selectionRoot is defined, it won’t
inherit the method when the prototype is patched later! This restores the
original order of dependencies so that the transition method is defined before
d3_selectionRoot is created.
The only function of d3.geo.identity was to allow viewport clipping on the
identity projection, so it seems clearer to replace d3.geo.identity with a
geometry transform that is explicitly tailored to viewport clipping.
Unfortunately, it seems that generically applying the point coordinates are
arguments prevents certain optimizations by the JavaScript runtime. If we
instead limit geometry streaming to three dimensions, this still allows us to
perform dynamic transformations based on arbitrary data.
Rather than exposing a geometry transformation that is limited to filtering
points based on a minimum z value, this commit introduces a more generic method
of constructing streaming geometry transformations. This eliminates some of the
wrapper code used previously to create transforms.
This commit also restructures the implementation in core/array to remove a
dependency on core/document; instead, core/document now depends on core/array
and redefines the implementation of d3_array for broken browsers.
Assumes that the third dimension (z) of each coordinate represents the
importance of that coordinate, and implements a filter stream that skips
coordinates that fail to meet the minimum importance threshold.
When the brush extent is set explicitly, use the exact values to set the
displayed position of the brush, rather than flooring the pixel coordinates.
This ensures a consistent appearance with other elements, such as an axis, that
may be rendered with shape-rendering: crispEdges.
Although submillisecond timing probably isn’t important for a single transition,
error can accumulate over time with chained transitions, so it’s important to
allow submillisecond timing for long-running looped transitions.
The zoom behavior was immediately persisting the new view when the transition
was scheduled, rather than waiting until the transition started. By waiting
until the transition starts, it’s possible to schedule chained transitions to
different views.
Previously, the transition would delete itself prior to notifying any end event
listeners. This could cause a crash when trying to schedule a chained transition
when a transition ends. To fix this, we now defer the deletion of the transition
until after the end event is dispatch.
This commit also makes a few tests that depended on the order in which tweens
were invoked a little more robust: the state after a transition ends is
inspected in the next tick after, rather than during, the end event.
Rather than dispatch brush events implicitly as part of selection.call or
transition.call, there is now a brush.event method which can be similarly called
to dispatch events. This way, the user can control when (or if) the events get
dispatched, and avoid unexpected events.
Rather than capture the brush’s pre-transition state via closure, store the
state on the DOM like the axis component (this.__chart__). Now the brush can be
instanced on multiple elements, and each element’s pre-transition state can be
tracked separately, consistent with its display.
Further, restore the pre-transition state of the brush prior to emitting the
brushstart event, using the pre-transition data-space extent, if any. And
likewise, when the transition ends, restore the post-transition data-space
extent if available, rather than the less accurate pixel-space extent. Since the
data-space extent is restored when the transition ends, an additional brush
event is now emitted prior to brushend, since the extent may have changed.
However, since the new extent is set to trigger the transition, this does not
provide interpolation of the extent in data-space, making it somewhat cumbersome
to perform live brush intersection during the transition.
This applies to linear.ticks and linear.tickFormat, and their equivalents on pow
and sqrt scales. In addition it changes the behavior of linear.nice() so that it
is equivalent to linear.nice(10) for consistency.
Due to the touchend listener being overwritten for every touchstart, two
touchstart events would result in d3_event_dragSuppress being called
twice, with only a single drag restore, meaning that a document's
user-select style could be lost.
This restructuring means that the first touchstart creates the closures
for subsequent touchstart, touchmove and touchend events. A nice
side-benefit is that fewer closures are created for multitouch.
For a two-finger pinch, if two touchstart events fire, this results in
two touchmove and touchend listeners. The first will have a single
location in the locations variable, but its touchmove listener will
fire with two touches, causing it to fail due to not finding the
location of the second touch.
If the first listener fires before the second, this exception breaks
touch zooming (as no further listeners will be called), but the order is
undefined so may be browser/device dependent (for reproducing the bug).
Since zooming only ever involves a single gesture at a time, it makes
more sense to only have one listener of each type at a time, unlike
dragging, which involves multiple drag gestures at once.
Rather than duck-checking for a forEach method, limit the use of forEach to
d3_Map instances. In ECMAScript 6, the map.forEach method passes the callback
(value, key) rather than (key, value); likewise, array.forEach does the same.