apexify.js 5.0.4 → 5.1.1

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +337 -137
  2. package/README.md +65 -12
  3. package/dist/cjs/Canvas/ApexPainter.d.ts +272 -0
  4. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
  5. package/dist/cjs/Canvas/ApexPainter.js +2275 -125
  6. package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
  7. package/dist/cjs/Canvas/utils/Custom/advancedLines.d.ts +4 -4
  8. package/dist/cjs/Canvas/utils/Custom/advancedLines.d.ts.map +1 -1
  9. package/dist/cjs/Canvas/utils/Custom/advancedLines.js +63 -21
  10. package/dist/cjs/Canvas/utils/Custom/advancedLines.js.map +1 -1
  11. package/dist/cjs/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  12. package/dist/cjs/Canvas/utils/Custom/customLines.js +3 -0
  13. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
  14. package/dist/cjs/Canvas/utils/types.d.ts +5 -1
  15. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
  16. package/dist/cjs/Canvas/utils/types.js.map +1 -1
  17. package/dist/esm/Canvas/ApexPainter.d.ts +272 -0
  18. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
  19. package/dist/esm/Canvas/ApexPainter.js +2275 -125
  20. package/dist/esm/Canvas/ApexPainter.js.map +1 -1
  21. package/dist/esm/Canvas/utils/Custom/advancedLines.d.ts +4 -4
  22. package/dist/esm/Canvas/utils/Custom/advancedLines.d.ts.map +1 -1
  23. package/dist/esm/Canvas/utils/Custom/advancedLines.js +63 -21
  24. package/dist/esm/Canvas/utils/Custom/advancedLines.js.map +1 -1
  25. package/dist/esm/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  26. package/dist/esm/Canvas/utils/Custom/customLines.js +3 -0
  27. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
  28. package/dist/esm/Canvas/utils/types.d.ts +5 -1
  29. package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
  30. package/dist/esm/Canvas/utils/types.js.map +1 -1
  31. package/lib/Canvas/ApexPainter.ts +2973 -136
  32. package/lib/Canvas/utils/Custom/advancedLines.ts +387 -335
  33. package/lib/Canvas/utils/Custom/customLines.ts +206 -202
  34. package/lib/Canvas/utils/types.ts +983 -979
  35. package/package.json +198 -200
