完成世界书、骰子、apiconfig页面处理

This commit is contained in:
2026-04-30 01:35:10 +08:00
parent a3e3711b2b
commit ba9b925c32
4602 changed files with 785225 additions and 23 deletions

72
frontend/node_modules/path-data-parser/README.md generated vendored Normal file
View File

@@ -0,0 +1,72 @@
# path-data-parser
I know there are a bunch of SVG path parsers out there, but here's one that I have been using for a few years now. It's small, tree-shakable and provides four simple functions that I have needed in several graphics projects.
## Install
From npm
```
npm install --save path-data-parser
```
The code is shipped as ES6 modules.
## Methods
The module exposes 4 methods
### pasrePath(d: string): Segment[]
This is the main method that parses the SVG path data. You pass in the path string and it returns an array of `Segments`. A segment has a `key` which is the commands, e.g. `M` or `h` or `C`; and `data` which is an array of numbers used by the command
```javascript
Segment {
key: string;
data: number[];
}
```
example:
```javascript
import { parsePath } from 'path-data-parser';
const segments = parsePath('M10 10 h 80 v 80 h -80 C Z');
```
### serialize(segments: Segment[]): string
This is essentially the opposite of the `parsePath` command. It outputs a path string from an array of `Segment` objects.
```javascript
import { parsePath, serialize, absolutize } from 'path-data-parser';
const segments = parsePath('M10 10 h 80 v 80 h -80 Z');
const absoluteSegments = absolutize(segments); // Turns relative commands to absolute
const outputPath = serialize(absoluteSegments);
console.log(outputPath); // M 10 10 H 90 V 90 H 10 Z
```
### absolutize(segments: Segment[]): Segment[]
Translates relative commands to absolute commands. i.e. all commands that use relative positions (lower-case ones), turns into absolute position commands (upper-case ones).
See the `serialize` example above.
### normalize(segments: Segment[]): Segment[]
Normalize takes a list of _absolute segments_ and outputs a list of segments with only four commands: `M, L, C, Z`. So every segment is described as move, line, or a bezier curve (cubic).
This is useful when translating SVG paths to non SVG mediums - Canvas, or some other graphics platform. Most such platforms will support lines and bezier curves. It also simplifies the cases to consider when modifying these segments.
```javascript
import { parsePath, serialize, absolutize, normalize } from 'path-data-parser';
const segments = parsePath(' M 10 80 Q 52.5 10, 95 80 T 180 80');
const absoluteSegments = absolutize(segments);
const normalizedSegments = normalize(absoluteSegments);
// M 10 80 C 38.33 33.33, 66.67 33.33, 95 80 C 123.33 126.67, 151.67 126.67, 180 80
```
## License
[MIT License](https://github.com/pshihn/path-data-parser/blob/master/LICENSE)

View File

@@ -0,0 +1,110 @@
// Translate relative commands to absolute commands
export function absolutize(segments) {
let cx = 0, cy = 0;
let subx = 0, suby = 0;
const out = [];
for (const { key, data } of segments) {
switch (key) {
case 'M':
out.push({ key: 'M', data: [...data] });
[cx, cy] = data;
[subx, suby] = data;
break;
case 'm':
cx += data[0];
cy += data[1];
out.push({ key: 'M', data: [cx, cy] });
subx = cx;
suby = cy;
break;
case 'L':
out.push({ key: 'L', data: [...data] });
[cx, cy] = data;
break;
case 'l':
cx += data[0];
cy += data[1];
out.push({ key: 'L', data: [cx, cy] });
break;
case 'C':
out.push({ key: 'C', data: [...data] });
cx = data[4];
cy = data[5];
break;
case 'c': {
const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx));
out.push({ key: 'C', data: newdata });
cx = newdata[4];
cy = newdata[5];
break;
}
case 'Q':
out.push({ key: 'Q', data: [...data] });
cx = data[2];
cy = data[3];
break;
case 'q': {
const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx));
out.push({ key: 'Q', data: newdata });
cx = newdata[2];
cy = newdata[3];
break;
}
case 'A':
out.push({ key: 'A', data: [...data] });
cx = data[5];
cy = data[6];
break;
case 'a':
cx += data[5];
cy += data[6];
out.push({ key: 'A', data: [data[0], data[1], data[2], data[3], data[4], cx, cy] });
break;
case 'H':
out.push({ key: 'H', data: [...data] });
cx = data[0];
break;
case 'h':
cx += data[0];
out.push({ key: 'H', data: [cx] });
break;
case 'V':
out.push({ key: 'V', data: [...data] });
cy = data[0];
break;
case 'v':
cy += data[0];
out.push({ key: 'V', data: [cy] });
break;
case 'S':
out.push({ key: 'S', data: [...data] });
cx = data[2];
cy = data[3];
break;
case 's': {
const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx));
out.push({ key: 'S', data: newdata });
cx = newdata[2];
cy = newdata[3];
break;
}
case 'T':
out.push({ key: 'T', data: [...data] });
cx = data[0];
cy = data[1];
break;
case 't':
cx += data[0];
cy += data[1];
out.push({ key: 'T', data: [cx, cy] });
break;
case 'Z':
case 'z':
out.push({ key: 'Z', data: [] });
cx = subx;
cy = suby;
break;
}
}
return out;
}

