liveline 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -262,39 +262,59 @@ function drawGrid(ctx, layout, palette, formatValue, state, dt) {
262
262
  }
263
263
 
264
264
  // src/math/spline.ts
265
- function drawSpline(ctx, pts, tension = 0.15, maxPoints = 300) {
265
+ function drawSpline(ctx, pts) {
266
266
  if (pts.length < 2) return;
267
267
  if (pts.length === 2) {
268
268
  ctx.lineTo(pts[1][0], pts[1][1]);
269
269
  return;
270
270
  }
271
- let sampled = pts;
272
- if (pts.length > maxPoints) {
273
- const step = pts.length / maxPoints;
274
- sampled = [];
275
- for (let i = 0; i < maxPoints; i++) {
276
- sampled.push(pts[Math.round(i * step)]);
271
+ const n = pts.length;
272
+ const delta = new Array(n - 1);
273
+ const h = new Array(n - 1);
274
+ for (let i = 0; i < n - 1; i++) {
275
+ h[i] = pts[i + 1][0] - pts[i][0];
276
+ delta[i] = h[i] === 0 ? 0 : (pts[i + 1][1] - pts[i][1]) / h[i];
277
+ }
278
+ const m = new Array(n);
279
+ m[0] = delta[0];
280
+ m[n - 1] = delta[n - 2];
281
+ for (let i = 1; i < n - 1; i++) {
282
+ if (delta[i - 1] * delta[i] <= 0) {
283
+ m[i] = 0;
284
+ } else {
285
+ m[i] = (delta[i - 1] + delta[i]) / 2;
286
+ }
287
+ }
288
+ for (let i = 0; i < n - 1; i++) {
289
+ if (delta[i] === 0) {
290
+ m[i] = 0;
291
+ m[i + 1] = 0;
292
+ } else {
293
+ const alpha = m[i] / delta[i];
294
+ const beta = m[i + 1] / delta[i];
295
+ const s2 = alpha * alpha + beta * beta;
296
+ if (s2 > 9) {
297
+ const s = 3 / Math.sqrt(s2);
298
+ m[i] = s * alpha * delta[i];
299
+ m[i + 1] = s * beta * delta[i];
300
+ }
277
301
  }
278
- sampled.push(pts[pts.length - 1]);
279
302
  }
280
- for (let i = 0; i < sampled.length - 1; i++) {
281
- const p0 = sampled[Math.max(0, i - 1)];
282
- const p1 = sampled[i];
283
- const p2 = sampled[i + 1];
284
- const p3 = sampled[Math.min(sampled.length - 1, i + 2)];
303
+ for (let i = 0; i < n - 1; i++) {
304
+ const hi = h[i];
285
305
  ctx.bezierCurveTo(
286
- p1[0] + (p2[0] - p0[0]) * tension,
287
- p1[1] + (p2[1] - p0[1]) * tension,
288
- p2[0] - (p3[0] - p1[0]) * tension,
289
- p2[1] - (p3[1] - p1[1]) * tension,
290
- p2[0],
291
- p2[1]
306
+ pts[i][0] + hi / 3,
307
+ pts[i][1] + m[i] * hi / 3,
308
+ pts[i + 1][0] - hi / 3,
309
+ pts[i + 1][1] - m[i + 1] * hi / 3,
310
+ pts[i + 1][0],
311
+ pts[i + 1][1]
292
312
  );
293
313
  }
294
314
  }
295
315
 
296
316
  // src/draw/line.ts
297
- function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
317
+ function renderCurve(ctx, layout, palette, pts, showFill) {
298
318
  const { h, pad } = layout;
299
319
  if (showFill) {
300
320
  const grad = ctx.createLinearGradient(0, pad.top, 0, h - pad.bottom);
@@ -303,7 +323,7 @@ function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
303
323
  ctx.beginPath();
304
324
  ctx.moveTo(pts[0][0], h - pad.bottom);
305
325
  ctx.lineTo(pts[0][0], pts[0][1]);
306
- drawSpline(ctx, pts, 0.15, maxSplinePts);
326
+ drawSpline(ctx, pts);
307
327
  ctx.lineTo(pts[pts.length - 1][0], h - pad.bottom);
308
328
  ctx.closePath();
309
329
  ctx.fillStyle = grad;
@@ -311,7 +331,7 @@ function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
311
331
  }
312
332
  ctx.beginPath();
313
333
  ctx.moveTo(pts[0][0], pts[0][1]);
314
- drawSpline(ctx, pts, 0.15, maxSplinePts);
334
+ drawSpline(ctx, pts);
315
335
  ctx.strokeStyle = palette.line;
316
336
  ctx.lineWidth = palette.lineWidth;
317
337
  ctx.lineJoin = "round";
@@ -329,7 +349,6 @@ function drawLine(ctx, layout, palette, visible, smoothValue, now, showFill, scr
329
349
  pts.push([toX(now), clampY(toY(smoothValue))]);
330
350
  if (pts.length < 2) return;
331
351
  const isScrubbing = scrubX !== null;
332
- const maxSplinePts = Math.max(300, Math.ceil(chartW));
333
352
  ctx.save();
334
353
  ctx.beginPath();
335
354
  ctx.rect(pad.left - 1, pad.top, chartW + 2, chartH);
@@ -339,17 +358,17 @@ function drawLine(ctx, layout, palette, visible, smoothValue, now, showFill, scr
339
358
  ctx.beginPath();
340
359
  ctx.rect(0, 0, scrubX, h);
341
360
  ctx.clip();
342
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
361
+ renderCurve(ctx, layout, palette, pts, showFill);
343
362
  ctx.restore();
344
363
  ctx.save();
345
364
  ctx.beginPath();
346
365
  ctx.rect(scrubX, 0, layout.w - scrubX, h);
347
366
  ctx.clip();
348
367
  ctx.globalAlpha = 1 - scrubAmount * 0.6;
349
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
368
+ renderCurve(ctx, layout, palette, pts, showFill);
350
369
  ctx.restore();
351
370
  } else {
352
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
371
+ renderCurve(ctx, layout, palette, pts, showFill);
353
372
  }
354
373
  ctx.restore();
355
374
  const currentY = Math.max(pad.top, Math.min(h - pad.bottom, toY(smoothValue)));
package/dist/index.js CHANGED
@@ -236,39 +236,59 @@ function drawGrid(ctx, layout, palette, formatValue, state, dt) {
236
236
  }
237
237
 
238
238
  // src/math/spline.ts
239
- function drawSpline(ctx, pts, tension = 0.15, maxPoints = 300) {
239
+ function drawSpline(ctx, pts) {
240
240
  if (pts.length < 2) return;
241
241
  if (pts.length === 2) {
242
242
  ctx.lineTo(pts[1][0], pts[1][1]);
243
243
  return;
244
244
  }
245
- let sampled = pts;
246
- if (pts.length > maxPoints) {
247
- const step = pts.length / maxPoints;
248
- sampled = [];
249
- for (let i = 0; i < maxPoints; i++) {
250
- sampled.push(pts[Math.round(i * step)]);
245
+ const n = pts.length;
246
+ const delta = new Array(n - 1);
247
+ const h = new Array(n - 1);
248
+ for (let i = 0; i < n - 1; i++) {
249
+ h[i] = pts[i + 1][0] - pts[i][0];
250
+ delta[i] = h[i] === 0 ? 0 : (pts[i + 1][1] - pts[i][1]) / h[i];
251
+ }
252
+ const m = new Array(n);
253
+ m[0] = delta[0];
254
+ m[n - 1] = delta[n - 2];
255
+ for (let i = 1; i < n - 1; i++) {
256
+ if (delta[i - 1] * delta[i] <= 0) {
257
+ m[i] = 0;
258
+ } else {
259
+ m[i] = (delta[i - 1] + delta[i]) / 2;
260
+ }
261
+ }
262
+ for (let i = 0; i < n - 1; i++) {
263
+ if (delta[i] === 0) {
264
+ m[i] = 0;
265
+ m[i + 1] = 0;
266
+ } else {
267
+ const alpha = m[i] / delta[i];
268
+ const beta = m[i + 1] / delta[i];
269
+ const s2 = alpha * alpha + beta * beta;
270
+ if (s2 > 9) {
271
+ const s = 3 / Math.sqrt(s2);
272
+ m[i] = s * alpha * delta[i];
273
+ m[i + 1] = s * beta * delta[i];
274
+ }
251
275
  }
252
- sampled.push(pts[pts.length - 1]);
253
276
  }
254
- for (let i = 0; i < sampled.length - 1; i++) {
255
- const p0 = sampled[Math.max(0, i - 1)];
256
- const p1 = sampled[i];
257
- const p2 = sampled[i + 1];
258
- const p3 = sampled[Math.min(sampled.length - 1, i + 2)];
277
+ for (let i = 0; i < n - 1; i++) {
278
+ const hi = h[i];
259
279
  ctx.bezierCurveTo(
260
- p1[0] + (p2[0] - p0[0]) * tension,
261
- p1[1] + (p2[1] - p0[1]) * tension,
262
- p2[0] - (p3[0] - p1[0]) * tension,
263
- p2[1] - (p3[1] - p1[1]) * tension,
264
- p2[0],
265
- p2[1]
280
+ pts[i][0] + hi / 3,
281
+ pts[i][1] + m[i] * hi / 3,
282
+ pts[i + 1][0] - hi / 3,
283
+ pts[i + 1][1] - m[i + 1] * hi / 3,
284
+ pts[i + 1][0],
285
+ pts[i + 1][1]
266
286
  );
267
287
  }
268
288
  }
269
289
 
270
290
  // src/draw/line.ts
271
- function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
291
+ function renderCurve(ctx, layout, palette, pts, showFill) {
272
292
  const { h, pad } = layout;
273
293
  if (showFill) {
274
294
  const grad = ctx.createLinearGradient(0, pad.top, 0, h - pad.bottom);
@@ -277,7 +297,7 @@ function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
277
297
  ctx.beginPath();
278
298
  ctx.moveTo(pts[0][0], h - pad.bottom);
279
299
  ctx.lineTo(pts[0][0], pts[0][1]);
280
- drawSpline(ctx, pts, 0.15, maxSplinePts);
300
+ drawSpline(ctx, pts);
281
301
  ctx.lineTo(pts[pts.length - 1][0], h - pad.bottom);
282
302
  ctx.closePath();
283
303
  ctx.fillStyle = grad;
@@ -285,7 +305,7 @@ function renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts) {
285
305
  }
286
306
  ctx.beginPath();
287
307
  ctx.moveTo(pts[0][0], pts[0][1]);
288
- drawSpline(ctx, pts, 0.15, maxSplinePts);
308
+ drawSpline(ctx, pts);
289
309
  ctx.strokeStyle = palette.line;
290
310
  ctx.lineWidth = palette.lineWidth;
291
311
  ctx.lineJoin = "round";
@@ -303,7 +323,6 @@ function drawLine(ctx, layout, palette, visible, smoothValue, now, showFill, scr
303
323
  pts.push([toX(now), clampY(toY(smoothValue))]);
304
324
  if (pts.length < 2) return;
305
325
  const isScrubbing = scrubX !== null;
306
- const maxSplinePts = Math.max(300, Math.ceil(chartW));
307
326
  ctx.save();
308
327
  ctx.beginPath();
309
328
  ctx.rect(pad.left - 1, pad.top, chartW + 2, chartH);
@@ -313,17 +332,17 @@ function drawLine(ctx, layout, palette, visible, smoothValue, now, showFill, scr
313
332
  ctx.beginPath();
314
333
  ctx.rect(0, 0, scrubX, h);
315
334
  ctx.clip();
316
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
335
+ renderCurve(ctx, layout, palette, pts, showFill);
317
336
  ctx.restore();
318
337
  ctx.save();
319
338
  ctx.beginPath();
320
339
  ctx.rect(scrubX, 0, layout.w - scrubX, h);
321
340
  ctx.clip();
322
341
  ctx.globalAlpha = 1 - scrubAmount * 0.6;
323
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
342
+ renderCurve(ctx, layout, palette, pts, showFill);
324
343
  ctx.restore();
325
344
  } else {
326
- renderCurve(ctx, layout, palette, pts, showFill, maxSplinePts);
345
+ renderCurve(ctx, layout, palette, pts, showFill);
327
346
  }
328
347
  ctx.restore();
329
348
  const currentY = Math.max(pad.top, Math.min(h - pad.bottom, toY(smoothValue)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liveline",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Real-time animated line chart for React",
6
6
  "keywords": [