@@ -1,202 +1,206 @@
1
- import { SKRSContext2D } from '@napi-rs/canvas';
2
- import { createGradientFill } from "../Image/imageProperties";
3
- import { CustomOptions } from "../types";
4
- import { drawArrow, drawMarker, createSmoothPath, createCatmullRomPath, applyLinePattern, applyLineTexture, getPointOnLinePath } from "./advancedLines";
5
-
6
-
7
- export async function customLines(ctx: SKRSContext2D, options: CustomOptions[]): Promise<void> {
8
- let previousEndCoordinates: { x: number; y: number } | null = null;
9
- let currentStyle: CustomOptions['lineStyle'] | null = null;
10
- let inSingleLineSequence = false;
11
-
12
- for (let i = 0; i < options.length; i++) {
13
- const customOption = options[i];
14
- const { startCoordinates, endCoordinates, lineStyle, path, arrow, markers } = customOption;
15
- const isSingleLine = lineStyle?.singleLine;
16
-
17
- // Collect all points for path rendering
18
- const allPoints: Array<{ x: number; y: number }> = [];
19
- if (i === 0 || !isSingleLine) {
20
- allPoints.push(startCoordinates);
21
- }
22
- allPoints.push(endCoordinates);
23
-
24
- if (isSingleLine && !inSingleLineSequence) {
25
- currentStyle = lineStyle;
26
- inSingleLineSequence = true;
27
- ctx.beginPath();
28
- ctx.moveTo(startCoordinates.x, startCoordinates.y);
29
- }
30
-
31
- if (!isSingleLine && inSingleLineSequence) {
32
- ctx.stroke();
33
- if (currentStyle) {
34
- applyStroke(ctx, currentStyle, startCoordinates, endCoordinates);
35
- }
36
- inSingleLineSequence = false;
37
- currentStyle = null;
38
- }
39
-
40
- const start = inSingleLineSequence && previousEndCoordinates
41
- ? previousEndCoordinates
42
- : startCoordinates;
43
-
44
- if (!inSingleLineSequence) {
45
- ctx.beginPath();
46
- ctx.moveTo(start.x, start.y);
47
- }
48
-
49
- // Apply path smoothing if specified
50
- if (path && allPoints.length >= 2) {
51
- ctx.beginPath();
52
- if (path.type === 'smooth') {
53
- createSmoothPath(ctx, allPoints, path.tension ?? 0.5, path.closed ?? false);
54
- } else if (path.type === 'catmull-rom') {
55
- createCatmullRomPath(ctx, allPoints, path.tension ?? 0.5, path.closed ?? false);
56
- } else if (path.type === 'bezier' && allPoints.length >= 4) {
57
- ctx.moveTo(allPoints[0].x, allPoints[0].y);
58
- for (let j = 1; j < allPoints.length - 2; j += 3) {
59
- if (j + 2 < allPoints.length) {
60
- ctx.bezierCurveTo(
61
- allPoints[j].x, allPoints[j].y,
62
- allPoints[j + 1].x, allPoints[j + 1].y,
63
- allPoints[j + 2].x, allPoints[j + 2].y
64
- );
65
- }
66
- }
67
- } else {
68
- ctx.lineTo(endCoordinates.x, endCoordinates.y);
69
- }
70
- } else {
71
- ctx.lineTo(endCoordinates.x, endCoordinates.y);
72
- }
73
-
74
- const appliedStyle = inSingleLineSequence ? currentStyle : lineStyle;
75
- ctx.lineWidth = appliedStyle?.width || 1;
76
-
77
- if (appliedStyle?.gradient) {
78
- ctx.strokeStyle = createGradientFill(ctx, appliedStyle.gradient, { x: start.x, y: start.y, w: endCoordinates.x - start.x, h: endCoordinates.y - start.y });
79
- } else {
80
- ctx.strokeStyle = appliedStyle?.color || 'black';
81
- }
82
-
83
- ctx.lineJoin = appliedStyle?.lineJoin || 'miter';
84
- ctx.lineCap = appliedStyle?.lineCap || 'butt';
85
-
86
- // Apply line patterns
87
- if (appliedStyle?.pattern) {
88
- applyLinePattern(ctx, appliedStyle.pattern);
89
- } else if (appliedStyle?.lineDash) {
90
- ctx.setLineDash(appliedStyle.lineDash.dashArray || []);
91
- ctx.lineDashOffset = appliedStyle.lineDash.offset || 0;
92
- } else {
93
- ctx.setLineDash([]);
94
- }
95
-
96
- // Apply line texture if specified
97
- if (appliedStyle?.texture) {
98
- await applyLineTexture(ctx, appliedStyle.texture, appliedStyle.width || 1,
99
- Math.sqrt(Math.pow(endCoordinates.x - start.x, 2) + Math.pow(endCoordinates.y - start.y, 2)));
100
- }
101
-
102
- if (typeof appliedStyle?.lineRadius === 'number' && appliedStyle.lineRadius > 0) {
103
- const radius = appliedStyle.lineRadius;
104
- const dx = endCoordinates.x - start.x;
105
- const dy = endCoordinates.y - start.y;
106
- const angle = Math.atan2(dy, dx);
107
-
108
- ctx.lineCap = "round";
109
- ctx.stroke();
110
-
111
- ctx.beginPath();
112
- ctx.arc(start.x, start.y, radius, angle + Math.PI / 2, angle - Math.PI / 2, true);
113
- ctx.arc(endCoordinates.x, endCoordinates.y, radius, angle - Math.PI / 2, angle + Math.PI / 2, false);
114
- ctx.fill();
115
- }
116
-
117
- else if (appliedStyle?.lineRadius === 'circular') {
118
- const dx = endCoordinates.x - start.x;
119
- const dy = endCoordinates.y - start.y;
120
- const length = Math.sqrt(dx * dx + dy * dy);
121
-
122
- ctx.beginPath();
123
- ctx.arcTo(start.x, start.y, endCoordinates.x, endCoordinates.y, length / 2);
124
- ctx.arcTo(endCoordinates.x, endCoordinates.y, start.x, start.y, length / 2);
125
- ctx.closePath();
126
- ctx.stroke();
127
- }
128
-
129
- if (!inSingleLineSequence || i === options.length - 1) {
130
- ctx.stroke();
131
- if (appliedStyle) {
132
- applyStroke(ctx, appliedStyle, startCoordinates, endCoordinates);
133
- }
134
- }
135
-
136
- // Draw arrows if specified
137
- if (arrow) {
138
- const dx = endCoordinates.x - start.x;
139
- const dy = endCoordinates.y - start.y;
140
- const angle = Math.atan2(dy, dx);
141
- const arrowColor = arrow.color || appliedStyle?.color || 'black';
142
- const arrowSize = arrow.size || 10;
143
-
144
- if (arrow.start) {
145
- drawArrow(ctx, start.x, start.y, angle + Math.PI, arrowSize, arrow.style || 'filled', arrowColor);
146
- }
147
- if (arrow.end) {
148
- drawArrow(ctx, endCoordinates.x, endCoordinates.y, angle, arrowSize, arrow.style || 'filled', arrowColor);
149
- }
150
- }
151
-
152
- // Draw markers if specified
153
- if (markers && markers.length > 0) {
154
- const linePoints = [start, endCoordinates];
155
- for (const marker of markers) {
156
- const point = getPointOnLinePath(linePoints, marker.position);
157
- drawMarker(ctx, point.x, point.y, marker.shape, marker.size, marker.color);
158
- }
159
- }
160
-
161
- previousEndCoordinates = endCoordinates;
162
-
163
- if (!isSingleLine) {
164
- currentStyle = null;
165
- inSingleLineSequence = false;
166
- }
167
- }
168
- }
169
-
170
- function applyStroke(ctx: SKRSContext2D, style: CustomOptions['lineStyle'] | undefined, start: { x: number; y: number }, end: { x: number; y: number }): void {
171
- if (!style || !style.stroke) {
172
- return;
173
- }
174
-
175
- if (style.stroke) {
176
- const { color, width, gradient, lineRadius, lineCap } = style.stroke;
177
- const prevStrokeStyle = ctx.strokeStyle;
178
- const prevLineWidth = ctx.lineWidth;
179
- const prevLineCap = ctx.lineCap;
180
-
181
- if (gradient) {
182
- ctx.strokeStyle = createGradientFill(ctx, gradient, { x: start.x, y: start.y, w: end.x - start.x, h: end.y - start.y });
183
- } else {
184
- ctx.strokeStyle = color || prevStrokeStyle;
185
- }
186
-
187
- ctx.lineWidth = width || prevLineWidth;
188
- ctx.lineCap = lineCap || prevLineCap;
189
- ctx.stroke();
190
-
191
- if (typeof lineRadius === 'number' && lineRadius > 0) {
192
- ctx.beginPath();
193
- ctx.arc(start.x, start.y, lineRadius, 0, Math.PI * 2);
194
- ctx.arc(end.x, end.y, lineRadius, 0, Math.PI * 2);
195
- ctx.fill();
196
- }
197
-
198
- ctx.strokeStyle = prevStrokeStyle;
199
- ctx.lineWidth = prevLineWidth;
200
- ctx.lineCap = prevLineCap;
201
- }
202
- }
1
+ import { SKRSContext2D } from '@napi-rs/canvas';
2
+ import { createGradientFill } from "../Image/imageProperties";
3
+ import { CustomOptions } from "../types";
4
+ import { drawArrow, drawMarker, createSmoothPath, createCatmullRomPath, applyLinePattern, applyLineTexture, getPointOnLinePath } from "./advancedLines";
5
+
6
+
7
+ export async function customLines(ctx: SKRSContext2D, options: CustomOptions[]): Promise<void> {
8
+ // Enable high-quality anti-aliasing for ultra-smooth lines (graph quality)
9
+ ctx.imageSmoothingEnabled = true;
10
+ ctx.imageSmoothingQuality = 'high';
11
+
12
+ let previousEndCoordinates: { x: number; y: number } | null = null;
13
+ let currentStyle: CustomOptions['lineStyle'] | null = null;
14
+ let inSingleLineSequence = false;
15
+
16
+ for (let i = 0; i < options.length; i++) {
17
+ const customOption = options[i];
18
+ const { startCoordinates, endCoordinates, lineStyle, path, arrow, markers } = customOption;
19
+ const isSingleLine = lineStyle?.singleLine;
20
+
21
+ // Collect all points for path rendering
22
+ const allPoints: Array<{ x: number; y: number }> = [];
23
+ if (i === 0 || !isSingleLine) {
24
+ allPoints.push(startCoordinates);
25
+ }
26
+ allPoints.push(endCoordinates);
27
+
28
+ if (isSingleLine && !inSingleLineSequence) {
29
+ currentStyle = lineStyle;
30
+ inSingleLineSequence = true;
31
+ ctx.beginPath();
32
+ ctx.moveTo(startCoordinates.x, startCoordinates.y);
33
+ }
34
+
35
+ if (!isSingleLine && inSingleLineSequence) {
36
+ ctx.stroke();
37
+ if (currentStyle) {
38
+ applyStroke(ctx, currentStyle, startCoordinates, endCoordinates);
39
+ }
40
+ inSingleLineSequence = false;
41
+ currentStyle = null;
42
+ }
43
+
44
+ const start = inSingleLineSequence && previousEndCoordinates
45
+ ? previousEndCoordinates
46
+ : startCoordinates;
47
+
48
+ if (!inSingleLineSequence) {
49
+ ctx.beginPath();
50
+ ctx.moveTo(start.x, start.y);
51
+ }
52
+
53
+ // Apply path smoothing if specified
54
+ if (path && allPoints.length >= 2) {
55
+ ctx.beginPath();
56
+ if (path.type === 'smooth') {
57
+ createSmoothPath(ctx, allPoints, path.tension ?? 0.5, path.closed ?? false);
58
+ } else if (path.type === 'catmull-rom') {
59
+ createCatmullRomPath(ctx, allPoints, path.tension ?? 0.5, path.closed ?? false);
60
+ } else if (path.type === 'bezier' && allPoints.length >= 4) {
61
+ ctx.moveTo(allPoints[0].x, allPoints[0].y);
62
+ for (let j = 1; j < allPoints.length - 2; j += 3) {
63
+ if (j + 2 < allPoints.length) {
64
+ ctx.bezierCurveTo(
65
+ allPoints[j].x, allPoints[j].y,
66
+ allPoints[j + 1].x, allPoints[j + 1].y,
67
+ allPoints[j + 2].x, allPoints[j + 2].y
68
+ );
69
+ }
70
+ }
71
+ } else {
72
+ ctx.lineTo(endCoordinates.x, endCoordinates.y);
73
+ }
74
+ } else {
75
+ ctx.lineTo(endCoordinates.x, endCoordinates.y);
76
+ }
77
+
78
+ const appliedStyle = inSingleLineSequence ? currentStyle : lineStyle;
79
+ ctx.lineWidth = appliedStyle?.width || 1;
80
+
81
+ if (appliedStyle?.gradient) {
82
+ ctx.strokeStyle = createGradientFill(ctx, appliedStyle.gradient, { x: start.x, y: start.y, w: endCoordinates.x - start.x, h: endCoordinates.y - start.y });
83
+ } else {
84
+ ctx.strokeStyle = appliedStyle?.color || 'black';
85
+ }
86
+
87
+ ctx.lineJoin = appliedStyle?.lineJoin || 'miter';
88
+ ctx.lineCap = appliedStyle?.lineCap || 'butt';
89
+
90
+ // Apply line patterns
91
+ if (appliedStyle?.pattern) {
92
+ applyLinePattern(ctx, appliedStyle.pattern);
93
+ } else if (appliedStyle?.lineDash) {
94
+ ctx.setLineDash(appliedStyle.lineDash.dashArray || []);
95
+ ctx.lineDashOffset = appliedStyle.lineDash.offset || 0;
96
+ } else {
97
+ ctx.setLineDash([]);
98
+ }
99
+
100
+ // Apply line texture if specified
101
+ if (appliedStyle?.texture) {
102
+ await applyLineTexture(ctx, appliedStyle.texture, appliedStyle.width || 1,
103
+ Math.sqrt(Math.pow(endCoordinates.x - start.x, 2) + Math.pow(endCoordinates.y - start.y, 2)));
104
+ }
105
+
106
+ if (typeof appliedStyle?.lineRadius === 'number' && appliedStyle.lineRadius > 0) {
107
+ const radius = appliedStyle.lineRadius;
108
+ const dx = endCoordinates.x - start.x;
109
+ const dy = endCoordinates.y - start.y;
110
+ const angle = Math.atan2(dy, dx);
111
+
112
+ ctx.lineCap = "round";
113
+ ctx.stroke();
114
+
115
+ ctx.beginPath();
116
+ ctx.arc(start.x, start.y, radius, angle + Math.PI / 2, angle - Math.PI / 2, true);
117
+ ctx.arc(endCoordinates.x, endCoordinates.y, radius, angle - Math.PI / 2, angle + Math.PI / 2, false);
118
+ ctx.fill();
119
+ }
120
+
121
+ else if (appliedStyle?.lineRadius === 'circular') {
122
+ const dx = endCoordinates.x - start.x;
123
+ const dy = endCoordinates.y - start.y;
124
+ const length = Math.sqrt(dx * dx + dy * dy);
125
+
126
+ ctx.beginPath();
127
+ ctx.arcTo(start.x, start.y, endCoordinates.x, endCoordinates.y, length / 2);
128
+ ctx.arcTo(endCoordinates.x, endCoordinates.y, start.x, start.y, length / 2);
129
+ ctx.closePath();
130
+ ctx.stroke();
131
+ }
132
+
133
+ if (!inSingleLineSequence || i === options.length - 1) {
134
+ ctx.stroke();
135
+ if (appliedStyle) {
136
+ applyStroke(ctx, appliedStyle, startCoordinates, endCoordinates);
137
+ }
138
+ }
139
+
140
+ // Draw arrows if specified
141
+ if (arrow) {
142
+ const dx = endCoordinates.x - start.x;
143
+ const dy = endCoordinates.y - start.y;
144
+ const angle = Math.atan2(dy, dx);
145
+ const arrowColor = arrow.color || appliedStyle?.color || 'black';
146
+ const arrowSize = arrow.size || 10;
147
+
148
+ if (arrow.start) {
149
+ drawArrow(ctx, start.x, start.y, angle + Math.PI, arrowSize, arrow.style || 'filled', arrowColor);
150
+ }
151
+ if (arrow.end) {
152
+ drawArrow(ctx, endCoordinates.x, endCoordinates.y, angle, arrowSize, arrow.style || 'filled', arrowColor);
153
+ }
154
+ }
155
+
156
+ // Draw markers if specified
157
+ if (markers && markers.length > 0) {
158
+ const linePoints = [start, endCoordinates];
159
+ for (const marker of markers) {
160
+ const point = getPointOnLinePath(linePoints, marker.position);
161
+ drawMarker(ctx, point.x, point.y, marker.shape, marker.size, marker.color);
162
+ }
163
+ }
164
+
165
+ previousEndCoordinates = endCoordinates;
166
+
167
+ if (!isSingleLine) {
168
+ currentStyle = null;
169
+ inSingleLineSequence = false;
170
+ }
171
+ }
172
+ }
173
+
174
+ function applyStroke(ctx: SKRSContext2D, style: CustomOptions['lineStyle'] | undefined, start: { x: number; y: number }, end: { x: number; y: number }): void {
175
+ if (!style || !style.stroke) {
176
+ return;
177
+ }
178
+
179
+ if (style.stroke) {
180
+ const { color, width, gradient, lineRadius, lineCap } = style.stroke;
181
+ const prevStrokeStyle = ctx.strokeStyle;
182
+ const prevLineWidth = ctx.lineWidth;
183
+ const prevLineCap = ctx.lineCap;
184
+
185
+ if (gradient) {
186
+ ctx.strokeStyle = createGradientFill(ctx, gradient, { x: start.x, y: start.y, w: end.x - start.x, h: end.y - start.y });
187
+ } else {
188
+ ctx.strokeStyle = color || prevStrokeStyle;
189
+ }
190
+
191
+ ctx.lineWidth = width || prevLineWidth;
192
+ ctx.lineCap = lineCap || prevLineCap;
193
+ ctx.stroke();
194
+
195
+ if (typeof lineRadius === 'number' && lineRadius > 0) {
196
+ ctx.beginPath();
197
+ ctx.arc(start.x, start.y, lineRadius, 0, Math.PI * 2);
198
+ ctx.arc(end.x, end.y, lineRadius, 0, Math.PI * 2);
199
+ ctx.fill();
200
+ }
201
+
202
+ ctx.strokeStyle = prevStrokeStyle;
203
+ ctx.lineWidth = prevLineWidth;
204
+ ctx.lineCap = prevLineCap;
205
+ }
206
+ }