hyperprop-charting-library 0.1.67 → 0.1.68
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/hyperprop-charting-library.cjs +120 -1
- package/dist/hyperprop-charting-library.d.ts +1 -1
- package/dist/hyperprop-charting-library.js +120 -1
- package/dist/index.cjs +120 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +120 -1
- package/docs/API.md +10 -3
- package/docs/RECIPES.md +5 -2
- package/package.json +1 -1
|
@@ -2168,6 +2168,20 @@ function createChart(element, options = {}) {
|
|
|
2168
2168
|
drawDrawingLabel(drawing.label, midX, y, drawing.color);
|
|
2169
2169
|
}
|
|
2170
2170
|
}
|
|
2171
|
+
} else if (drawing.type === "vertical-line") {
|
|
2172
|
+
const point = drawing.points[0];
|
|
2173
|
+
if (point) {
|
|
2174
|
+
const x = xFromDrawingPoint(point);
|
|
2175
|
+
ctx.beginPath();
|
|
2176
|
+
ctx.moveTo(crisp(x), crisp(chartTop));
|
|
2177
|
+
ctx.lineTo(crisp(x), crisp(chartBottom));
|
|
2178
|
+
ctx.stroke();
|
|
2179
|
+
const handleY = (chartTop + chartBottom) / 2;
|
|
2180
|
+
drawDrawingHandle(x, handleY, drawing.color);
|
|
2181
|
+
if (drawing.label) {
|
|
2182
|
+
drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2171
2185
|
} else if (drawing.type === "trendline") {
|
|
2172
2186
|
const first = drawing.points[0];
|
|
2173
2187
|
const second = drawing.points[1];
|
|
@@ -2188,6 +2202,36 @@ function createChart(element, options = {}) {
|
|
|
2188
2202
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2189
2203
|
}
|
|
2190
2204
|
}
|
|
2205
|
+
} else if (drawing.type === "ray") {
|
|
2206
|
+
const first = drawing.points[0];
|
|
2207
|
+
const second = drawing.points[1];
|
|
2208
|
+
if (first && second) {
|
|
2209
|
+
const firstX = xFromDrawingPoint(first);
|
|
2210
|
+
const firstY = yFromPrice(first.price);
|
|
2211
|
+
const secondX = xFromDrawingPoint(second);
|
|
2212
|
+
const secondY = yFromPrice(second.price);
|
|
2213
|
+
const dx = secondX - firstX;
|
|
2214
|
+
const dy = secondY - firstY;
|
|
2215
|
+
let endX = secondX;
|
|
2216
|
+
let endY = secondY;
|
|
2217
|
+
const len = Math.hypot(dx, dy);
|
|
2218
|
+
if (len > 0) {
|
|
2219
|
+
const far = Math.abs(chartRight - chartLeft) + Math.abs(chartBottom - chartTop) + 2e3;
|
|
2220
|
+
endX = firstX + dx / len * far;
|
|
2221
|
+
endY = firstY + dy / len * far;
|
|
2222
|
+
}
|
|
2223
|
+
ctx.beginPath();
|
|
2224
|
+
ctx.moveTo(firstX, firstY);
|
|
2225
|
+
ctx.lineTo(endX, endY);
|
|
2226
|
+
ctx.stroke();
|
|
2227
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2228
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2229
|
+
if (drawing.label) {
|
|
2230
|
+
const midX = (firstX + secondX) / 2;
|
|
2231
|
+
const midY = (firstY + secondY) / 2;
|
|
2232
|
+
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2191
2235
|
} else if (drawing.type === "fib-retracement") {
|
|
2192
2236
|
const first = drawing.points[0];
|
|
2193
2237
|
const second = drawing.points[1];
|
|
@@ -3330,6 +3374,17 @@ function createChart(element, options = {}) {
|
|
|
3330
3374
|
if (Math.abs(y - lineY) <= 7) {
|
|
3331
3375
|
return { drawing, target: "line" };
|
|
3332
3376
|
}
|
|
3377
|
+
} else if (drawing.type === "vertical-line") {
|
|
3378
|
+
const point = drawing.points[0];
|
|
3379
|
+
if (!point) continue;
|
|
3380
|
+
const lineX = canvasXFromDrawingPoint(point);
|
|
3381
|
+
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
3382
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
3383
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3384
|
+
}
|
|
3385
|
+
if (Math.abs(x - lineX) <= 7) {
|
|
3386
|
+
return { drawing, target: "line" };
|
|
3387
|
+
}
|
|
3333
3388
|
} else if (drawing.type === "trendline") {
|
|
3334
3389
|
const first = drawing.points[0];
|
|
3335
3390
|
const second = drawing.points[1];
|
|
@@ -3347,6 +3402,31 @@ function createChart(element, options = {}) {
|
|
|
3347
3402
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3348
3403
|
return { drawing, target: "line" };
|
|
3349
3404
|
}
|
|
3405
|
+
} else if (drawing.type === "ray") {
|
|
3406
|
+
const first = drawing.points[0];
|
|
3407
|
+
const second = drawing.points[1];
|
|
3408
|
+
if (!first || !second) continue;
|
|
3409
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3410
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3411
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3412
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3413
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3414
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3415
|
+
}
|
|
3416
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3417
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3418
|
+
}
|
|
3419
|
+
const dx = x2 - x1;
|
|
3420
|
+
const dy = y2 - y1;
|
|
3421
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3422
|
+
if (lengthSq > 0) {
|
|
3423
|
+
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
3424
|
+
const projX = x1 + t * dx;
|
|
3425
|
+
const projY = y1 + t * dy;
|
|
3426
|
+
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
3427
|
+
return { drawing, target: "line" };
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3350
3430
|
} else if (drawing.type === "fib-retracement") {
|
|
3351
3431
|
const first = drawing.points[0];
|
|
3352
3432
|
const second = drawing.points[1];
|
|
@@ -3448,6 +3528,21 @@ function createChart(element, options = {}) {
|
|
|
3448
3528
|
draw();
|
|
3449
3529
|
return true;
|
|
3450
3530
|
}
|
|
3531
|
+
if (activeDrawingTool === "vertical-line") {
|
|
3532
|
+
const defaults = getDrawingToolDefaults("vertical-line");
|
|
3533
|
+
drawings.push(
|
|
3534
|
+
normalizeDrawingState({
|
|
3535
|
+
type: "vertical-line",
|
|
3536
|
+
points: [point],
|
|
3537
|
+
color: defaults.color ?? "#38bdf8",
|
|
3538
|
+
style: defaults.style ?? "solid",
|
|
3539
|
+
width: defaults.width ?? 1
|
|
3540
|
+
})
|
|
3541
|
+
);
|
|
3542
|
+
emitDrawingsChange();
|
|
3543
|
+
draw();
|
|
3544
|
+
return true;
|
|
3545
|
+
}
|
|
3451
3546
|
if (activeDrawingTool === "trendline") {
|
|
3452
3547
|
if (draftDrawing?.type === "trendline") {
|
|
3453
3548
|
const completed = normalizeDrawingState({
|
|
@@ -3472,6 +3567,30 @@ function createChart(element, options = {}) {
|
|
|
3472
3567
|
draw();
|
|
3473
3568
|
return true;
|
|
3474
3569
|
}
|
|
3570
|
+
if (activeDrawingTool === "ray") {
|
|
3571
|
+
if (draftDrawing?.type === "ray") {
|
|
3572
|
+
const completed = normalizeDrawingState({
|
|
3573
|
+
...serializeDrawing(draftDrawing),
|
|
3574
|
+
points: [draftDrawing.points[0], point]
|
|
3575
|
+
});
|
|
3576
|
+
drawings.push(completed);
|
|
3577
|
+
draftDrawing = null;
|
|
3578
|
+
activeDrawingTool = null;
|
|
3579
|
+
emitDrawingsChange();
|
|
3580
|
+
draw();
|
|
3581
|
+
return true;
|
|
3582
|
+
}
|
|
3583
|
+
const defaults = getDrawingToolDefaults("ray");
|
|
3584
|
+
draftDrawing = normalizeDrawingState({
|
|
3585
|
+
type: "ray",
|
|
3586
|
+
points: [point, point],
|
|
3587
|
+
color: defaults.color ?? "#2563eb",
|
|
3588
|
+
style: defaults.style ?? "solid",
|
|
3589
|
+
width: defaults.width ?? 2
|
|
3590
|
+
});
|
|
3591
|
+
draw();
|
|
3592
|
+
return true;
|
|
3593
|
+
}
|
|
3475
3594
|
if (activeDrawingTool === "fib-retracement") {
|
|
3476
3595
|
if (draftDrawing?.type === "fib-retracement") {
|
|
3477
3596
|
const completed = normalizeDrawingState({
|
|
@@ -3768,7 +3887,7 @@ function createChart(element, options = {}) {
|
|
|
3768
3887
|
setCrosshairPoint(null);
|
|
3769
3888
|
return;
|
|
3770
3889
|
}
|
|
3771
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3890
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement")) {
|
|
3772
3891
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3773
3892
|
if (nextPoint) {
|
|
3774
3893
|
draftDrawing = {
|
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
|
@@ -2144,6 +2144,20 @@ function createChart(element, options = {}) {
|
|
|
2144
2144
|
drawDrawingLabel(drawing.label, midX, y, drawing.color);
|
|
2145
2145
|
}
|
|
2146
2146
|
}
|
|
2147
|
+
} else if (drawing.type === "vertical-line") {
|
|
2148
|
+
const point = drawing.points[0];
|
|
2149
|
+
if (point) {
|
|
2150
|
+
const x = xFromDrawingPoint(point);
|
|
2151
|
+
ctx.beginPath();
|
|
2152
|
+
ctx.moveTo(crisp(x), crisp(chartTop));
|
|
2153
|
+
ctx.lineTo(crisp(x), crisp(chartBottom));
|
|
2154
|
+
ctx.stroke();
|
|
2155
|
+
const handleY = (chartTop + chartBottom) / 2;
|
|
2156
|
+
drawDrawingHandle(x, handleY, drawing.color);
|
|
2157
|
+
if (drawing.label) {
|
|
2158
|
+
drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2147
2161
|
} else if (drawing.type === "trendline") {
|
|
2148
2162
|
const first = drawing.points[0];
|
|
2149
2163
|
const second = drawing.points[1];
|
|
@@ -2164,6 +2178,36 @@ function createChart(element, options = {}) {
|
|
|
2164
2178
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2165
2179
|
}
|
|
2166
2180
|
}
|
|
2181
|
+
} else if (drawing.type === "ray") {
|
|
2182
|
+
const first = drawing.points[0];
|
|
2183
|
+
const second = drawing.points[1];
|
|
2184
|
+
if (first && second) {
|
|
2185
|
+
const firstX = xFromDrawingPoint(first);
|
|
2186
|
+
const firstY = yFromPrice(first.price);
|
|
2187
|
+
const secondX = xFromDrawingPoint(second);
|
|
2188
|
+
const secondY = yFromPrice(second.price);
|
|
2189
|
+
const dx = secondX - firstX;
|
|
2190
|
+
const dy = secondY - firstY;
|
|
2191
|
+
let endX = secondX;
|
|
2192
|
+
let endY = secondY;
|
|
2193
|
+
const len = Math.hypot(dx, dy);
|
|
2194
|
+
if (len > 0) {
|
|
2195
|
+
const far = Math.abs(chartRight - chartLeft) + Math.abs(chartBottom - chartTop) + 2e3;
|
|
2196
|
+
endX = firstX + dx / len * far;
|
|
2197
|
+
endY = firstY + dy / len * far;
|
|
2198
|
+
}
|
|
2199
|
+
ctx.beginPath();
|
|
2200
|
+
ctx.moveTo(firstX, firstY);
|
|
2201
|
+
ctx.lineTo(endX, endY);
|
|
2202
|
+
ctx.stroke();
|
|
2203
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2204
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2205
|
+
if (drawing.label) {
|
|
2206
|
+
const midX = (firstX + secondX) / 2;
|
|
2207
|
+
const midY = (firstY + secondY) / 2;
|
|
2208
|
+
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2167
2211
|
} else if (drawing.type === "fib-retracement") {
|
|
2168
2212
|
const first = drawing.points[0];
|
|
2169
2213
|
const second = drawing.points[1];
|
|
@@ -3306,6 +3350,17 @@ function createChart(element, options = {}) {
|
|
|
3306
3350
|
if (Math.abs(y - lineY) <= 7) {
|
|
3307
3351
|
return { drawing, target: "line" };
|
|
3308
3352
|
}
|
|
3353
|
+
} else if (drawing.type === "vertical-line") {
|
|
3354
|
+
const point = drawing.points[0];
|
|
3355
|
+
if (!point) continue;
|
|
3356
|
+
const lineX = canvasXFromDrawingPoint(point);
|
|
3357
|
+
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
3358
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
3359
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3360
|
+
}
|
|
3361
|
+
if (Math.abs(x - lineX) <= 7) {
|
|
3362
|
+
return { drawing, target: "line" };
|
|
3363
|
+
}
|
|
3309
3364
|
} else if (drawing.type === "trendline") {
|
|
3310
3365
|
const first = drawing.points[0];
|
|
3311
3366
|
const second = drawing.points[1];
|
|
@@ -3323,6 +3378,31 @@ function createChart(element, options = {}) {
|
|
|
3323
3378
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3324
3379
|
return { drawing, target: "line" };
|
|
3325
3380
|
}
|
|
3381
|
+
} else if (drawing.type === "ray") {
|
|
3382
|
+
const first = drawing.points[0];
|
|
3383
|
+
const second = drawing.points[1];
|
|
3384
|
+
if (!first || !second) continue;
|
|
3385
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3386
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3387
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3388
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3389
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3390
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3391
|
+
}
|
|
3392
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3393
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3394
|
+
}
|
|
3395
|
+
const dx = x2 - x1;
|
|
3396
|
+
const dy = y2 - y1;
|
|
3397
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3398
|
+
if (lengthSq > 0) {
|
|
3399
|
+
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
3400
|
+
const projX = x1 + t * dx;
|
|
3401
|
+
const projY = y1 + t * dy;
|
|
3402
|
+
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
3403
|
+
return { drawing, target: "line" };
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3326
3406
|
} else if (drawing.type === "fib-retracement") {
|
|
3327
3407
|
const first = drawing.points[0];
|
|
3328
3408
|
const second = drawing.points[1];
|
|
@@ -3424,6 +3504,21 @@ function createChart(element, options = {}) {
|
|
|
3424
3504
|
draw();
|
|
3425
3505
|
return true;
|
|
3426
3506
|
}
|
|
3507
|
+
if (activeDrawingTool === "vertical-line") {
|
|
3508
|
+
const defaults = getDrawingToolDefaults("vertical-line");
|
|
3509
|
+
drawings.push(
|
|
3510
|
+
normalizeDrawingState({
|
|
3511
|
+
type: "vertical-line",
|
|
3512
|
+
points: [point],
|
|
3513
|
+
color: defaults.color ?? "#38bdf8",
|
|
3514
|
+
style: defaults.style ?? "solid",
|
|
3515
|
+
width: defaults.width ?? 1
|
|
3516
|
+
})
|
|
3517
|
+
);
|
|
3518
|
+
emitDrawingsChange();
|
|
3519
|
+
draw();
|
|
3520
|
+
return true;
|
|
3521
|
+
}
|
|
3427
3522
|
if (activeDrawingTool === "trendline") {
|
|
3428
3523
|
if (draftDrawing?.type === "trendline") {
|
|
3429
3524
|
const completed = normalizeDrawingState({
|
|
@@ -3448,6 +3543,30 @@ function createChart(element, options = {}) {
|
|
|
3448
3543
|
draw();
|
|
3449
3544
|
return true;
|
|
3450
3545
|
}
|
|
3546
|
+
if (activeDrawingTool === "ray") {
|
|
3547
|
+
if (draftDrawing?.type === "ray") {
|
|
3548
|
+
const completed = normalizeDrawingState({
|
|
3549
|
+
...serializeDrawing(draftDrawing),
|
|
3550
|
+
points: [draftDrawing.points[0], point]
|
|
3551
|
+
});
|
|
3552
|
+
drawings.push(completed);
|
|
3553
|
+
draftDrawing = null;
|
|
3554
|
+
activeDrawingTool = null;
|
|
3555
|
+
emitDrawingsChange();
|
|
3556
|
+
draw();
|
|
3557
|
+
return true;
|
|
3558
|
+
}
|
|
3559
|
+
const defaults = getDrawingToolDefaults("ray");
|
|
3560
|
+
draftDrawing = normalizeDrawingState({
|
|
3561
|
+
type: "ray",
|
|
3562
|
+
points: [point, point],
|
|
3563
|
+
color: defaults.color ?? "#2563eb",
|
|
3564
|
+
style: defaults.style ?? "solid",
|
|
3565
|
+
width: defaults.width ?? 2
|
|
3566
|
+
});
|
|
3567
|
+
draw();
|
|
3568
|
+
return true;
|
|
3569
|
+
}
|
|
3451
3570
|
if (activeDrawingTool === "fib-retracement") {
|
|
3452
3571
|
if (draftDrawing?.type === "fib-retracement") {
|
|
3453
3572
|
const completed = normalizeDrawingState({
|
|
@@ -3744,7 +3863,7 @@ function createChart(element, options = {}) {
|
|
|
3744
3863
|
setCrosshairPoint(null);
|
|
3745
3864
|
return;
|
|
3746
3865
|
}
|
|
3747
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3866
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement")) {
|
|
3748
3867
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3749
3868
|
if (nextPoint) {
|
|
3750
3869
|
draftDrawing = {
|
package/dist/index.cjs
CHANGED
|
@@ -2168,6 +2168,20 @@ function createChart(element, options = {}) {
|
|
|
2168
2168
|
drawDrawingLabel(drawing.label, midX, y, drawing.color);
|
|
2169
2169
|
}
|
|
2170
2170
|
}
|
|
2171
|
+
} else if (drawing.type === "vertical-line") {
|
|
2172
|
+
const point = drawing.points[0];
|
|
2173
|
+
if (point) {
|
|
2174
|
+
const x = xFromDrawingPoint(point);
|
|
2175
|
+
ctx.beginPath();
|
|
2176
|
+
ctx.moveTo(crisp(x), crisp(chartTop));
|
|
2177
|
+
ctx.lineTo(crisp(x), crisp(chartBottom));
|
|
2178
|
+
ctx.stroke();
|
|
2179
|
+
const handleY = (chartTop + chartBottom) / 2;
|
|
2180
|
+
drawDrawingHandle(x, handleY, drawing.color);
|
|
2181
|
+
if (drawing.label) {
|
|
2182
|
+
drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2171
2185
|
} else if (drawing.type === "trendline") {
|
|
2172
2186
|
const first = drawing.points[0];
|
|
2173
2187
|
const second = drawing.points[1];
|
|
@@ -2188,6 +2202,36 @@ function createChart(element, options = {}) {
|
|
|
2188
2202
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2189
2203
|
}
|
|
2190
2204
|
}
|
|
2205
|
+
} else if (drawing.type === "ray") {
|
|
2206
|
+
const first = drawing.points[0];
|
|
2207
|
+
const second = drawing.points[1];
|
|
2208
|
+
if (first && second) {
|
|
2209
|
+
const firstX = xFromDrawingPoint(first);
|
|
2210
|
+
const firstY = yFromPrice(first.price);
|
|
2211
|
+
const secondX = xFromDrawingPoint(second);
|
|
2212
|
+
const secondY = yFromPrice(second.price);
|
|
2213
|
+
const dx = secondX - firstX;
|
|
2214
|
+
const dy = secondY - firstY;
|
|
2215
|
+
let endX = secondX;
|
|
2216
|
+
let endY = secondY;
|
|
2217
|
+
const len = Math.hypot(dx, dy);
|
|
2218
|
+
if (len > 0) {
|
|
2219
|
+
const far = Math.abs(chartRight - chartLeft) + Math.abs(chartBottom - chartTop) + 2e3;
|
|
2220
|
+
endX = firstX + dx / len * far;
|
|
2221
|
+
endY = firstY + dy / len * far;
|
|
2222
|
+
}
|
|
2223
|
+
ctx.beginPath();
|
|
2224
|
+
ctx.moveTo(firstX, firstY);
|
|
2225
|
+
ctx.lineTo(endX, endY);
|
|
2226
|
+
ctx.stroke();
|
|
2227
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2228
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2229
|
+
if (drawing.label) {
|
|
2230
|
+
const midX = (firstX + secondX) / 2;
|
|
2231
|
+
const midY = (firstY + secondY) / 2;
|
|
2232
|
+
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2191
2235
|
} else if (drawing.type === "fib-retracement") {
|
|
2192
2236
|
const first = drawing.points[0];
|
|
2193
2237
|
const second = drawing.points[1];
|
|
@@ -3330,6 +3374,17 @@ function createChart(element, options = {}) {
|
|
|
3330
3374
|
if (Math.abs(y - lineY) <= 7) {
|
|
3331
3375
|
return { drawing, target: "line" };
|
|
3332
3376
|
}
|
|
3377
|
+
} else if (drawing.type === "vertical-line") {
|
|
3378
|
+
const point = drawing.points[0];
|
|
3379
|
+
if (!point) continue;
|
|
3380
|
+
const lineX = canvasXFromDrawingPoint(point);
|
|
3381
|
+
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
3382
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
3383
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3384
|
+
}
|
|
3385
|
+
if (Math.abs(x - lineX) <= 7) {
|
|
3386
|
+
return { drawing, target: "line" };
|
|
3387
|
+
}
|
|
3333
3388
|
} else if (drawing.type === "trendline") {
|
|
3334
3389
|
const first = drawing.points[0];
|
|
3335
3390
|
const second = drawing.points[1];
|
|
@@ -3347,6 +3402,31 @@ function createChart(element, options = {}) {
|
|
|
3347
3402
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3348
3403
|
return { drawing, target: "line" };
|
|
3349
3404
|
}
|
|
3405
|
+
} else if (drawing.type === "ray") {
|
|
3406
|
+
const first = drawing.points[0];
|
|
3407
|
+
const second = drawing.points[1];
|
|
3408
|
+
if (!first || !second) continue;
|
|
3409
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3410
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3411
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3412
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3413
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3414
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3415
|
+
}
|
|
3416
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3417
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3418
|
+
}
|
|
3419
|
+
const dx = x2 - x1;
|
|
3420
|
+
const dy = y2 - y1;
|
|
3421
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3422
|
+
if (lengthSq > 0) {
|
|
3423
|
+
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
3424
|
+
const projX = x1 + t * dx;
|
|
3425
|
+
const projY = y1 + t * dy;
|
|
3426
|
+
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
3427
|
+
return { drawing, target: "line" };
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3350
3430
|
} else if (drawing.type === "fib-retracement") {
|
|
3351
3431
|
const first = drawing.points[0];
|
|
3352
3432
|
const second = drawing.points[1];
|
|
@@ -3448,6 +3528,21 @@ function createChart(element, options = {}) {
|
|
|
3448
3528
|
draw();
|
|
3449
3529
|
return true;
|
|
3450
3530
|
}
|
|
3531
|
+
if (activeDrawingTool === "vertical-line") {
|
|
3532
|
+
const defaults = getDrawingToolDefaults("vertical-line");
|
|
3533
|
+
drawings.push(
|
|
3534
|
+
normalizeDrawingState({
|
|
3535
|
+
type: "vertical-line",
|
|
3536
|
+
points: [point],
|
|
3537
|
+
color: defaults.color ?? "#38bdf8",
|
|
3538
|
+
style: defaults.style ?? "solid",
|
|
3539
|
+
width: defaults.width ?? 1
|
|
3540
|
+
})
|
|
3541
|
+
);
|
|
3542
|
+
emitDrawingsChange();
|
|
3543
|
+
draw();
|
|
3544
|
+
return true;
|
|
3545
|
+
}
|
|
3451
3546
|
if (activeDrawingTool === "trendline") {
|
|
3452
3547
|
if (draftDrawing?.type === "trendline") {
|
|
3453
3548
|
const completed = normalizeDrawingState({
|
|
@@ -3472,6 +3567,30 @@ function createChart(element, options = {}) {
|
|
|
3472
3567
|
draw();
|
|
3473
3568
|
return true;
|
|
3474
3569
|
}
|
|
3570
|
+
if (activeDrawingTool === "ray") {
|
|
3571
|
+
if (draftDrawing?.type === "ray") {
|
|
3572
|
+
const completed = normalizeDrawingState({
|
|
3573
|
+
...serializeDrawing(draftDrawing),
|
|
3574
|
+
points: [draftDrawing.points[0], point]
|
|
3575
|
+
});
|
|
3576
|
+
drawings.push(completed);
|
|
3577
|
+
draftDrawing = null;
|
|
3578
|
+
activeDrawingTool = null;
|
|
3579
|
+
emitDrawingsChange();
|
|
3580
|
+
draw();
|
|
3581
|
+
return true;
|
|
3582
|
+
}
|
|
3583
|
+
const defaults = getDrawingToolDefaults("ray");
|
|
3584
|
+
draftDrawing = normalizeDrawingState({
|
|
3585
|
+
type: "ray",
|
|
3586
|
+
points: [point, point],
|
|
3587
|
+
color: defaults.color ?? "#2563eb",
|
|
3588
|
+
style: defaults.style ?? "solid",
|
|
3589
|
+
width: defaults.width ?? 2
|
|
3590
|
+
});
|
|
3591
|
+
draw();
|
|
3592
|
+
return true;
|
|
3593
|
+
}
|
|
3475
3594
|
if (activeDrawingTool === "fib-retracement") {
|
|
3476
3595
|
if (draftDrawing?.type === "fib-retracement") {
|
|
3477
3596
|
const completed = normalizeDrawingState({
|
|
@@ -3768,7 +3887,7 @@ function createChart(element, options = {}) {
|
|
|
3768
3887
|
setCrosshairPoint(null);
|
|
3769
3888
|
return;
|
|
3770
3889
|
}
|
|
3771
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3890
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement")) {
|
|
3772
3891
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3773
3892
|
if (nextPoint) {
|
|
3774
3893
|
draftDrawing = {
|
package/dist/index.d.cts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "trendline" | "fib-retracement";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
package/dist/index.js
CHANGED
|
@@ -2144,6 +2144,20 @@ function createChart(element, options = {}) {
|
|
|
2144
2144
|
drawDrawingLabel(drawing.label, midX, y, drawing.color);
|
|
2145
2145
|
}
|
|
2146
2146
|
}
|
|
2147
|
+
} else if (drawing.type === "vertical-line") {
|
|
2148
|
+
const point = drawing.points[0];
|
|
2149
|
+
if (point) {
|
|
2150
|
+
const x = xFromDrawingPoint(point);
|
|
2151
|
+
ctx.beginPath();
|
|
2152
|
+
ctx.moveTo(crisp(x), crisp(chartTop));
|
|
2153
|
+
ctx.lineTo(crisp(x), crisp(chartBottom));
|
|
2154
|
+
ctx.stroke();
|
|
2155
|
+
const handleY = (chartTop + chartBottom) / 2;
|
|
2156
|
+
drawDrawingHandle(x, handleY, drawing.color);
|
|
2157
|
+
if (drawing.label) {
|
|
2158
|
+
drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2147
2161
|
} else if (drawing.type === "trendline") {
|
|
2148
2162
|
const first = drawing.points[0];
|
|
2149
2163
|
const second = drawing.points[1];
|
|
@@ -2164,6 +2178,36 @@ function createChart(element, options = {}) {
|
|
|
2164
2178
|
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2165
2179
|
}
|
|
2166
2180
|
}
|
|
2181
|
+
} else if (drawing.type === "ray") {
|
|
2182
|
+
const first = drawing.points[0];
|
|
2183
|
+
const second = drawing.points[1];
|
|
2184
|
+
if (first && second) {
|
|
2185
|
+
const firstX = xFromDrawingPoint(first);
|
|
2186
|
+
const firstY = yFromPrice(first.price);
|
|
2187
|
+
const secondX = xFromDrawingPoint(second);
|
|
2188
|
+
const secondY = yFromPrice(second.price);
|
|
2189
|
+
const dx = secondX - firstX;
|
|
2190
|
+
const dy = secondY - firstY;
|
|
2191
|
+
let endX = secondX;
|
|
2192
|
+
let endY = secondY;
|
|
2193
|
+
const len = Math.hypot(dx, dy);
|
|
2194
|
+
if (len > 0) {
|
|
2195
|
+
const far = Math.abs(chartRight - chartLeft) + Math.abs(chartBottom - chartTop) + 2e3;
|
|
2196
|
+
endX = firstX + dx / len * far;
|
|
2197
|
+
endY = firstY + dy / len * far;
|
|
2198
|
+
}
|
|
2199
|
+
ctx.beginPath();
|
|
2200
|
+
ctx.moveTo(firstX, firstY);
|
|
2201
|
+
ctx.lineTo(endX, endY);
|
|
2202
|
+
ctx.stroke();
|
|
2203
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2204
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2205
|
+
if (drawing.label) {
|
|
2206
|
+
const midX = (firstX + secondX) / 2;
|
|
2207
|
+
const midY = (firstY + secondY) / 2;
|
|
2208
|
+
drawDrawingLabel(drawing.label, midX, midY, drawing.color);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2167
2211
|
} else if (drawing.type === "fib-retracement") {
|
|
2168
2212
|
const first = drawing.points[0];
|
|
2169
2213
|
const second = drawing.points[1];
|
|
@@ -3306,6 +3350,17 @@ function createChart(element, options = {}) {
|
|
|
3306
3350
|
if (Math.abs(y - lineY) <= 7) {
|
|
3307
3351
|
return { drawing, target: "line" };
|
|
3308
3352
|
}
|
|
3353
|
+
} else if (drawing.type === "vertical-line") {
|
|
3354
|
+
const point = drawing.points[0];
|
|
3355
|
+
if (!point) continue;
|
|
3356
|
+
const lineX = canvasXFromDrawingPoint(point);
|
|
3357
|
+
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
3358
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
3359
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3360
|
+
}
|
|
3361
|
+
if (Math.abs(x - lineX) <= 7) {
|
|
3362
|
+
return { drawing, target: "line" };
|
|
3363
|
+
}
|
|
3309
3364
|
} else if (drawing.type === "trendline") {
|
|
3310
3365
|
const first = drawing.points[0];
|
|
3311
3366
|
const second = drawing.points[1];
|
|
@@ -3323,6 +3378,31 @@ function createChart(element, options = {}) {
|
|
|
3323
3378
|
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3324
3379
|
return { drawing, target: "line" };
|
|
3325
3380
|
}
|
|
3381
|
+
} else if (drawing.type === "ray") {
|
|
3382
|
+
const first = drawing.points[0];
|
|
3383
|
+
const second = drawing.points[1];
|
|
3384
|
+
if (!first || !second) continue;
|
|
3385
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3386
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3387
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3388
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3389
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3390
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3391
|
+
}
|
|
3392
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3393
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3394
|
+
}
|
|
3395
|
+
const dx = x2 - x1;
|
|
3396
|
+
const dy = y2 - y1;
|
|
3397
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3398
|
+
if (lengthSq > 0) {
|
|
3399
|
+
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
3400
|
+
const projX = x1 + t * dx;
|
|
3401
|
+
const projY = y1 + t * dy;
|
|
3402
|
+
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
3403
|
+
return { drawing, target: "line" };
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3326
3406
|
} else if (drawing.type === "fib-retracement") {
|
|
3327
3407
|
const first = drawing.points[0];
|
|
3328
3408
|
const second = drawing.points[1];
|
|
@@ -3424,6 +3504,21 @@ function createChart(element, options = {}) {
|
|
|
3424
3504
|
draw();
|
|
3425
3505
|
return true;
|
|
3426
3506
|
}
|
|
3507
|
+
if (activeDrawingTool === "vertical-line") {
|
|
3508
|
+
const defaults = getDrawingToolDefaults("vertical-line");
|
|
3509
|
+
drawings.push(
|
|
3510
|
+
normalizeDrawingState({
|
|
3511
|
+
type: "vertical-line",
|
|
3512
|
+
points: [point],
|
|
3513
|
+
color: defaults.color ?? "#38bdf8",
|
|
3514
|
+
style: defaults.style ?? "solid",
|
|
3515
|
+
width: defaults.width ?? 1
|
|
3516
|
+
})
|
|
3517
|
+
);
|
|
3518
|
+
emitDrawingsChange();
|
|
3519
|
+
draw();
|
|
3520
|
+
return true;
|
|
3521
|
+
}
|
|
3427
3522
|
if (activeDrawingTool === "trendline") {
|
|
3428
3523
|
if (draftDrawing?.type === "trendline") {
|
|
3429
3524
|
const completed = normalizeDrawingState({
|
|
@@ -3448,6 +3543,30 @@ function createChart(element, options = {}) {
|
|
|
3448
3543
|
draw();
|
|
3449
3544
|
return true;
|
|
3450
3545
|
}
|
|
3546
|
+
if (activeDrawingTool === "ray") {
|
|
3547
|
+
if (draftDrawing?.type === "ray") {
|
|
3548
|
+
const completed = normalizeDrawingState({
|
|
3549
|
+
...serializeDrawing(draftDrawing),
|
|
3550
|
+
points: [draftDrawing.points[0], point]
|
|
3551
|
+
});
|
|
3552
|
+
drawings.push(completed);
|
|
3553
|
+
draftDrawing = null;
|
|
3554
|
+
activeDrawingTool = null;
|
|
3555
|
+
emitDrawingsChange();
|
|
3556
|
+
draw();
|
|
3557
|
+
return true;
|
|
3558
|
+
}
|
|
3559
|
+
const defaults = getDrawingToolDefaults("ray");
|
|
3560
|
+
draftDrawing = normalizeDrawingState({
|
|
3561
|
+
type: "ray",
|
|
3562
|
+
points: [point, point],
|
|
3563
|
+
color: defaults.color ?? "#2563eb",
|
|
3564
|
+
style: defaults.style ?? "solid",
|
|
3565
|
+
width: defaults.width ?? 2
|
|
3566
|
+
});
|
|
3567
|
+
draw();
|
|
3568
|
+
return true;
|
|
3569
|
+
}
|
|
3451
3570
|
if (activeDrawingTool === "fib-retracement") {
|
|
3452
3571
|
if (draftDrawing?.type === "fib-retracement") {
|
|
3453
3572
|
const completed = normalizeDrawingState({
|
|
@@ -3744,7 +3863,7 @@ function createChart(element, options = {}) {
|
|
|
3744
3863
|
setCrosshairPoint(null);
|
|
3745
3864
|
return;
|
|
3746
3865
|
}
|
|
3747
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "fib-retracement")) {
|
|
3866
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement")) {
|
|
3748
3867
|
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3749
3868
|
if (nextPoint) {
|
|
3750
3869
|
draftDrawing = {
|
package/docs/API.md
CHANGED
|
@@ -414,7 +414,11 @@ Volume style inputs:
|
|
|
414
414
|
Drawings are user-created chart tools, separate from indicators. They are interactive chart objects like horizontal lines and trendlines.
|
|
415
415
|
|
|
416
416
|
- `id?: string`
|
|
417
|
-
- `type: "horizontal-line" | "trendline"`
|
|
417
|
+
- `type: "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`
|
|
418
|
+
- `horizontal-line` / `vertical-line`: single-point, full-width/full-height line (one click to place)
|
|
419
|
+
- `trendline`: two-point segment (click start, click end)
|
|
420
|
+
- `ray`: two-point line that extends infinitely past the second point
|
|
421
|
+
- `fib-retracement`: two-point retracement with levels/bands
|
|
418
422
|
- `points: DrawingPoint[]`
|
|
419
423
|
- `visible?: boolean`
|
|
420
424
|
- `color?: string`
|
|
@@ -436,7 +440,10 @@ Tool workflow:
|
|
|
436
440
|
|
|
437
441
|
```ts
|
|
438
442
|
chart.setActiveDrawingTool("horizontal-line"); // next plot click creates a line
|
|
443
|
+
chart.setActiveDrawingTool("vertical-line"); // next plot click creates a vertical line
|
|
439
444
|
chart.setActiveDrawingTool("trendline"); // first click starts, second click commits
|
|
445
|
+
chart.setActiveDrawingTool("ray"); // first click starts, second click commits (extends past p2)
|
|
446
|
+
chart.setActiveDrawingTool("fib-retracement"); // first click starts, second click commits
|
|
440
447
|
chart.setActiveDrawingTool(null); // back to normal cursor/pan
|
|
441
448
|
```
|
|
442
449
|
|
|
@@ -466,8 +473,8 @@ Use `getDrawings()` / `setDrawings()` for persistence.
|
|
|
466
473
|
- `panY(priceDelta: number): void` (positive = move viewport up)
|
|
467
474
|
- `fitContent(): void` (x-only fit, keeps y zoom)
|
|
468
475
|
- `resetViewport(): void` (fit x + reset y auto-scale)
|
|
469
|
-
- `setActiveDrawingTool(tool: "horizontal-line" | "trendline" |
|
|
470
|
-
- `getActiveDrawingTool():
|
|
476
|
+
- `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
|
|
477
|
+
- `getActiveDrawingTool(): DrawingToolType | null`
|
|
471
478
|
- `setDrawings(drawings: DrawingObjectOptions[]): void`
|
|
472
479
|
- `getDrawings(): DrawingObjectOptions[]`
|
|
473
480
|
- `addDrawing(drawing: DrawingObjectOptions): string`
|
package/docs/RECIPES.md
CHANGED
|
@@ -106,11 +106,14 @@ chart.addPriceLine({
|
|
|
106
106
|
Drawing tools are separate from indicators. Indicators compute/render data series; drawings are user-created objects that can be persisted.
|
|
107
107
|
|
|
108
108
|
```ts
|
|
109
|
-
//
|
|
109
|
+
// Single-click tools: the next plot click creates the drawing.
|
|
110
110
|
chart.setActiveDrawingTool("horizontal-line");
|
|
111
|
+
chart.setActiveDrawingTool("vertical-line");
|
|
111
112
|
|
|
112
|
-
//
|
|
113
|
+
// Two-click tools: first click starts, second click commits.
|
|
113
114
|
chart.setActiveDrawingTool("trendline");
|
|
115
|
+
chart.setActiveDrawingTool("ray"); // extends infinitely past the second point
|
|
116
|
+
chart.setActiveDrawingTool("fib-retracement");
|
|
114
117
|
|
|
115
118
|
// Back to normal cursor/pan mode.
|
|
116
119
|
chart.setActiveDrawingTool(null);
|