graphics-debug 0.0.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.
- package/README.md +117 -0
- package/dist/chunk-646ISP7J.js +27 -0
- package/dist/chunk-646ISP7J.js.map +1 -0
- package/dist/chunk-G7UBPMLZ.js +268 -0
- package/dist/chunk-G7UBPMLZ.js.map +1 -0
- package/dist/chunk-GDKEOGZB.js +56 -0
- package/dist/chunk-GDKEOGZB.js.map +1 -0
- package/dist/cli/cli.js +79 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli.js +386 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/getGraphicsObjectsFromLogString.js +7 -0
- package/dist/lib/getGraphicsObjectsFromLogString.js.map +1 -0
- package/dist/lib/getSvgFromGraphicsObject.js +7 -0
- package/dist/lib/getSvgFromGraphicsObject.js.map +1 -0
- package/dist/lib/index.js +19 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# graphics-debug
|
|
2
|
+
|
|
3
|
+
Module for debugging graphics, turn log output into meaningful markdown and SVG diagrams.
|
|
4
|
+
|
|
5
|
+
Just pipe in output with graphics JSON objects into `graphics-debug` (or `gd`) to get an html file
|
|
6
|
+
with all your graphics drawn-in.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
echo '{ graphics: { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" } }' | graphics-debug
|
|
10
|
+
# wrote to "test-graphic-1.debug.svg"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Don't have access to the cli? Paste into the online version: `TBA`
|
|
14
|
+
|
|
15
|
+
## Format
|
|
16
|
+
|
|
17
|
+
The `graphics` json object is very simple, here's the basic schema:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface GraphicsObject {
|
|
21
|
+
graphics: {
|
|
22
|
+
points?: { x: number; y: number; color?: string; label?: string }[]
|
|
23
|
+
lines?: { points: { x: number; y: number; stroke?: number }[] }[]
|
|
24
|
+
rects?: Array<{
|
|
25
|
+
center: { x: number; y: number }
|
|
26
|
+
width: number
|
|
27
|
+
height: number
|
|
28
|
+
fill?: string
|
|
29
|
+
stroke?: string
|
|
30
|
+
}>
|
|
31
|
+
circles?: Array<{
|
|
32
|
+
center: { x: number; y: number }
|
|
33
|
+
radius: number
|
|
34
|
+
fill?: string
|
|
35
|
+
stroke?: string
|
|
36
|
+
}>
|
|
37
|
+
grid?: { cellSize: number; label?: boolean }
|
|
38
|
+
coordinateSystem?: "cartesian" | "screen"
|
|
39
|
+
title?: string
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
When emiting a graphics object, keep the `{ graphics }` object on a single line,
|
|
45
|
+
`graphics-debug` won't parse multi-line `{ graphics }` objects. You can have
|
|
46
|
+
other content on same line as the `{ graphics }` object. This means you can't
|
|
47
|
+
use `console.log` to emit graphics objects, use the [debug](https://www.npmjs.com/package/debug)
|
|
48
|
+
library or `console.log(JSON.stringify(...))` instead.
|
|
49
|
+
|
|
50
|
+
## Library Usage
|
|
51
|
+
|
|
52
|
+
### Writing `graphics-debug` compatible logs
|
|
53
|
+
|
|
54
|
+
The best way to write `graphics-debug` compatible logs is to use the [`debug` library](https://www.npmjs.com/package/debug).
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import Debug from "debug"
|
|
58
|
+
|
|
59
|
+
const debugGraphics = Debug("mypackage:graphics")
|
|
60
|
+
|
|
61
|
+
const A = { x: 0, y: 0, label: "A" }
|
|
62
|
+
const B = { x: 1, y: 1, label: "B" }
|
|
63
|
+
|
|
64
|
+
debugGraphics({
|
|
65
|
+
graphics: {
|
|
66
|
+
points: [A, B],
|
|
67
|
+
title: "initial points for my algorithm",
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// ... do some algorithm stuff e.g....
|
|
72
|
+
const C = { x: (A.x + B.x) / 2, y: (A.y + B.y) / 2, label: "C" }
|
|
73
|
+
|
|
74
|
+
debugGraphics({
|
|
75
|
+
graphics: {
|
|
76
|
+
points: [A, B, C],
|
|
77
|
+
title: "final points for my algorithm",
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
To see the output, you'll need to run `DEBUG=mypackage:graphics` in your terminal
|
|
83
|
+
before running the program. This makes it easy to turn on/off the graphics output
|
|
84
|
+
|
|
85
|
+
You can also use `debugGraphics.enabled` to conditionally emit graphics based,
|
|
86
|
+
this is useful if it's expensive to compute the graphics object.
|
|
87
|
+
|
|
88
|
+
### Process Log Strings into HTML or SVGs
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import {
|
|
92
|
+
getSvgFromLogString,
|
|
93
|
+
getHtmlFromLogString,
|
|
94
|
+
getSvgsFromLogString,
|
|
95
|
+
} from "graphics-debug"
|
|
96
|
+
|
|
97
|
+
const logString = `
|
|
98
|
+
hello world! This is some other content that will be ignored
|
|
99
|
+
here's some graphics: { graphics: { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" } }
|
|
100
|
+
`
|
|
101
|
+
|
|
102
|
+
const svg = getSvgFromLogString(logString)
|
|
103
|
+
const html = getHtmlFromLogString(logString)
|
|
104
|
+
|
|
105
|
+
// If you want to parse for multiple SVGs
|
|
106
|
+
const svgs = getSvgsFromLogString(logString)
|
|
107
|
+
// Array<{ title: string; svg: string }>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Extract `graphics` objects from a Debug Log
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { getGraphicsObjectsFromLogString } from "graphics-debug"
|
|
114
|
+
|
|
115
|
+
const graphicsObjects = getGraphicsObjectsFromLogString(logString)
|
|
116
|
+
// Array<GraphicsObject>
|
|
117
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// lib/getGraphicsObjectsFromLogString.ts
|
|
2
|
+
function getGraphicsObjectsFromLogString(logString) {
|
|
3
|
+
const results = [];
|
|
4
|
+
const graphicsRegex = /\{[\s]*(?:"graphics"|graphics)[\s]*:[\s]*\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}[\s]*\}/g;
|
|
5
|
+
const matches = logString.match(graphicsRegex);
|
|
6
|
+
if (!matches) return results;
|
|
7
|
+
for (const match of matches) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(match);
|
|
10
|
+
results.push(parsed);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
try {
|
|
13
|
+
const fixed = match.replace(/(\b\w+)(?=\s*:)/g, '"$1"').replace(/,(\s*[}\]])/g, "$1").replace(/:\s*'([^']*)'/g, ':"$1"');
|
|
14
|
+
const parsed = JSON.parse(fixed);
|
|
15
|
+
results.push(parsed);
|
|
16
|
+
} catch (e2) {
|
|
17
|
+
console.warn("Failed to parse graphics object:", match, e2);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return results;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
getGraphicsObjectsFromLogString
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-646ISP7J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/getGraphicsObjectsFromLogString.ts"],"sourcesContent":["import type { GraphicsObject } from \"./types\";\n\n/**\n * Extracts graphics objects from a debug log string\n * Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)\n */\nexport function getGraphicsObjectsFromLogString(\n\tlogString: string,\n): GraphicsObject[] {\n\tconst results: GraphicsObject[] = [];\n\n\t// Match { graphics: ... } patterns, allowing for other content on same line\n\t// Use a more precise regex that handles nested structures\n\tconst graphicsRegex =\n\t\t/\\{[\\s]*(?:\"graphics\"|graphics)[\\s]*:[\\s]*\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}[\\s]*\\}/g;\n\tconst matches = logString.match(graphicsRegex);\n\n\tif (!matches) return results;\n\n\tfor (const match of matches) {\n\t\ttry {\n\t\t\t// First try parsing as regular JSON\n\t\t\tconst parsed = JSON.parse(match);\n\t\t\tresults.push(parsed);\n\t\t} catch (e) {\n\t\t\ttry {\n\t\t\t\t// If that fails, fix JSON formatting issues:\n\t\t\t\t// 1. Add quotes around unquoted keys\n\t\t\t\t// 2. Handle potential trailing commas\n\t\t\t\tconst fixed = match\n\t\t\t\t\t.replace(/(\\b\\w+)(?=\\s*:)/g, '\"$1\"') // Quote unquoted keys more precisely\n\t\t\t\t\t.replace(/,(\\s*[}\\]])/g, \"$1\") // Remove trailing commas\n\t\t\t\t\t.replace(/:\\s*'([^']*)'/g, ':\"$1\"'); // Convert single quotes to double quotes\n\n\t\t\t\tconst parsed = JSON.parse(fixed);\n\t\t\t\tresults.push(parsed);\n\t\t\t} catch (e) {\n\t\t\t\t// Skip invalid entries but log the error and the problematic match\n\t\t\t\tconsole.warn(\"Failed to parse graphics object:\", match, e);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"],"mappings":";AAMO,SAAS,gCACf,WACmB;AACnB,QAAM,UAA4B,CAAC;AAInC,QAAM,gBACL;AACD,QAAM,UAAU,UAAU,MAAM,aAAa;AAE7C,MAAI,CAAC,QAAS,QAAO;AAErB,aAAW,SAAS,SAAS;AAC5B,QAAI;AAEH,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,cAAQ,KAAK,MAAM;AAAA,IACpB,SAAS,GAAG;AACX,UAAI;AAIH,cAAM,QAAQ,MACZ,QAAQ,oBAAoB,MAAM,EAClC,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,kBAAkB,OAAO;AAEnC,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,gBAAQ,KAAK,MAAM;AAAA,MACpB,SAASA,IAAG;AAEX,gBAAQ,KAAK,oCAAoC,OAAOA,EAAC;AAAA,MAC1D;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;","names":["e"]}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// lib/getSvgFromGraphicsObject.ts
|
|
2
|
+
import {
|
|
3
|
+
compose,
|
|
4
|
+
translate,
|
|
5
|
+
scale,
|
|
6
|
+
applyToPoint
|
|
7
|
+
} from "transformation-matrix";
|
|
8
|
+
import { stringify } from "svgson";
|
|
9
|
+
import pretty from "pretty";
|
|
10
|
+
var DEFAULT_SVG_SIZE = 640;
|
|
11
|
+
var PADDING = 40;
|
|
12
|
+
function getBounds(graphics) {
|
|
13
|
+
const points = [
|
|
14
|
+
...graphics.points || [],
|
|
15
|
+
...(graphics.lines || []).flatMap((line) => line.points),
|
|
16
|
+
...(graphics.rects || []).flatMap((rect) => {
|
|
17
|
+
const halfWidth = rect.width / 2;
|
|
18
|
+
const halfHeight = rect.height / 2;
|
|
19
|
+
return [
|
|
20
|
+
{ x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },
|
|
21
|
+
{ x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },
|
|
22
|
+
{ x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },
|
|
23
|
+
{ x: rect.center.x + halfWidth, y: rect.center.y + halfHeight }
|
|
24
|
+
];
|
|
25
|
+
}),
|
|
26
|
+
...(graphics.circles || []).flatMap((circle) => [
|
|
27
|
+
{ x: circle.center.x - circle.radius, y: circle.center.y },
|
|
28
|
+
// left
|
|
29
|
+
{ x: circle.center.x + circle.radius, y: circle.center.y },
|
|
30
|
+
// right
|
|
31
|
+
{ x: circle.center.x, y: circle.center.y - circle.radius },
|
|
32
|
+
// top
|
|
33
|
+
{ x: circle.center.x, y: circle.center.y + circle.radius }
|
|
34
|
+
// bottom
|
|
35
|
+
])
|
|
36
|
+
];
|
|
37
|
+
if (points.length === 0) {
|
|
38
|
+
return { minX: -1, maxX: 1, minY: -1, maxY: 1 };
|
|
39
|
+
}
|
|
40
|
+
return points.reduce(
|
|
41
|
+
(bounds, point) => ({
|
|
42
|
+
minX: Math.min(bounds.minX, point.x),
|
|
43
|
+
maxX: Math.max(bounds.maxX, point.x),
|
|
44
|
+
minY: Math.min(bounds.minY, point.y),
|
|
45
|
+
maxY: Math.max(bounds.maxY, point.y)
|
|
46
|
+
}),
|
|
47
|
+
{ minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function getProjectionMatrix(bounds, coordinateSystem) {
|
|
51
|
+
const width = bounds.maxX - bounds.minX;
|
|
52
|
+
const height = bounds.maxY - bounds.minY;
|
|
53
|
+
const scale_factor = Math.min(
|
|
54
|
+
(DEFAULT_SVG_SIZE - 2 * PADDING) / width,
|
|
55
|
+
(DEFAULT_SVG_SIZE - 2 * PADDING) / height
|
|
56
|
+
);
|
|
57
|
+
return compose(
|
|
58
|
+
translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),
|
|
59
|
+
scale(
|
|
60
|
+
scale_factor,
|
|
61
|
+
coordinateSystem === "screen" ? -scale_factor : scale_factor
|
|
62
|
+
),
|
|
63
|
+
translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
function projectPoint(point, matrix) {
|
|
67
|
+
const projected = applyToPoint(matrix, { x: point.x, y: point.y });
|
|
68
|
+
return { ...point, ...projected };
|
|
69
|
+
}
|
|
70
|
+
function getSvgFromGraphicsObject(graphicsObj) {
|
|
71
|
+
const { graphics } = graphicsObj;
|
|
72
|
+
const bounds = getBounds(graphics);
|
|
73
|
+
const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem));
|
|
74
|
+
const svgObject = {
|
|
75
|
+
name: "svg",
|
|
76
|
+
type: "element",
|
|
77
|
+
attributes: {
|
|
78
|
+
width: DEFAULT_SVG_SIZE.toString(),
|
|
79
|
+
height: DEFAULT_SVG_SIZE.toString(),
|
|
80
|
+
viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,
|
|
81
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
82
|
+
},
|
|
83
|
+
children: [
|
|
84
|
+
// Points
|
|
85
|
+
...(graphics.points || []).map((point) => {
|
|
86
|
+
const projected = projectPoint(point, matrix);
|
|
87
|
+
return {
|
|
88
|
+
name: "g",
|
|
89
|
+
type: "element",
|
|
90
|
+
children: [
|
|
91
|
+
{
|
|
92
|
+
name: "circle",
|
|
93
|
+
type: "element",
|
|
94
|
+
attributes: {
|
|
95
|
+
cx: projected.x.toString(),
|
|
96
|
+
cy: projected.y.toString(),
|
|
97
|
+
r: "3",
|
|
98
|
+
fill: point.color || "black"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
...point.label ? [
|
|
102
|
+
{
|
|
103
|
+
name: "text",
|
|
104
|
+
type: "element",
|
|
105
|
+
attributes: {
|
|
106
|
+
x: (projected.x + 5).toString(),
|
|
107
|
+
y: (projected.y - 5).toString(),
|
|
108
|
+
"font-family": "sans-serif",
|
|
109
|
+
"font-size": "12"
|
|
110
|
+
},
|
|
111
|
+
children: [{ type: "text", value: point.label }]
|
|
112
|
+
}
|
|
113
|
+
] : []
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
}),
|
|
117
|
+
// Lines
|
|
118
|
+
...(graphics.lines || []).map((line) => ({
|
|
119
|
+
name: "polyline",
|
|
120
|
+
type: "element",
|
|
121
|
+
attributes: {
|
|
122
|
+
points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
123
|
+
fill: "none",
|
|
124
|
+
stroke: "black",
|
|
125
|
+
"stroke-width": (line.points[0].stroke || 1).toString()
|
|
126
|
+
}
|
|
127
|
+
})),
|
|
128
|
+
// Rectangles
|
|
129
|
+
...(graphics.rects || []).map((rect) => {
|
|
130
|
+
const projected = projectPoint(rect.center, matrix);
|
|
131
|
+
const scaledWidth = rect.width * matrix.a;
|
|
132
|
+
const scaledHeight = rect.height * matrix.d;
|
|
133
|
+
return {
|
|
134
|
+
name: "rect",
|
|
135
|
+
type: "element",
|
|
136
|
+
attributes: {
|
|
137
|
+
x: (projected.x - scaledWidth / 2).toString(),
|
|
138
|
+
y: (projected.y - scaledHeight / 2).toString(),
|
|
139
|
+
width: scaledWidth.toString(),
|
|
140
|
+
height: Math.abs(scaledHeight).toString(),
|
|
141
|
+
fill: rect.fill || "none",
|
|
142
|
+
stroke: rect.stroke || "black"
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}),
|
|
146
|
+
// Circles
|
|
147
|
+
...(graphics.circles || []).map((circle) => {
|
|
148
|
+
const projected = projectPoint(circle.center, matrix);
|
|
149
|
+
const scaledRadius = circle.radius * Math.abs(matrix.a);
|
|
150
|
+
return {
|
|
151
|
+
name: "circle",
|
|
152
|
+
type: "element",
|
|
153
|
+
attributes: {
|
|
154
|
+
cx: projected.x.toString(),
|
|
155
|
+
cy: projected.y.toString(),
|
|
156
|
+
r: scaledRadius.toString(),
|
|
157
|
+
fill: circle.fill || "none",
|
|
158
|
+
stroke: circle.stroke || "black"
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}),
|
|
162
|
+
// Crosshair lines and coordinates (initially hidden)
|
|
163
|
+
{
|
|
164
|
+
name: "g",
|
|
165
|
+
type: "element",
|
|
166
|
+
attributes: {
|
|
167
|
+
id: "crosshair",
|
|
168
|
+
style: "display: none"
|
|
169
|
+
},
|
|
170
|
+
children: [
|
|
171
|
+
{
|
|
172
|
+
name: "line",
|
|
173
|
+
type: "element",
|
|
174
|
+
attributes: {
|
|
175
|
+
id: "crosshair-h",
|
|
176
|
+
y1: "0",
|
|
177
|
+
y2: DEFAULT_SVG_SIZE.toString(),
|
|
178
|
+
stroke: "#666",
|
|
179
|
+
"stroke-width": "0.5"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "line",
|
|
184
|
+
type: "element",
|
|
185
|
+
attributes: {
|
|
186
|
+
id: "crosshair-v",
|
|
187
|
+
x1: "0",
|
|
188
|
+
x2: DEFAULT_SVG_SIZE.toString(),
|
|
189
|
+
stroke: "#666",
|
|
190
|
+
"stroke-width": "0.5"
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "text",
|
|
195
|
+
type: "element",
|
|
196
|
+
attributes: {
|
|
197
|
+
id: "coordinates",
|
|
198
|
+
"font-family": "monospace",
|
|
199
|
+
"font-size": "12",
|
|
200
|
+
fill: "#666"
|
|
201
|
+
},
|
|
202
|
+
children: [{ type: "text", value: "" }]
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
// Mouse tracking script
|
|
207
|
+
{
|
|
208
|
+
name: "script",
|
|
209
|
+
type: "element",
|
|
210
|
+
children: [
|
|
211
|
+
{
|
|
212
|
+
type: "text",
|
|
213
|
+
value: `
|
|
214
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
215
|
+
const svg = e.currentTarget;
|
|
216
|
+
const rect = svg.getBoundingClientRect();
|
|
217
|
+
const x = e.clientX - rect.left;
|
|
218
|
+
const y = e.clientY - rect.top;
|
|
219
|
+
const crosshair = svg.getElementById('crosshair');
|
|
220
|
+
const h = svg.getElementById('crosshair-h');
|
|
221
|
+
const v = svg.getElementById('crosshair-v');
|
|
222
|
+
const coords = svg.getElementById('coordinates');
|
|
223
|
+
|
|
224
|
+
crosshair.style.display = 'block';
|
|
225
|
+
h.setAttribute('x1', '0');
|
|
226
|
+
h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');
|
|
227
|
+
h.setAttribute('y1', y);
|
|
228
|
+
h.setAttribute('y2', y);
|
|
229
|
+
v.setAttribute('x1', x);
|
|
230
|
+
v.setAttribute('x2', x);
|
|
231
|
+
v.setAttribute('y1', '0');
|
|
232
|
+
v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');
|
|
233
|
+
|
|
234
|
+
// Calculate real coordinates using inverse transformation
|
|
235
|
+
const matrix = ${JSON.stringify(matrix)};
|
|
236
|
+
// Manually invert and apply the affine transform
|
|
237
|
+
// Since we only use translate and scale, we can directly compute:
|
|
238
|
+
// x' = (x - tx) / sx
|
|
239
|
+
// y' = (y - ty) / sy
|
|
240
|
+
const sx = matrix.a;
|
|
241
|
+
const sy = matrix.d;
|
|
242
|
+
const tx = matrix.e;
|
|
243
|
+
const ty = matrix.f;
|
|
244
|
+
const realPoint = {
|
|
245
|
+
x: (x - tx) / sx,
|
|
246
|
+
y: -(y - ty) / sy // Flip y back since we used negative scale
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
coords.textContent = \`(\${realPoint.x.toFixed(2)}, \${realPoint.y.toFixed(2)})\`;
|
|
250
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
251
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
252
|
+
});
|
|
253
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
254
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
255
|
+
});
|
|
256
|
+
`
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
};
|
|
262
|
+
return pretty(stringify(svgObject));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export {
|
|
266
|
+
getSvgFromGraphicsObject
|
|
267
|
+
};
|
|
268
|
+
//# sourceMappingURL=chunk-G7UBPMLZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/getSvgFromGraphicsObject.ts"],"sourcesContent":["import {\n transform,\n compose,\n translate,\n scale,\n applyToPoint,\n identity,\n type Matrix,\n} from \"transformation-matrix\"\nimport type { GraphicsObject, Point } from \"./types\"\nimport { stringify } from \"svgson\"\nimport pretty from \"pretty\"\n\nconst DEFAULT_SVG_SIZE = 640\nconst PADDING = 40\n\ninterface Bounds {\n minX: number\n maxX: number\n minY: number\n maxY: number\n}\n\nfunction getBounds(graphics: GraphicsObject[\"graphics\"]): Bounds {\n const points: Point[] = [\n ...(graphics.points || []),\n ...(graphics.lines || []).flatMap((line) => line.points),\n ...(graphics.rects || []).flatMap((rect) => {\n const halfWidth = rect.width / 2\n const halfHeight = rect.height / 2\n return [\n { x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y + halfHeight },\n ]\n }),\n ...(graphics.circles || []).flatMap((circle) => [\n { x: circle.center.x - circle.radius, y: circle.center.y }, // left\n { x: circle.center.x + circle.radius, y: circle.center.y }, // right\n { x: circle.center.x, y: circle.center.y - circle.radius }, // top\n { x: circle.center.x, y: circle.center.y + circle.radius }, // bottom\n ]),\n ]\n\n if (points.length === 0) {\n return { minX: -1, maxX: 1, minY: -1, maxY: 1 }\n }\n\n return points.reduce(\n (bounds, point) => ({\n minX: Math.min(bounds.minX, point.x),\n maxX: Math.max(bounds.maxX, point.x),\n minY: Math.min(bounds.minY, point.y),\n maxY: Math.max(bounds.maxY, point.y),\n }),\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity },\n )\n}\n\nfunction getProjectionMatrix(\n bounds: Bounds,\n coordinateSystem: GraphicsObject[\"graphics\"][\"coordinateSystem\"],\n) {\n const width = bounds.maxX - bounds.minX\n const height = bounds.maxY - bounds.minY\n\n const scale_factor = Math.min(\n (DEFAULT_SVG_SIZE - 2 * PADDING) / width,\n (DEFAULT_SVG_SIZE - 2 * PADDING) / height,\n )\n\n return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(\n scale_factor,\n coordinateSystem === \"screen\" ? -scale_factor : scale_factor,\n ),\n translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2)),\n )\n}\n\nfunction projectPoint(point: Point, matrix: Matrix) {\n const projected = applyToPoint(matrix, { x: point.x, y: point.y })\n return { ...point, ...projected }\n}\n\nexport function getSvgFromGraphicsObject(graphicsObj: GraphicsObject): string {\n const { graphics } = graphicsObj\n const bounds = getBounds(graphics)\n const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem))\n\n const svgObject = {\n name: \"svg\",\n type: \"element\",\n attributes: {\n width: DEFAULT_SVG_SIZE.toString(),\n height: DEFAULT_SVG_SIZE.toString(),\n viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,\n xmlns: \"http://www.w3.org/2000/svg\",\n },\n children: [\n // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(point.label\n ? [\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n x: (projected.x + 5).toString(),\n y: (projected.y - 5).toString(),\n \"font-family\": \"sans-serif\",\n \"font-size\": \"12\",\n },\n children: [{ type: \"text\", value: point.label }],\n },\n ]\n : []),\n ],\n }\n }),\n // Lines\n ...(graphics.lines || []).map((line) => ({\n name: \"polyline\",\n type: \"element\",\n attributes: {\n points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: \"black\",\n \"stroke-width\": (line.points[0].stroke || 1).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const projected = projectPoint(rect.center, matrix)\n const scaledWidth = rect.width * matrix.a\n const scaledHeight = rect.height * matrix.d\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n x: (projected.x - scaledWidth / 2).toString(),\n y: (projected.y - scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: Math.abs(scaledHeight).toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\n },\n }\n }),\n // Circles\n ...(graphics.circles || []).map((circle) => {\n const projected = projectPoint(circle.center, matrix)\n const scaledRadius = circle.radius * Math.abs(matrix.a)\n return {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\n },\n }\n }),\n // Crosshair lines and coordinates (initially hidden)\n {\n name: \"g\",\n type: \"element\",\n attributes: {\n id: \"crosshair\",\n style: \"display: none\",\n },\n children: [\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-h\",\n y1: \"0\",\n y2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-v\",\n x1: \"0\",\n x2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n id: \"coordinates\",\n \"font-family\": \"monospace\",\n \"font-size\": \"12\",\n fill: \"#666\",\n },\n children: [{ type: \"text\", value: \"\" }],\n },\n ],\n },\n // Mouse tracking script\n {\n name: \"script\",\n type: \"element\",\n children: [\n {\n type: \"text\",\n value: `\n document.currentScript.parentElement.addEventListener('mousemove', (e) => {\n const svg = e.currentTarget;\n const rect = svg.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const crosshair = svg.getElementById('crosshair');\n const h = svg.getElementById('crosshair-h');\n const v = svg.getElementById('crosshair-v');\n const coords = svg.getElementById('coordinates');\n \n crosshair.style.display = 'block';\n h.setAttribute('x1', '0');\n h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');\n h.setAttribute('y1', y);\n h.setAttribute('y2', y);\n v.setAttribute('x1', x);\n v.setAttribute('x2', x);\n v.setAttribute('y1', '0');\n v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');\n\n // Calculate real coordinates using inverse transformation\n const matrix = ${JSON.stringify(matrix)};\n // Manually invert and apply the affine transform\n // Since we only use translate and scale, we can directly compute:\n // x' = (x - tx) / sx\n // y' = (y - ty) / sy\n const sx = matrix.a;\n const sy = matrix.d;\n const tx = matrix.e; \n const ty = matrix.f;\n const realPoint = {\n x: (x - tx) / sx,\n y: -(y - ty) / sy // Flip y back since we used negative scale\n }\n \n coords.textContent = \\`(\\${realPoint.x.toFixed(2)}, \\${realPoint.y.toFixed(2)})\\`;\n coords.setAttribute('x', (x + 5).toString());\n coords.setAttribute('y', (y - 5).toString());\n });\n document.currentScript.parentElement.addEventListener('mouseleave', () => {\n document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';\n });\n `,\n },\n ],\n },\n ],\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n return pretty(stringify(svgObject as any))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGraphicsObjectsFromLogString
|
|
3
|
+
} from "./chunk-646ISP7J.js";
|
|
4
|
+
import {
|
|
5
|
+
getSvgFromGraphicsObject
|
|
6
|
+
} from "./chunk-G7UBPMLZ.js";
|
|
7
|
+
|
|
8
|
+
// lib/index.ts
|
|
9
|
+
function getSvgFromLogString(logString) {
|
|
10
|
+
const objects = getGraphicsObjectsFromLogString(logString);
|
|
11
|
+
if (objects.length === 0) return "";
|
|
12
|
+
return getSvgFromGraphicsObject(objects[0]);
|
|
13
|
+
}
|
|
14
|
+
function getHtmlFromLogString(logString) {
|
|
15
|
+
const svgs = getSvgsFromLogString(logString);
|
|
16
|
+
if (svgs.length === 0) return "";
|
|
17
|
+
const sections = svgs.map(
|
|
18
|
+
({ title, svg }) => `
|
|
19
|
+
<section>
|
|
20
|
+
<h2>${title}</h2>
|
|
21
|
+
${svg}
|
|
22
|
+
</section>
|
|
23
|
+
`
|
|
24
|
+
).join("\n");
|
|
25
|
+
return `
|
|
26
|
+
<!DOCTYPE html>
|
|
27
|
+
<html>
|
|
28
|
+
<head>
|
|
29
|
+
<title>Graphics Debug Output</title>
|
|
30
|
+
<style>
|
|
31
|
+
body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
32
|
+
h1 { color: #333; }
|
|
33
|
+
section { margin: 40px 0; }
|
|
34
|
+
svg { max-width: 100%; height: auto; border: 1px solid #eee; }
|
|
35
|
+
</style>
|
|
36
|
+
</head>
|
|
37
|
+
<body>
|
|
38
|
+
<h1>Graphics Debug Output</h1>
|
|
39
|
+
${sections}
|
|
40
|
+
</body>
|
|
41
|
+
</html>`;
|
|
42
|
+
}
|
|
43
|
+
function getSvgsFromLogString(logString) {
|
|
44
|
+
const objects = getGraphicsObjectsFromLogString(logString);
|
|
45
|
+
return objects.map((obj) => ({
|
|
46
|
+
title: obj.graphics.title || "Untitled Graphic",
|
|
47
|
+
svg: getSvgFromGraphicsObject(obj)
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
getSvgFromLogString,
|
|
53
|
+
getHtmlFromLogString,
|
|
54
|
+
getSvgsFromLogString
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=chunk-GDKEOGZB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/index.ts"],"sourcesContent":["import { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport type { Point, Line, Rect, Circle, GraphicsObject } from \"./types\"\nexport { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nexport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport function getSvgFromLogString(logString: string): string {\n const objects = getGraphicsObjectsFromLogString(logString)\n if (objects.length === 0) return \"\"\n return getSvgFromGraphicsObject(objects[0])\n}\n\nexport function getHtmlFromLogString(logString: string): string {\n const svgs = getSvgsFromLogString(logString)\n if (svgs.length === 0) return \"\"\n\n const sections = svgs\n .map(\n ({ title, svg }) => `\n <section>\n <h2>${title}</h2>\n ${svg}\n </section>\n `,\n )\n .join(\"\\n\")\n\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Graphics Debug Output</title>\n <style>\n body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }\n h1 { color: #333; }\n section { margin: 40px 0; }\n svg { max-width: 100%; height: auto; border: 1px solid #eee; }\n </style>\n</head>\n<body>\n <h1>Graphics Debug Output</h1>\n ${sections}\n</body>\n</html>`\n}\n\nexport function getSvgsFromLogString(\n logString: string,\n): Array<{ title: string; svg: string }> {\n const objects = getGraphicsObjectsFromLogString(logString)\n return objects.map((obj) => ({\n title: obj.graphics.title || \"Untitled Graphic\",\n svg: getSvgFromGraphicsObject(obj),\n }))\n}\n"],"mappings":";;;;;;;;AAOO,SAAS,oBAAoB,WAA2B;AAC7D,QAAM,UAAU,gCAAgC,SAAS;AACzD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,yBAAyB,QAAQ,CAAC,CAAC;AAC5C;AAEO,SAAS,qBAAqB,WAA2B;AAC9D,QAAM,OAAO,qBAAqB,SAAS;AAC3C,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,WAAW,KACd;AAAA,IACC,CAAC,EAAE,OAAO,IAAI,MAAM;AAAA;AAAA,YAEd,KAAK;AAAA,QACT,GAAG;AAAA;AAAA;AAAA,EAGP,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcL,QAAQ;AAAA;AAAA;AAGZ;AAEO,SAAS,qBACd,WACuC;AACvC,QAAM,UAAU,gCAAgC,SAAS;AACzD,SAAO,QAAQ,IAAI,CAAC,SAAS;AAAA,IAC3B,OAAO,IAAI,SAAS,SAAS;AAAA,IAC7B,KAAK,yBAAyB,GAAG;AAAA,EACnC,EAAE;AACJ;","names":[]}
|
package/dist/cli/cli.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getHtmlFromLogString,
|
|
4
|
+
getSvgsFromLogString
|
|
5
|
+
} from "../chunk-GDKEOGZB.js";
|
|
6
|
+
import {
|
|
7
|
+
getGraphicsObjectsFromLogString
|
|
8
|
+
} from "../chunk-646ISP7J.js";
|
|
9
|
+
import "../chunk-G7UBPMLZ.js";
|
|
10
|
+
|
|
11
|
+
// cli/cli.ts
|
|
12
|
+
import { parseArgs } from "node:util";
|
|
13
|
+
import "node:fs";
|
|
14
|
+
import { writeFileSync } from "node:fs";
|
|
15
|
+
async function getInput() {
|
|
16
|
+
if (process.stdin.isTTY) {
|
|
17
|
+
console.error(
|
|
18
|
+
"Error: No input provided. Pipe in content with graphics objects."
|
|
19
|
+
);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const chunks = [];
|
|
23
|
+
for await (const chunk of process.stdin) {
|
|
24
|
+
chunks.push(chunk);
|
|
25
|
+
}
|
|
26
|
+
return chunks.join("");
|
|
27
|
+
}
|
|
28
|
+
async function main() {
|
|
29
|
+
const { values } = parseArgs({
|
|
30
|
+
options: {
|
|
31
|
+
html: { type: "boolean" },
|
|
32
|
+
url: { type: "boolean" },
|
|
33
|
+
help: { type: "boolean" }
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (values.help) {
|
|
37
|
+
console.log(`
|
|
38
|
+
Usage: graphics-debug [options]
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--html Output a single HTML file with all graphics
|
|
42
|
+
--url Print a url to view the graphics in a browser
|
|
43
|
+
--help Show this help message
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
cat debug.log | graphics-debug
|
|
47
|
+
echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html
|
|
48
|
+
`);
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
const input = await getInput();
|
|
52
|
+
if (values.html) {
|
|
53
|
+
const html = getHtmlFromLogString(input);
|
|
54
|
+
writeFileSync("graphicsdebug.debug.html", html);
|
|
55
|
+
console.log('Wrote to "graphicsdebug.debug.html"');
|
|
56
|
+
} else if (values.url) {
|
|
57
|
+
const { url } = await fetch("https://gdstore.seve.workers.dev/store", {
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
graphicsObjects: getGraphicsObjectsFromLogString(input)
|
|
61
|
+
}),
|
|
62
|
+
headers: {
|
|
63
|
+
"Content-Type": "application/json"
|
|
64
|
+
}
|
|
65
|
+
}).then((res) => res.json());
|
|
66
|
+
} else {
|
|
67
|
+
const svgs = getSvgsFromLogString(input);
|
|
68
|
+
svgs.forEach((svg, i) => {
|
|
69
|
+
const filename = `${svg.title.toLowerCase().replace(/\s+/g, "-")}-${i + 1}.debug.svg`;
|
|
70
|
+
writeFileSync(filename, svg.svg);
|
|
71
|
+
console.log(`Wrote to "${filename}"`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error("Error:", err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../cli/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { readFileSync } from \"node:fs\"\nimport { writeFileSync } from \"node:fs\"\nimport {\n getSvgsFromLogString,\n getHtmlFromLogString,\n getGraphicsObjectsFromLogString,\n} from \"../lib\"\n\nasync function getInput(): Promise<string> {\n // Check if there's data being piped in\n if (process.stdin.isTTY) {\n console.error(\n \"Error: No input provided. Pipe in content with graphics objects.\",\n )\n process.exit(1)\n }\n\n const chunks = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n return chunks.join(\"\")\n}\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n html: { type: \"boolean\" },\n url: { type: \"boolean\" },\n help: { type: \"boolean\" },\n },\n })\n\n if (values.help) {\n console.log(`\nUsage: graphics-debug [options]\n\nOptions:\n --html Output a single HTML file with all graphics\n --url Print a url to view the graphics in a browser\n --help Show this help message\n\nExamples:\n cat debug.log | graphics-debug\n echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html\n `)\n process.exit(0)\n }\n\n const input = await getInput()\n\n if (values.html) {\n const html = getHtmlFromLogString(input)\n writeFileSync(\"graphicsdebug.debug.html\", html)\n console.log('Wrote to \"graphicsdebug.debug.html\"')\n } else if (values.url) {\n const { url } = await fetch(\"https://gdstore.seve.workers.dev/store\", {\n method: \"POST\",\n body: JSON.stringify({\n graphicsObjects: getGraphicsObjectsFromLogString(input),\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n }).then((res) => res.json())\n\n // console.log(\"https://graphicsdebug.com/\")\n // console.log(`Debug Graphics: ${url}`)\n } else {\n const svgs = getSvgsFromLogString(input)\n svgs.forEach((svg, i) => {\n const filename = `${svg.title.toLowerCase().replace(/\\s+/g, \"-\")}-${i + 1}.debug.svg`\n writeFileSync(filename, svg.svg)\n console.log(`Wrote to \"${filename}\"`)\n })\n }\n}\n\nmain().catch((err) => {\n console.error(\"Error:\", err)\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;;AACA,SAAS,iBAAiB;AAC1B,OAA6B;AAC7B,SAAS,qBAAqB;AAO9B,eAAe,WAA4B;AAEzC,MAAI,QAAQ,MAAM,OAAO;AACvB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,CAAC;AAChB,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,KAAK;AAAA,EACnB;AACA,SAAO,OAAO,KAAK,EAAE;AACvB;AAEA,eAAe,OAAO;AACpB,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,UAAU;AAAA,MACxB,KAAK,EAAE,MAAM,UAAU;AAAA,MACvB,MAAM,EAAE,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF,CAAC;AAED,MAAI,OAAO,MAAM;AACf,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAWX;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,MAAM,SAAS;AAE7B,MAAI,OAAO,MAAM;AACf,UAAM,OAAO,qBAAqB,KAAK;AACvC,kBAAc,4BAA4B,IAAI;AAC9C,YAAQ,IAAI,qCAAqC;AAAA,EACnD,WAAW,OAAO,KAAK;AACrB,UAAM,EAAE,IAAI,IAAI,MAAM,MAAM,0CAA0C;AAAA,MACpE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,iBAAiB,gCAAgC,KAAK;AAAA,MACxD,CAAC;AAAA,MACD,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC;AAAA,EAI7B,OAAO;AACL,UAAM,OAAO,qBAAqB,KAAK;AACvC,SAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,YAAM,WAAW,GAAG,IAAI,MAAM,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC;AACzE,oBAAc,UAAU,IAAI,GAAG;AAC/B,cAAQ,IAAI,aAAa,QAAQ,GAAG;AAAA,IACtC,CAAC;AAAA,EACH;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,UAAU,GAAG;AAC3B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// cli/cli.ts
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import "node:fs";
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
|
|
8
|
+
// lib/getGraphicsObjectsFromLogString.ts
|
|
9
|
+
function getGraphicsObjectsFromLogString(logString) {
|
|
10
|
+
const results = [];
|
|
11
|
+
const graphicsRegex = /\{[\s]*(?:"graphics"|graphics)[\s]*:[\s]*\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}[\s]*\}/g;
|
|
12
|
+
const matches = logString.match(graphicsRegex);
|
|
13
|
+
if (!matches) return results;
|
|
14
|
+
for (const match of matches) {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(match);
|
|
17
|
+
results.push(parsed);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
try {
|
|
20
|
+
const fixed = match.replace(/(\b\w+)(?=\s*:)/g, '"$1"').replace(/,(\s*[}\]])/g, "$1").replace(/:\s*'([^']*)'/g, ':"$1"');
|
|
21
|
+
const parsed = JSON.parse(fixed);
|
|
22
|
+
results.push(parsed);
|
|
23
|
+
} catch (e2) {
|
|
24
|
+
console.warn("Failed to parse graphics object:", match, e2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// lib/getSvgFromGraphicsObject.ts
|
|
32
|
+
import {
|
|
33
|
+
compose,
|
|
34
|
+
translate,
|
|
35
|
+
scale,
|
|
36
|
+
applyToPoint
|
|
37
|
+
} from "transformation-matrix";
|
|
38
|
+
import { stringify } from "svgson";
|
|
39
|
+
import pretty from "pretty";
|
|
40
|
+
var DEFAULT_SVG_SIZE = 640;
|
|
41
|
+
var PADDING = 40;
|
|
42
|
+
function getBounds(graphics) {
|
|
43
|
+
const points = [
|
|
44
|
+
...graphics.points || [],
|
|
45
|
+
...(graphics.lines || []).flatMap((line) => line.points),
|
|
46
|
+
...(graphics.rects || []).flatMap((rect) => {
|
|
47
|
+
const halfWidth = rect.width / 2;
|
|
48
|
+
const halfHeight = rect.height / 2;
|
|
49
|
+
return [
|
|
50
|
+
{ x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },
|
|
51
|
+
{ x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },
|
|
52
|
+
{ x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },
|
|
53
|
+
{ x: rect.center.x + halfWidth, y: rect.center.y + halfHeight }
|
|
54
|
+
];
|
|
55
|
+
}),
|
|
56
|
+
...(graphics.circles || []).flatMap((circle) => [
|
|
57
|
+
{ x: circle.center.x - circle.radius, y: circle.center.y },
|
|
58
|
+
// left
|
|
59
|
+
{ x: circle.center.x + circle.radius, y: circle.center.y },
|
|
60
|
+
// right
|
|
61
|
+
{ x: circle.center.x, y: circle.center.y - circle.radius },
|
|
62
|
+
// top
|
|
63
|
+
{ x: circle.center.x, y: circle.center.y + circle.radius }
|
|
64
|
+
// bottom
|
|
65
|
+
])
|
|
66
|
+
];
|
|
67
|
+
if (points.length === 0) {
|
|
68
|
+
return { minX: -1, maxX: 1, minY: -1, maxY: 1 };
|
|
69
|
+
}
|
|
70
|
+
return points.reduce(
|
|
71
|
+
(bounds, point) => ({
|
|
72
|
+
minX: Math.min(bounds.minX, point.x),
|
|
73
|
+
maxX: Math.max(bounds.maxX, point.x),
|
|
74
|
+
minY: Math.min(bounds.minY, point.y),
|
|
75
|
+
maxY: Math.max(bounds.maxY, point.y)
|
|
76
|
+
}),
|
|
77
|
+
{ minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function getProjectionMatrix(bounds, coordinateSystem) {
|
|
81
|
+
const width = bounds.maxX - bounds.minX;
|
|
82
|
+
const height = bounds.maxY - bounds.minY;
|
|
83
|
+
const scale_factor = Math.min(
|
|
84
|
+
(DEFAULT_SVG_SIZE - 2 * PADDING) / width,
|
|
85
|
+
(DEFAULT_SVG_SIZE - 2 * PADDING) / height
|
|
86
|
+
);
|
|
87
|
+
return compose(
|
|
88
|
+
translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),
|
|
89
|
+
scale(
|
|
90
|
+
scale_factor,
|
|
91
|
+
coordinateSystem === "screen" ? -scale_factor : scale_factor
|
|
92
|
+
),
|
|
93
|
+
translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function projectPoint(point, matrix) {
|
|
97
|
+
const projected = applyToPoint(matrix, { x: point.x, y: point.y });
|
|
98
|
+
return { ...point, ...projected };
|
|
99
|
+
}
|
|
100
|
+
function getSvgFromGraphicsObject(graphicsObj) {
|
|
101
|
+
const { graphics } = graphicsObj;
|
|
102
|
+
const bounds = getBounds(graphics);
|
|
103
|
+
const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem));
|
|
104
|
+
const svgObject = {
|
|
105
|
+
name: "svg",
|
|
106
|
+
type: "element",
|
|
107
|
+
attributes: {
|
|
108
|
+
width: DEFAULT_SVG_SIZE.toString(),
|
|
109
|
+
height: DEFAULT_SVG_SIZE.toString(),
|
|
110
|
+
viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,
|
|
111
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
112
|
+
},
|
|
113
|
+
children: [
|
|
114
|
+
// Points
|
|
115
|
+
...(graphics.points || []).map((point) => {
|
|
116
|
+
const projected = projectPoint(point, matrix);
|
|
117
|
+
return {
|
|
118
|
+
name: "g",
|
|
119
|
+
type: "element",
|
|
120
|
+
children: [
|
|
121
|
+
{
|
|
122
|
+
name: "circle",
|
|
123
|
+
type: "element",
|
|
124
|
+
attributes: {
|
|
125
|
+
cx: projected.x.toString(),
|
|
126
|
+
cy: projected.y.toString(),
|
|
127
|
+
r: "3",
|
|
128
|
+
fill: point.color || "black"
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
...point.label ? [
|
|
132
|
+
{
|
|
133
|
+
name: "text",
|
|
134
|
+
type: "element",
|
|
135
|
+
attributes: {
|
|
136
|
+
x: (projected.x + 5).toString(),
|
|
137
|
+
y: (projected.y - 5).toString(),
|
|
138
|
+
"font-family": "sans-serif",
|
|
139
|
+
"font-size": "12"
|
|
140
|
+
},
|
|
141
|
+
children: [{ type: "text", value: point.label }]
|
|
142
|
+
}
|
|
143
|
+
] : []
|
|
144
|
+
]
|
|
145
|
+
};
|
|
146
|
+
}),
|
|
147
|
+
// Lines
|
|
148
|
+
...(graphics.lines || []).map((line) => ({
|
|
149
|
+
name: "polyline",
|
|
150
|
+
type: "element",
|
|
151
|
+
attributes: {
|
|
152
|
+
points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
153
|
+
fill: "none",
|
|
154
|
+
stroke: "black",
|
|
155
|
+
"stroke-width": (line.points[0].stroke || 1).toString()
|
|
156
|
+
}
|
|
157
|
+
})),
|
|
158
|
+
// Rectangles
|
|
159
|
+
...(graphics.rects || []).map((rect) => {
|
|
160
|
+
const projected = projectPoint(rect.center, matrix);
|
|
161
|
+
const scaledWidth = rect.width * matrix.a;
|
|
162
|
+
const scaledHeight = rect.height * matrix.d;
|
|
163
|
+
return {
|
|
164
|
+
name: "rect",
|
|
165
|
+
type: "element",
|
|
166
|
+
attributes: {
|
|
167
|
+
x: (projected.x - scaledWidth / 2).toString(),
|
|
168
|
+
y: (projected.y - scaledHeight / 2).toString(),
|
|
169
|
+
width: scaledWidth.toString(),
|
|
170
|
+
height: Math.abs(scaledHeight).toString(),
|
|
171
|
+
fill: rect.fill || "none",
|
|
172
|
+
stroke: rect.stroke || "black"
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}),
|
|
176
|
+
// Circles
|
|
177
|
+
...(graphics.circles || []).map((circle) => {
|
|
178
|
+
const projected = projectPoint(circle.center, matrix);
|
|
179
|
+
const scaledRadius = circle.radius * Math.abs(matrix.a);
|
|
180
|
+
return {
|
|
181
|
+
name: "circle",
|
|
182
|
+
type: "element",
|
|
183
|
+
attributes: {
|
|
184
|
+
cx: projected.x.toString(),
|
|
185
|
+
cy: projected.y.toString(),
|
|
186
|
+
r: scaledRadius.toString(),
|
|
187
|
+
fill: circle.fill || "none",
|
|
188
|
+
stroke: circle.stroke || "black"
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}),
|
|
192
|
+
// Crosshair lines and coordinates (initially hidden)
|
|
193
|
+
{
|
|
194
|
+
name: "g",
|
|
195
|
+
type: "element",
|
|
196
|
+
attributes: {
|
|
197
|
+
id: "crosshair",
|
|
198
|
+
style: "display: none"
|
|
199
|
+
},
|
|
200
|
+
children: [
|
|
201
|
+
{
|
|
202
|
+
name: "line",
|
|
203
|
+
type: "element",
|
|
204
|
+
attributes: {
|
|
205
|
+
id: "crosshair-h",
|
|
206
|
+
y1: "0",
|
|
207
|
+
y2: DEFAULT_SVG_SIZE.toString(),
|
|
208
|
+
stroke: "#666",
|
|
209
|
+
"stroke-width": "0.5"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "line",
|
|
214
|
+
type: "element",
|
|
215
|
+
attributes: {
|
|
216
|
+
id: "crosshair-v",
|
|
217
|
+
x1: "0",
|
|
218
|
+
x2: DEFAULT_SVG_SIZE.toString(),
|
|
219
|
+
stroke: "#666",
|
|
220
|
+
"stroke-width": "0.5"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "text",
|
|
225
|
+
type: "element",
|
|
226
|
+
attributes: {
|
|
227
|
+
id: "coordinates",
|
|
228
|
+
"font-family": "monospace",
|
|
229
|
+
"font-size": "12",
|
|
230
|
+
fill: "#666"
|
|
231
|
+
},
|
|
232
|
+
children: [{ type: "text", value: "" }]
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
// Mouse tracking script
|
|
237
|
+
{
|
|
238
|
+
name: "script",
|
|
239
|
+
type: "element",
|
|
240
|
+
children: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
value: `
|
|
244
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
245
|
+
const svg = e.currentTarget;
|
|
246
|
+
const rect = svg.getBoundingClientRect();
|
|
247
|
+
const x = e.clientX - rect.left;
|
|
248
|
+
const y = e.clientY - rect.top;
|
|
249
|
+
const crosshair = svg.getElementById('crosshair');
|
|
250
|
+
const h = svg.getElementById('crosshair-h');
|
|
251
|
+
const v = svg.getElementById('crosshair-v');
|
|
252
|
+
const coords = svg.getElementById('coordinates');
|
|
253
|
+
|
|
254
|
+
crosshair.style.display = 'block';
|
|
255
|
+
h.setAttribute('x1', '0');
|
|
256
|
+
h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');
|
|
257
|
+
h.setAttribute('y1', y);
|
|
258
|
+
h.setAttribute('y2', y);
|
|
259
|
+
v.setAttribute('x1', x);
|
|
260
|
+
v.setAttribute('x2', x);
|
|
261
|
+
v.setAttribute('y1', '0');
|
|
262
|
+
v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');
|
|
263
|
+
|
|
264
|
+
// Calculate real coordinates using inverse transformation
|
|
265
|
+
const matrix = ${JSON.stringify(matrix)};
|
|
266
|
+
// Manually invert and apply the affine transform
|
|
267
|
+
// Since we only use translate and scale, we can directly compute:
|
|
268
|
+
// x' = (x - tx) / sx
|
|
269
|
+
// y' = (y - ty) / sy
|
|
270
|
+
const sx = matrix.a;
|
|
271
|
+
const sy = matrix.d;
|
|
272
|
+
const tx = matrix.e;
|
|
273
|
+
const ty = matrix.f;
|
|
274
|
+
const realPoint = {
|
|
275
|
+
x: (x - tx) / sx,
|
|
276
|
+
y: -(y - ty) / sy // Flip y back since we used negative scale
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
coords.textContent = \`(\${realPoint.x.toFixed(2)}, \${realPoint.y.toFixed(2)})\`;
|
|
280
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
281
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
282
|
+
});
|
|
283
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
284
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
285
|
+
});
|
|
286
|
+
`
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
};
|
|
292
|
+
return pretty(stringify(svgObject));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// lib/index.ts
|
|
296
|
+
function getHtmlFromLogString(logString) {
|
|
297
|
+
const svgs = getSvgsFromLogString(logString);
|
|
298
|
+
if (svgs.length === 0) return "";
|
|
299
|
+
const sections = svgs.map(
|
|
300
|
+
({ title, svg }) => `
|
|
301
|
+
<section>
|
|
302
|
+
<h2>${title}</h2>
|
|
303
|
+
${svg}
|
|
304
|
+
</section>
|
|
305
|
+
`
|
|
306
|
+
).join("\n");
|
|
307
|
+
return `
|
|
308
|
+
<!DOCTYPE html>
|
|
309
|
+
<html>
|
|
310
|
+
<head>
|
|
311
|
+
<title>Graphics Debug Output</title>
|
|
312
|
+
<style>
|
|
313
|
+
body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
314
|
+
h1 { color: #333; }
|
|
315
|
+
section { margin: 40px 0; }
|
|
316
|
+
svg { max-width: 100%; height: auto; border: 1px solid #eee; }
|
|
317
|
+
</style>
|
|
318
|
+
</head>
|
|
319
|
+
<body>
|
|
320
|
+
<h1>Graphics Debug Output</h1>
|
|
321
|
+
${sections}
|
|
322
|
+
</body>
|
|
323
|
+
</html>`;
|
|
324
|
+
}
|
|
325
|
+
function getSvgsFromLogString(logString) {
|
|
326
|
+
const objects = getGraphicsObjectsFromLogString(logString);
|
|
327
|
+
return objects.map((obj) => ({
|
|
328
|
+
title: obj.graphics.title || "Untitled Graphic",
|
|
329
|
+
svg: getSvgFromGraphicsObject(obj)
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// cli/cli.ts
|
|
334
|
+
async function getInput() {
|
|
335
|
+
if (process.stdin.isTTY) {
|
|
336
|
+
console.error(
|
|
337
|
+
"Error: No input provided. Pipe in content with graphics objects."
|
|
338
|
+
);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
const chunks = [];
|
|
342
|
+
for await (const chunk of process.stdin) {
|
|
343
|
+
chunks.push(chunk);
|
|
344
|
+
}
|
|
345
|
+
return chunks.join("");
|
|
346
|
+
}
|
|
347
|
+
async function main() {
|
|
348
|
+
const { values } = parseArgs({
|
|
349
|
+
options: {
|
|
350
|
+
html: { type: "boolean" },
|
|
351
|
+
help: { type: "boolean" }
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
if (values.help) {
|
|
355
|
+
console.log(`
|
|
356
|
+
Usage: graphics-debug [options]
|
|
357
|
+
|
|
358
|
+
Options:
|
|
359
|
+
--html Output a single HTML file with all graphics
|
|
360
|
+
--help Show this help message
|
|
361
|
+
|
|
362
|
+
Examples:
|
|
363
|
+
cat debug.log | graphics-debug
|
|
364
|
+
echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html
|
|
365
|
+
`);
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}
|
|
368
|
+
const input = await getInput();
|
|
369
|
+
if (values.html) {
|
|
370
|
+
const html = getHtmlFromLogString(input);
|
|
371
|
+
writeFileSync("graphicsdebug.debug.html", html);
|
|
372
|
+
console.log('Wrote to "graphicsdebug.debug.html"');
|
|
373
|
+
} else {
|
|
374
|
+
const svgs = getSvgsFromLogString(input);
|
|
375
|
+
svgs.forEach((svg, i) => {
|
|
376
|
+
const filename = `${svg.title.toLowerCase().replace(/\s+/g, "-")}-${i + 1}.debug.svg`;
|
|
377
|
+
writeFileSync(filename, svg.svg);
|
|
378
|
+
console.log(`Wrote to "${filename}"`);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
main().catch((err) => {
|
|
383
|
+
console.error("Error:", err);
|
|
384
|
+
process.exit(1);
|
|
385
|
+
});
|
|
386
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/cli.ts","../lib/getGraphicsObjectsFromLogString.ts","../lib/getSvgFromGraphicsObject.ts","../lib/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { readFileSync } from \"node:fs\"\nimport { writeFileSync } from \"node:fs\"\nimport { getSvgsFromLogString, getHtmlFromLogString } from \"../lib\"\n\nasync function getInput(): Promise<string> {\n // Check if there's data being piped in\n if (process.stdin.isTTY) {\n console.error(\n \"Error: No input provided. Pipe in content with graphics objects.\",\n )\n process.exit(1)\n }\n\n const chunks = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n return chunks.join(\"\")\n}\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n html: { type: \"boolean\" },\n help: { type: \"boolean\" },\n },\n })\n\n if (values.help) {\n console.log(`\nUsage: graphics-debug [options]\n\nOptions:\n --html Output a single HTML file with all graphics\n --help Show this help message\n\nExamples:\n cat debug.log | graphics-debug\n echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html\n `)\n process.exit(0)\n }\n\n const input = await getInput()\n\n if (values.html) {\n const html = getHtmlFromLogString(input)\n writeFileSync(\"graphicsdebug.debug.html\", html)\n console.log('Wrote to \"graphicsdebug.debug.html\"')\n } else {\n const svgs = getSvgsFromLogString(input)\n svgs.forEach((svg, i) => {\n const filename = `${svg.title.toLowerCase().replace(/\\s+/g, \"-\")}-${i + 1}.debug.svg`\n writeFileSync(filename, svg.svg)\n console.log(`Wrote to \"${filename}\"`)\n })\n }\n}\n\nmain().catch((err) => {\n console.error(\"Error:\", err)\n process.exit(1)\n})\n","import type { GraphicsObject } from \"./types\";\n\n/**\n * Extracts graphics objects from a debug log string\n * Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)\n */\nexport function getGraphicsObjectsFromLogString(\n\tlogString: string,\n): GraphicsObject[] {\n\tconst results: GraphicsObject[] = [];\n\n\t// Match { graphics: ... } patterns, allowing for other content on same line\n\t// Use a more precise regex that handles nested structures\n\tconst graphicsRegex =\n\t\t/\\{[\\s]*(?:\"graphics\"|graphics)[\\s]*:[\\s]*\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}[\\s]*\\}/g;\n\tconst matches = logString.match(graphicsRegex);\n\n\tif (!matches) return results;\n\n\tfor (const match of matches) {\n\t\ttry {\n\t\t\t// First try parsing as regular JSON\n\t\t\tconst parsed = JSON.parse(match);\n\t\t\tresults.push(parsed);\n\t\t} catch (e) {\n\t\t\ttry {\n\t\t\t\t// If that fails, fix JSON formatting issues:\n\t\t\t\t// 1. Add quotes around unquoted keys\n\t\t\t\t// 2. Handle potential trailing commas\n\t\t\t\tconst fixed = match\n\t\t\t\t\t.replace(/(\\b\\w+)(?=\\s*:)/g, '\"$1\"') // Quote unquoted keys more precisely\n\t\t\t\t\t.replace(/,(\\s*[}\\]])/g, \"$1\") // Remove trailing commas\n\t\t\t\t\t.replace(/:\\s*'([^']*)'/g, ':\"$1\"'); // Convert single quotes to double quotes\n\n\t\t\t\tconst parsed = JSON.parse(fixed);\n\t\t\t\tresults.push(parsed);\n\t\t\t} catch (e) {\n\t\t\t\t// Skip invalid entries but log the error and the problematic match\n\t\t\t\tconsole.warn(\"Failed to parse graphics object:\", match, e);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n","import {\n transform,\n compose,\n translate,\n scale,\n applyToPoint,\n identity,\n type Matrix,\n} from \"transformation-matrix\"\nimport type { GraphicsObject, Point } from \"./types\"\nimport { stringify } from \"svgson\"\nimport pretty from \"pretty\"\n\nconst DEFAULT_SVG_SIZE = 640\nconst PADDING = 40\n\ninterface Bounds {\n minX: number\n maxX: number\n minY: number\n maxY: number\n}\n\nfunction getBounds(graphics: GraphicsObject[\"graphics\"]): Bounds {\n const points: Point[] = [\n ...(graphics.points || []),\n ...(graphics.lines || []).flatMap((line) => line.points),\n ...(graphics.rects || []).flatMap((rect) => {\n const halfWidth = rect.width / 2\n const halfHeight = rect.height / 2\n return [\n { x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y + halfHeight },\n ]\n }),\n ...(graphics.circles || []).flatMap((circle) => [\n { x: circle.center.x - circle.radius, y: circle.center.y }, // left\n { x: circle.center.x + circle.radius, y: circle.center.y }, // right\n { x: circle.center.x, y: circle.center.y - circle.radius }, // top\n { x: circle.center.x, y: circle.center.y + circle.radius }, // bottom\n ]),\n ]\n\n if (points.length === 0) {\n return { minX: -1, maxX: 1, minY: -1, maxY: 1 }\n }\n\n return points.reduce(\n (bounds, point) => ({\n minX: Math.min(bounds.minX, point.x),\n maxX: Math.max(bounds.maxX, point.x),\n minY: Math.min(bounds.minY, point.y),\n maxY: Math.max(bounds.maxY, point.y),\n }),\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity },\n )\n}\n\nfunction getProjectionMatrix(\n bounds: Bounds,\n coordinateSystem: GraphicsObject[\"graphics\"][\"coordinateSystem\"],\n) {\n const width = bounds.maxX - bounds.minX\n const height = bounds.maxY - bounds.minY\n\n const scale_factor = Math.min(\n (DEFAULT_SVG_SIZE - 2 * PADDING) / width,\n (DEFAULT_SVG_SIZE - 2 * PADDING) / height,\n )\n\n return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(\n scale_factor,\n coordinateSystem === \"screen\" ? -scale_factor : scale_factor,\n ),\n translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2)),\n )\n}\n\nfunction projectPoint(point: Point, matrix: Matrix) {\n const projected = applyToPoint(matrix, { x: point.x, y: point.y })\n return { ...point, ...projected }\n}\n\nexport function getSvgFromGraphicsObject(graphicsObj: GraphicsObject): string {\n const { graphics } = graphicsObj\n const bounds = getBounds(graphics)\n const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem))\n\n const svgObject = {\n name: \"svg\",\n type: \"element\",\n attributes: {\n width: DEFAULT_SVG_SIZE.toString(),\n height: DEFAULT_SVG_SIZE.toString(),\n viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,\n xmlns: \"http://www.w3.org/2000/svg\",\n },\n children: [\n // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(point.label\n ? [\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n x: (projected.x + 5).toString(),\n y: (projected.y - 5).toString(),\n \"font-family\": \"sans-serif\",\n \"font-size\": \"12\",\n },\n children: [{ type: \"text\", value: point.label }],\n },\n ]\n : []),\n ],\n }\n }),\n // Lines\n ...(graphics.lines || []).map((line) => ({\n name: \"polyline\",\n type: \"element\",\n attributes: {\n points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: \"black\",\n \"stroke-width\": (line.points[0].stroke || 1).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const projected = projectPoint(rect.center, matrix)\n const scaledWidth = rect.width * matrix.a\n const scaledHeight = rect.height * matrix.d\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n x: (projected.x - scaledWidth / 2).toString(),\n y: (projected.y - scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: Math.abs(scaledHeight).toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\n },\n }\n }),\n // Circles\n ...(graphics.circles || []).map((circle) => {\n const projected = projectPoint(circle.center, matrix)\n const scaledRadius = circle.radius * Math.abs(matrix.a)\n return {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\n },\n }\n }),\n // Crosshair lines and coordinates (initially hidden)\n {\n name: \"g\",\n type: \"element\",\n attributes: {\n id: \"crosshair\",\n style: \"display: none\",\n },\n children: [\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-h\",\n y1: \"0\",\n y2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-v\",\n x1: \"0\",\n x2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n id: \"coordinates\",\n \"font-family\": \"monospace\",\n \"font-size\": \"12\",\n fill: \"#666\",\n },\n children: [{ type: \"text\", value: \"\" }],\n },\n ],\n },\n // Mouse tracking script\n {\n name: \"script\",\n type: \"element\",\n children: [\n {\n type: \"text\",\n value: `\n document.currentScript.parentElement.addEventListener('mousemove', (e) => {\n const svg = e.currentTarget;\n const rect = svg.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const crosshair = svg.getElementById('crosshair');\n const h = svg.getElementById('crosshair-h');\n const v = svg.getElementById('crosshair-v');\n const coords = svg.getElementById('coordinates');\n \n crosshair.style.display = 'block';\n h.setAttribute('x1', '0');\n h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');\n h.setAttribute('y1', y);\n h.setAttribute('y2', y);\n v.setAttribute('x1', x);\n v.setAttribute('x2', x);\n v.setAttribute('y1', '0');\n v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');\n\n // Calculate real coordinates using inverse transformation\n const matrix = ${JSON.stringify(matrix)};\n // Manually invert and apply the affine transform\n // Since we only use translate and scale, we can directly compute:\n // x' = (x - tx) / sx\n // y' = (y - ty) / sy\n const sx = matrix.a;\n const sy = matrix.d;\n const tx = matrix.e; \n const ty = matrix.f;\n const realPoint = {\n x: (x - tx) / sx,\n y: -(y - ty) / sy // Flip y back since we used negative scale\n }\n \n coords.textContent = \\`(\\${realPoint.x.toFixed(2)}, \\${realPoint.y.toFixed(2)})\\`;\n coords.setAttribute('x', (x + 5).toString());\n coords.setAttribute('y', (y - 5).toString());\n });\n document.currentScript.parentElement.addEventListener('mouseleave', () => {\n document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';\n });\n `,\n },\n ],\n },\n ],\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n return pretty(stringify(svgObject as any))\n}\n","import { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport type { Point, Line, Rect, Circle, GraphicsObject } from \"./types\"\nexport { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nexport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport function getSvgFromLogString(logString: string): string {\n const objects = getGraphicsObjectsFromLogString(logString)\n if (objects.length === 0) return \"\"\n return getSvgFromGraphicsObject(objects[0])\n}\n\nexport function getHtmlFromLogString(logString: string): string {\n const svgs = getSvgsFromLogString(logString)\n if (svgs.length === 0) return \"\"\n\n const sections = svgs\n .map(\n ({ title, svg }) => `\n <section>\n <h2>${title}</h2>\n ${svg}\n </section>\n `,\n )\n .join(\"\\n\")\n\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Graphics Debug Output</title>\n <style>\n body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }\n h1 { color: #333; }\n section { margin: 40px 0; }\n svg { max-width: 100%; height: auto; border: 1px solid #eee; }\n </style>\n</head>\n<body>\n <h1>Graphics Debug Output</h1>\n ${sections}\n</body>\n</html>`\n}\n\nexport function getSvgsFromLogString(\n logString: string,\n): Array<{ title: string; svg: string }> {\n const objects = getGraphicsObjectsFromLogString(logString)\n return objects.map((obj) => ({\n title: obj.graphics.title || \"Untitled Graphic\",\n svg: getSvgFromGraphicsObject(obj),\n }))\n}\n"],"mappings":"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","names":["e"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getHtmlFromLogString,
|
|
3
|
+
getSvgFromLogString,
|
|
4
|
+
getSvgsFromLogString
|
|
5
|
+
} from "../chunk-GDKEOGZB.js";
|
|
6
|
+
import {
|
|
7
|
+
getGraphicsObjectsFromLogString
|
|
8
|
+
} from "../chunk-646ISP7J.js";
|
|
9
|
+
import {
|
|
10
|
+
getSvgFromGraphicsObject
|
|
11
|
+
} from "../chunk-G7UBPMLZ.js";
|
|
12
|
+
export {
|
|
13
|
+
getGraphicsObjectsFromLogString,
|
|
14
|
+
getHtmlFromLogString,
|
|
15
|
+
getSvgFromGraphicsObject,
|
|
16
|
+
getSvgFromLogString,
|
|
17
|
+
getSvgsFromLogString
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "graphics-debug",
|
|
3
|
+
"main": "dist/lib/index.js",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"graphics-debug": "./dist/cli/cli.js",
|
|
11
|
+
"gd": "./dist/cli/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup-node ./cli ./lib --format esm --sourcemap",
|
|
15
|
+
"format": "biome format --write .",
|
|
16
|
+
"format:check": "biome format ."
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@biomejs/biome": "^1.9.4",
|
|
20
|
+
"@types/bun": "latest",
|
|
21
|
+
"@types/pretty": "^2.0.3",
|
|
22
|
+
"@types/react": "^18.3.12",
|
|
23
|
+
"@types/react-dom": "^18.3.1",
|
|
24
|
+
"bun-match-svg": "^0.0.8",
|
|
25
|
+
"commander": "^12.1.0",
|
|
26
|
+
"react": "^18.3.1",
|
|
27
|
+
"react-dom": "^18.3.1",
|
|
28
|
+
"tsup": "^8.3.5"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"pretty": "^2.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|