🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@sveltejs/pancake

Package Overview
Dependencies
Maintainers
3
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sveltejs/pancake - npm Package Compare versions

Comparing version
0.0.15
to
0.0.16
+5
-0
CHANGELOG.md
# @sveltejs/pancake changelog
## 0.0.16
* Add optional `radius` prop for `<Quadtree>`
* Expose `width` and `height` on context
## 0.0.15

@@ -4,0 +9,0 @@

+1
-1

@@ -10,3 +10,3 @@ <script>

const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -13,0 +13,0 @@ let style; $: {

@@ -29,11 +29,6 @@ <script context="module">

const pointer = writable({
left: undefined,
top: undefined,
x: undefined,
y: undefined
});
const width = writable();
const height = writable();
const pointer = writable(null);
// $: console.log({ x: $pointer.x, y: $pointer.y });
const handle_mousemove = e => {

@@ -44,8 +39,12 @@ const bcr = chart.getBoundingClientRect();

const x = $x_inverse(100 * left / (bcr.right - bcr.left));
const y = $y_inverse(100 * top / (bcr.bottom - bcr.top));
const x = $x_scale_inverse(100 * left / (bcr.right - bcr.left));
const y = $y_scale_inverse(100 * top / (bcr.bottom - bcr.top));
pointer.set({ x, y });
pointer.set({ x, y, left, top });
};
const handle_mouseleave = () => {
pointer.set(null);
};
$: _x1.set(x1);

@@ -56,12 +55,12 @@ $: _y1.set(y1);

const x = derived([_x1, _x2], ([$x1, $x2]) => {
const x_scale = derived([_x1, _x2], ([$x1, $x2]) => {
return yootils.linearScale([$x1, $x2], [0, 100]);
});
const y = derived([_y1, _y2], ([$y1, $y2]) => {
const y_scale = derived([_y1, _y2], ([$y1, $y2]) => {
return yootils.linearScale([$y1, $y2], [100, 0]);
});
const x_inverse = derived(x, $x => $x.inverse());
const y_inverse = derived(y, $y => $y.inverse());
const x_scale_inverse = derived(x_scale, $x_scale => $x_scale.inverse());
const y_scale_inverse = derived(y_scale, $y_scale => $y_scale.inverse());

@@ -73,9 +72,21 @@ setContext(key, {

y2: _y2,
x,
y,
pointer
x_scale,
y_scale,
x_scale_inverse,
y_scale_inverse,
pointer,
width,
height
});
</script>
<div class="pancake-chart" bind:this={chart} on:mousemove={handle_mousemove} class:clip>
<div
class="pancake-chart"
bind:this={chart}
bind:clientWidth={$width}
bind:clientHeight={$height}
on:mousemove={handle_mousemove}
on:mouseleave={handle_mouseleave}
class:clip
>
<slot></slot>

@@ -82,0 +93,0 @@ </div>

@@ -10,3 +10,3 @@ <script>

const { x1, y1, x2, y2, x, y } = getChartContext();
const { x1, y1, x2, y2, x_scale, y_scale } = getChartContext();

@@ -27,4 +27,4 @@ const VERTICAL = {};

$: style = orientation === HORIZONTAL
? (n, i) => `width: 100%; height: 0; top: ${$y(n, i)}%`
: (n, i) => `width: 0; height: 100%; left: ${$x(n, i)}%`;
? (n, i) => `width: 100%; height: 0; top: ${$y_scale(n, i)}%`
: (n, i) => `width: 0; height: 100%; left: ${$x_scale(n, i)}%`;
</script>

@@ -31,0 +31,0 @@

<script>
import { getChartContext } from './Chart.svelte';
const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -6,0 +6,0 @@ export let x;

<script>
import { createEventDispatcher } from 'svelte';
import { getChartContext } from './Chart.svelte';

@@ -9,2 +10,3 @@ import Quadtree from '../utils/Quadtree.mjs'

export let y = default_y;
export let radius = Infinity;

@@ -14,8 +16,21 @@ // exposing the prop lets consumers use let: or bind:

const { pointer } = getChartContext();
const { pointer, x_scale, y_scale, x_scale_inverse, y_scale_inverse, width, height } = getChartContext();
const dispatch = createEventDispatcher();
$: quadtree = new Quadtree(data, x, y); // TODO map data here so we don't need to pass accessors around, and accessors can access index
$: closest = $pointer.x !== undefined ? quadtree.find($pointer.x, $pointer.y) : null;
$: quadtree = new Quadtree(data);
$: quadtree.update(x, y, $x_scale, $y_scale);
// track reference changes, to trigger updates sparingly
let prev_closest;
let next_closest;
$: next_closest = $pointer !== null
? quadtree.find($pointer.left, $pointer.top, $width, $height, radius)
: null;
$: if (next_closest !== prev_closest) {
closest = prev_closest = next_closest;
}
</script>
<slot {closest}></slot>

@@ -5,3 +5,3 @@ <script>

const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -8,0 +8,0 @@ export let data;

<script>
import { getChartContext } from './Chart.svelte';
const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -6,0 +6,0 @@ export let x;

@@ -5,3 +5,3 @@ <script>

const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -8,0 +8,0 @@ export let data;

@@ -5,3 +5,3 @@ <script>

const { x: x_scale, y: y_scale } = getChartContext();
const { x_scale, y_scale } = getChartContext();

@@ -8,0 +8,0 @@ export let data;

{
"name": "@sveltejs/pancake",
"version": "0.0.15",
"version": "0.0.16",
"description": "Experimental charting library for Svelte",

@@ -5,0 +5,0 @@ "module": "index.mjs",

@@ -1,9 +0,3 @@

/*
Adapted from https://github.com/d3/d3-quadtree
Released under BSD license
*/
class Quad {
constructor(node, x0, y0, x1, y1) {
this.node = node;
class Node {
constructor(x0, y0, x1, y1) {
this.x0 = x0;

@@ -13,240 +7,143 @@ this.y0 = y0;

this.y1 = y1;
}
}
this.xm = (x0 + x1) / 2;
this.ym = (y0 + y1) / 2;
export default class Quadtree {
constructor(nodes, _x, _y) {
this._x = _x;
this._y = _y;
this._x0 = NaN;
this._y0 = NaN;
this._x1 = NaN;
this._y1 = NaN;
this._root = undefined;
this.addAll(nodes);
this.empty = true;
this.leaf = null;
this.children = null;
}
addAll(data) {
var d,
i,
n = data.length,
x,
y,
xz = new Array(n),
yz = new Array(n),
x0 = Infinity,
y0 = Infinity,
x1 = -Infinity,
y1 = -Infinity;
add(p) {
const { x0, y0, x1, y1, xm, ym, leaf } = this;
// Compute the points and their extent.
for (i = 0; i < n; ++i) {
if (
isNaN((x = +this._x.call(null, (d = data[i])))) ||
isNaN((y = +this._y.call(null, d)))
)
continue;
xz[i] = x;
yz[i] = y;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
if (y < y0) y0 = y;
if (y > y1) y1 = y;
if (this.empty) {
this.leaf = p;
this.empty = false;
return;
}
// If there were no (valid) points, abort.
if (x0 > x1 || y0 > y1) return this;
if (leaf) {
// need to subdivide
this.children = {
nw: new Node(x0, y0, xm, ym),
ne: new Node(xm, y0, x1, ym),
sw: new Node(x0, ym, xm, y1),
se: new Node(xm, ym, x1, y1)
};
// Expand the tree to cover the new points.
this.cover(x0, y0).cover(x1, y1);
// Add the new points.
for (i = 0; i < n; ++i) {
this._add(xz[i], yz[i], data[i]);
this.leaf = null;
this.add(leaf);
}
return this;
const child = p.x < xm
? p.y < ym ? this.children.nw : this.children.sw
: p.y < ym ? this.children.ne : this.children.se;
child.add(p);
}
}
cover(x, y) {
if (isNaN((x = +x)) || isNaN((y = +y))) return this; // ignore invalid points
function build_tree(data, x, y, x_scale, y_scale) {
const points = data.map((d, i) => ({
d,
x: x_scale(x(d, i)),
y: y_scale(y(d, i))
}));
var x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1;
let x0 = Infinity;
let y0 = Infinity;
let x1 = -Infinity;
let y1 = -Infinity;
// If the quadtree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing quadrant boundaries don’t change due to floating point error!
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
y1 = (y0 = Math.floor(y)) + 1;
}
for (let i = 0; i < points.length; i += 1) {
const p = points[i];
// Otherwise, double repeatedly to cover.
else {
var z = x1 - x0,
node = this._root,
parent,
i;
if (p.x < x0) x0 = p.x;
if (p.y < y0) y0 = p.y;
if (p.x > x1) x1 = p.x;
if (p.y > y1) y1 = p.y;
}
while (x0 > x || x >= x1 || y0 > y || y >= y1) {
i = ((y < y0) << 1) | (x < x0);
(parent = new Array(4)), (parent[i] = node), (node = parent), (z *= 2);
switch (i) {
case 0:
(x1 = x0 + z), (y1 = y0 + z);
break;
case 1:
(x0 = x1 - z), (y1 = y0 + z);
break;
case 2:
(x1 = x0 + z), (y0 = y1 - z);
break;
case 3:
(x0 = x1 - z), (y0 = y1 - z);
break;
}
}
const root = new Node(x0, y0, x1, y1);
if (this._root && this._root.length) this._root = node;
}
for (let i = 0; i < points.length; i += 1) {
const p = points[i];
if (isNaN(p.x) || isNaN(p.y)) continue;
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
return this;
root.add(p);
}
find(x, y, radius) {
var data,
x0 = this._x0,
y0 = this._y0,
x1,
y1,
x2,
y2,
x3 = this._x1,
y3 = this._y1,
quads = [],
node = this._root,
q,
i;
return root;
}
if (node) quads.push(new Quad(node, x0, y0, x3, y3));
if (radius == null) radius = Infinity;
else {
(x0 = x - radius), (y0 = y - radius);
(x3 = x + radius), (y3 = y + radius);
radius *= radius;
}
export default class Quadtree {
constructor(data) {
this.data = data;
this.x = null;
this.y = null;
this.x_scale = null;
this.y_scale = null;
}
while ((q = quads.pop())) {
// Stop searching if this quadrant can’t contain a closer node.
if (
!(node = q.node) ||
(x1 = q.x0) > x3 ||
(y1 = q.y0) > y3 ||
(x2 = q.x1) < x0 ||
(y2 = q.y1) < y0
)
continue;
update(x, y, x_scale, y_scale) {
this.root = null;
this.x = x;
this.y = y;
this.x_scale = x_scale;
this.y_scale = y_scale;
}
// Bisect the current quadrant.
if (node.length) {
var xm = (x1 + x2) / 2,
ym = (y1 + y2) / 2;
find(left, top, width, height, radius) {
if (!this.root) this.root = build_tree(this.data, this.x, this.y, this.x_scale, this.y_scale);
quads.push(
new Quad(node[3], xm, ym, x2, y2),
new Quad(node[2], x1, ym, xm, y2),
new Quad(node[1], xm, y1, x2, ym),
new Quad(node[0], x1, y1, xm, ym)
);
const queue = [this.root];
// Visit the closest quadrant first.
if ((i = ((y >= ym) << 1) | (x >= xm))) {
q = quads[quads.length - 1];
quads[quads.length - 1] = quads[quads.length - 1 - i];
quads[quads.length - 1 - i] = q;
}
}
let node;
let closest;
let min_d_squared = Infinity;
// Visit this point. (Visiting coincident points isn’t necessary!)
else {
var dx = x - +this._x.call(null, node.data),
dy = y - +this._y.call(null, node.data),
d2 = dx * dx + dy * dy;
if (d2 < radius) {
var d = Math.sqrt((radius = d2));
(x0 = x - d), (y0 = y - d);
(x3 = x + d), (y3 = y + d);
data = node.data;
}
}
}
const x_to_px = x => x * width / 100;
const y_to_px = y => y * height / 100;
return data;
}
while (node = queue.shift()) {
if (node.empty) continue;
_add(x, y, d) {
if (isNaN(x) || isNaN(y)) return this; // ignore invalid points
const left0 = x_to_px(node.x0);
const left1 = x_to_px(node.x1);
const top0 = y_to_px(node.y0);
const top1 = y_to_px(node.y1);
var parent,
node = this._root,
leaf = { data: d },
x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1,
xm,
ym,
xp,
yp,
right,
bottom,
i,
j;
const out_of_bounds = (
left < (Math.min(left0, left1) - radius) ||
left > (Math.max(left0, left1) + radius) ||
top < (Math.min(top0, top1) - radius) ||
top > (Math.max(top0, top1) + radius)
);
// If the tree is empty, initialize the root as a leaf.
if (!node) return (this._root = leaf), this;
if (out_of_bounds) continue;
// Find the existing leaf for the new point, or add it.
while (node.length) {
if ((right = x >= (xm = (x0 + x1) / 2))) x0 = xm;
else x1 = xm;
if ((bottom = y >= (ym = (y0 + y1) / 2))) y0 = ym;
else y1 = ym;
if (((parent = node), !(node = node[(i = (bottom << 1) | right)])))
return (parent[i] = leaf), this;
}
if (node.leaf) {
const dl = x_to_px(node.leaf.x) - left;
const dt = y_to_px(node.leaf.y) - top;
// Is the new point is exactly coincident with the existing point?
xp = +this._x.call(null, node.data);
yp = +this._y.call(null, node.data);
if (x === xp && y === yp)
return (
(leaf.next = node),
parent ? (parent[i] = leaf) : (this._root = leaf),
this
);
const d_squared = (dl * dl + dt * dt);
// Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent
? (parent[i] = new Array(4))
: (this._root = new Array(4));
if ((right = x >= (xm = (x0 + x1) / 2))) x0 = xm;
else x1 = xm;
if ((bottom = y >= (ym = (y0 + y1) / 2))) y0 = ym;
else y1 = ym;
} while (
(i = (bottom << 1) | right) === (j = ((yp >= ym) << 1) | (xp >= xm))
);
if (d_squared < min_d_squared) {
closest = node.leaf.d;
min_d_squared = d_squared;
}
} else {
queue.push(
node.children.nw,
node.children.ne,
node.children.sw,
node.children.se
);
}
}
return (parent[j] = node), (parent[i] = leaf), this;
return min_d_squared < (radius * radius)
? closest
: null;
}
}