107
frontend/node_modules/path-data-parser/lib/parser.js generated vendored Normal file
View File

@@ -0,0 +1,107 @@
const COMMAND = 0;
const NUMBER = 1;
const EOD = 2;
const PARAMS = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 };
function tokenize(d) {
const tokens = new Array();
while (d !== '') {
if (d.match(/^([ \t\r\n,]+)/)) {
d = d.substr(RegExp.$1.length);
}
else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) {
tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 };
d = d.substr(RegExp.$1.length);
}
else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) {
tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` };
d = d.substr(RegExp.$1.length);
}
else {
return [];
}
}
tokens[tokens.length] = { type: EOD, text: '' };
return tokens;
}
function isType(token, type) {
return token.type === type;
}
export function parsePath(d) {
const segments = [];
const tokens = tokenize(d);
let mode = 'BOD';
let index = 0;
let token = tokens[index];
while (!isType(token, EOD)) {
let paramsCount = 0;
const params = [];
if (mode === 'BOD') {
if (token.text === 'M' || token.text === 'm') {
index++;
paramsCount = PARAMS[token.text];
mode = token.text;
}
else {
return parsePath('M0,0' + d);
}
}
else if (isType(token, NUMBER)) {
paramsCount = PARAMS[mode];
}
else {
index++;
paramsCount = PARAMS[token.text];
mode = token.text;
}
if ((index + paramsCount) < tokens.length) {
for (let i = index; i < index + paramsCount; i++) {
const numbeToken = tokens[i];
if (isType(numbeToken, NUMBER)) {
params[params.length] = +numbeToken.text;
}
else {
throw new Error('Param not a number: ' + mode + ',' + numbeToken.text);
}
}
if (typeof PARAMS[mode] === 'number') {
const segment = { key: mode, data: params };
segments.push(segment);
index += paramsCount;
token = tokens[index];
if (mode === 'M')
mode = 'L';
if (mode === 'm')
mode = 'l';
}
else {
throw new Error('Bad segment: ' + mode);
}
}
else {
throw new Error('Path data ended short');
}
}
return segments;
}
export function serialize(segments) {
const tokens = [];
for (const { key, data } of segments) {
tokens.push(key);
switch (key) {
case 'C':
case 'c':
tokens.push(data[0], `${data[1]},`, data[2], `${data[3]},`, data[4], data[5]);
break;
case 'S':
case 's':
case 'Q':
case 'q':
tokens.push(data[0], `${data[1]},`, data[2], data[3]);
break;
default:
tokens.push(...data);
break;
}
}
return tokens.join(' ');
}