@knitting-tools/core 2.0.0 → 2.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 +42 -0
- package/dist/index.d.ts +68 -100
- package/dist/index.js +288 -212
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -9,12 +9,54 @@ A knitting pattern engine for expanding rows, applying shaping, and compiling pa
|
|
|
9
9
|
- Apply physical shaping to stitch streams
|
|
10
10
|
- Compile simple knitting DSL
|
|
11
11
|
|
|
12
|
+
## Package boundaries
|
|
13
|
+
|
|
14
|
+
`@knitting-tools/core` is the pattern engine package.
|
|
15
|
+
|
|
16
|
+
- Keep here: parsing, shaping, row expansion, and compile pipeline.
|
|
17
|
+
- Use `@knitting-tools/math` for gauge and measurement utility functions.
|
|
18
|
+
|
|
12
19
|
## Install
|
|
13
20
|
|
|
14
21
|
```bash
|
|
15
22
|
pnpm add @knitting-tools/core
|
|
16
23
|
```
|
|
17
24
|
|
|
25
|
+
## Migration from @knitting-tools/math
|
|
26
|
+
|
|
27
|
+
The package was renamed in `2.0.0`:
|
|
28
|
+
|
|
29
|
+
- Old package: `@knitting-tools/math`
|
|
30
|
+
- New package: `@knitting-tools/core`
|
|
31
|
+
|
|
32
|
+
Update your project by changing both install and import paths.
|
|
33
|
+
|
|
34
|
+
Before:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { compilePattern } from "@knitting-tools/math";
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { compilePattern } from "@knitting-tools/core";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The runtime API surface is unchanged for this rename; migration is package-path only.
|
|
47
|
+
|
|
48
|
+
For split-package migration, prefer these utility imports from `@knitting-tools/math`:
|
|
49
|
+
|
|
50
|
+
- `rowsPerCm`
|
|
51
|
+
- `stitchesPerCm`
|
|
52
|
+
- `cmToRows`
|
|
53
|
+
- `cmToStitches`
|
|
54
|
+
- `convertLength`
|
|
55
|
+
- `normaliseMeasurement`
|
|
56
|
+
- `roundStitches`
|
|
57
|
+
|
|
58
|
+
These remain re-exported from `@knitting-tools/core` for compatibility.
|
|
59
|
+
|
|
18
60
|
## Quick example
|
|
19
61
|
|
|
20
62
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,78 +1,13 @@
|
|
|
1
|
+
import { Gauge } from '@knitting-tools/math';
|
|
2
|
+
export { Gauge, ImperialGauge, MetricGauge, UnitSystem, cmToRows, cmToStitches, convertLength, normaliseMeasurement, roundStitches, rowsPerCm, stitchesPerCm } from '@knitting-tools/math';
|
|
3
|
+
|
|
1
4
|
declare const INCH_TO_CM: number;
|
|
2
5
|
declare const CM_TO_INCH: number;
|
|
3
6
|
declare const MM_TO_CM: number;
|
|
4
7
|
declare const CM_TO_MM: number;
|
|
5
8
|
|
|
6
|
-
type UnitSystem = "imperial" | "metric";
|
|
7
|
-
interface MetricGauge {
|
|
8
|
-
rowsPer10cm: number;
|
|
9
|
-
stitchesPer10cm: number;
|
|
10
|
-
unit: "metric";
|
|
11
|
-
}
|
|
12
|
-
interface ImperialGauge {
|
|
13
|
-
rowsPer4in: number;
|
|
14
|
-
stitchesPer4in: number;
|
|
15
|
-
unit: "imperial";
|
|
16
|
-
}
|
|
17
|
-
type Gauge = MetricGauge | ImperialGauge;
|
|
18
|
-
/**
|
|
19
|
-
* Get the number of stitches per centimeter based on the gauge.
|
|
20
|
-
*
|
|
21
|
-
* @param gauge - The gauge information.
|
|
22
|
-
* @returns The number of stitches per centimeter.
|
|
23
|
-
*/
|
|
24
|
-
declare function stitchesPerCm(gauge: Gauge): number;
|
|
25
|
-
/**
|
|
26
|
-
* Get the number of rows per centimeter based on the gauge.
|
|
27
|
-
*
|
|
28
|
-
* @param gauge - The gauge information.
|
|
29
|
-
* @returns The number of rows per centimeter.
|
|
30
|
-
*/
|
|
31
|
-
declare function rowsPerCm(gauge: Gauge): number;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Convert a centimetre measurement into a rounded stitch count for the given gauge.
|
|
35
|
-
*
|
|
36
|
-
* @param cm - The measurement in centimetres.
|
|
37
|
-
* @param gauge - The gauge definition used for conversion.
|
|
38
|
-
* @returns The nearest whole-number stitch count.
|
|
39
|
-
*/
|
|
40
|
-
declare function cmToStitches(cm: number, gauge: Gauge): number;
|
|
41
|
-
/**
|
|
42
|
-
* Convert a centimetre measurement into a rounded row count for the given gauge.
|
|
43
|
-
*
|
|
44
|
-
* @param cm - The measurement in centimetres.
|
|
45
|
-
* @param gauge - The gauge definition used for conversion.
|
|
46
|
-
* @returns The nearest whole-number row count.
|
|
47
|
-
*/
|
|
48
|
-
declare function cmToRows(cm: number, gauge: Gauge): number;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Convert a length value between centimeters and inches.
|
|
52
|
-
*
|
|
53
|
-
* @param value - The length value to convert.
|
|
54
|
-
* @param from - The unit of the input value ("cm" or "in").
|
|
55
|
-
* @param to - The unit to convert to ("cm" or "in").
|
|
56
|
-
* @returns The converted length value.
|
|
57
|
-
*/
|
|
58
|
-
declare function convertLength(value: number, from: "cm" | "in", to: "cm" | "in"): number;
|
|
59
|
-
/**
|
|
60
|
-
* Normalise a length measurement to centimeters.
|
|
61
|
-
*
|
|
62
|
-
* @param value - The length value to normalise.
|
|
63
|
-
* @param unit - The unit of the input value ("cm" or "in").
|
|
64
|
-
* @returns The normalised length value in centimeters.
|
|
65
|
-
*/
|
|
66
|
-
declare function normaliseMeasurement(value: number, unit: "cm" | "in"): number;
|
|
67
|
-
/**
|
|
68
|
-
* Round a number of stitches to the nearest whole number.
|
|
69
|
-
*
|
|
70
|
-
* @param stitches - The number of stitches to round.
|
|
71
|
-
* @returns The rounded number of stitches.
|
|
72
|
-
*/
|
|
73
|
-
declare function roundStitches(stitches: number): number;
|
|
74
|
-
|
|
75
9
|
type StitchType = "knit" | "purl" | "decrease" | "increase";
|
|
10
|
+
type OperationType = "decrease" | "increase";
|
|
76
11
|
type StitchVariant = "inv-l" | "inv-r" | "k1-b" | "k2tog" | "kfb" | "kfbf" | "m1" | "p1-b" | "p2tog" | "pfb" | "psso" | "s2kp" | "sk2p" | "skp" | "sl" | "sl1" | "slip" | "ssp" | "ssk" | "tbl" | "yfon" | "yfrn" | "yo" | "yon" | "yrn";
|
|
77
12
|
type StitchVariantParameterValue = boolean | number | string;
|
|
78
13
|
type StitchVariantParameters = Record<string, StitchVariantParameterValue>;
|
|
@@ -94,6 +29,7 @@ type EdgeElement = ShapingElement | StitchInstruction;
|
|
|
94
29
|
type RowElement = ShapingElement | StitchInstruction | RepeatBlock;
|
|
95
30
|
interface StitchInstruction {
|
|
96
31
|
count: number;
|
|
32
|
+
operation?: OperationType;
|
|
97
33
|
type: StitchType;
|
|
98
34
|
variant?: StitchVariant;
|
|
99
35
|
variantParameters?: StitchVariantParameters;
|
|
@@ -338,6 +274,65 @@ declare function createDefaultStitchRegistry(): StitchRegistry;
|
|
|
338
274
|
*/
|
|
339
275
|
declare function createEmptyStitchRegistry(): StitchRegistry;
|
|
340
276
|
|
|
277
|
+
type ShapingType = "decrease" | "increase";
|
|
278
|
+
|
|
279
|
+
interface ShapeRowInput {
|
|
280
|
+
alignment?: "left" | "center" | "right";
|
|
281
|
+
row: Row;
|
|
282
|
+
shaping: {
|
|
283
|
+
type: ShapingType;
|
|
284
|
+
count: number;
|
|
285
|
+
};
|
|
286
|
+
totalStitches: number;
|
|
287
|
+
}
|
|
288
|
+
interface ShapedStitch {
|
|
289
|
+
isShaping?: boolean;
|
|
290
|
+
operationIndex?: number;
|
|
291
|
+
shapingType?: ShapingType;
|
|
292
|
+
sourceIndex: number;
|
|
293
|
+
type: StitchType;
|
|
294
|
+
}
|
|
295
|
+
interface ShapeRowResult {
|
|
296
|
+
shapedStitches: ShapedStitch[];
|
|
297
|
+
stitches: StitchInstruction[];
|
|
298
|
+
totalStitches: number;
|
|
299
|
+
}
|
|
300
|
+
interface ShapingOperationGroup {
|
|
301
|
+
operationIndex: number;
|
|
302
|
+
outputIndices: number[];
|
|
303
|
+
shapingType: ShapingType;
|
|
304
|
+
sourceIndices: number[];
|
|
305
|
+
stitches: ShapedStitch[];
|
|
306
|
+
}
|
|
307
|
+
interface CompileRowInput {
|
|
308
|
+
alignment?: "left" | "center" | "right";
|
|
309
|
+
row: Row;
|
|
310
|
+
shaping?: {
|
|
311
|
+
count: number;
|
|
312
|
+
type: ShapingType;
|
|
313
|
+
};
|
|
314
|
+
totalStitches: number;
|
|
315
|
+
}
|
|
316
|
+
interface CompileRowResult {
|
|
317
|
+
shapedStitches?: ShapedStitch[];
|
|
318
|
+
stitches: StitchInstruction[];
|
|
319
|
+
totalStitches: number;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Options object for the shaping-aware overload of compileRowDsl /
|
|
324
|
+
* compileCompatibleRowDsl. Pass this instead of a plain registry when you
|
|
325
|
+
* want DSL parsing, expansion, and shaping in a single call.
|
|
326
|
+
*/
|
|
327
|
+
interface CompileRowDslOptions {
|
|
328
|
+
alignment?: "left" | "center" | "right";
|
|
329
|
+
registry?: StitchRegistry;
|
|
330
|
+
shaping?: {
|
|
331
|
+
count: number;
|
|
332
|
+
type: ShapingType;
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
341
336
|
interface RowDslAstEdgeStartElement {
|
|
342
337
|
stitches: EdgeElement[];
|
|
343
338
|
type: "edge-start";
|
|
@@ -366,42 +361,14 @@ declare function isRowDslAstWellFormed(ast: RowDslAst): boolean;
|
|
|
366
361
|
declare function parseRowDsl(input: string, registry?: StitchRegistry): Row;
|
|
367
362
|
declare function parseCompatibleRowDsl(input: string, registry?: StitchRegistry): Row;
|
|
368
363
|
declare function compileRowDsl(input: string, totalStitches: number, registry?: StitchRegistry): StitchInstruction[];
|
|
364
|
+
declare function compileRowDsl(input: string, totalStitches: number, options: CompileRowDslOptions): CompileRowResult;
|
|
369
365
|
declare function compileCompatibleRowDsl(input: string, totalStitches: number, registry?: StitchRegistry): StitchInstruction[];
|
|
366
|
+
declare function compileCompatibleRowDsl(input: string, totalStitches: number, options: CompileRowDslOptions): CompileRowResult;
|
|
370
367
|
declare function rowToRowDsl(row: Row): string;
|
|
371
368
|
declare function parseRowDslAst(input: string, registry?: StitchRegistry): RowDslAst;
|
|
372
369
|
declare function parseCompatibleRowDslAst(input: string, registry?: StitchRegistry): RowDslAst;
|
|
373
370
|
declare function resolveRowDslAst(ast: RowDslAst): Row;
|
|
374
371
|
|
|
375
|
-
type ShapingType = "decrease" | "increase";
|
|
376
|
-
|
|
377
|
-
interface ShapeRowInput {
|
|
378
|
-
row: Row;
|
|
379
|
-
shaping: {
|
|
380
|
-
type: ShapingType;
|
|
381
|
-
count: number;
|
|
382
|
-
};
|
|
383
|
-
totalStitches: number;
|
|
384
|
-
}
|
|
385
|
-
interface ShapedStitch {
|
|
386
|
-
isShaping?: boolean;
|
|
387
|
-
operationIndex?: number;
|
|
388
|
-
shapingType?: ShapingType;
|
|
389
|
-
sourceIndex: number;
|
|
390
|
-
type: StitchType;
|
|
391
|
-
}
|
|
392
|
-
interface ShapeRowResult {
|
|
393
|
-
shapedStitches: ShapedStitch[];
|
|
394
|
-
stitches: StitchInstruction[];
|
|
395
|
-
totalStitches: number;
|
|
396
|
-
}
|
|
397
|
-
interface ShapingOperationGroup {
|
|
398
|
-
operationIndex: number;
|
|
399
|
-
outputIndices: number[];
|
|
400
|
-
shapingType: ShapingType;
|
|
401
|
-
sourceIndices: number[];
|
|
402
|
-
stitches: ShapedStitch[];
|
|
403
|
-
}
|
|
404
|
-
|
|
405
372
|
type ShapingDslParseResult = ShapeRowInput;
|
|
406
373
|
type ShapingDslElement = {
|
|
407
374
|
row: Row;
|
|
@@ -474,7 +441,8 @@ declare function printShapingDebug(shapedStitches: ShapedStitch[], label?: strin
|
|
|
474
441
|
declare function parseShapingDslAst(input: string): ShapingDslAst;
|
|
475
442
|
declare function parseShapingDsl(input: string): ShapingDslParseResult;
|
|
476
443
|
declare function compileShapingDsl(input: string): ShapeRowResult;
|
|
444
|
+
declare function compileRow(input: CompileRowInput): CompileRowResult;
|
|
477
445
|
declare function shapeRow(input: ShapeRowInput): ShapeRowResult;
|
|
478
446
|
declare function groupShapedStitchesByOperation(shapedStitches: ShapedStitch[]): ShapingOperationGroup[];
|
|
479
447
|
|
|
480
|
-
export { CM_TO_INCH, CM_TO_MM, type CastOnInput, type CompilePatternOptions, type
|
|
448
|
+
export { CM_TO_INCH, CM_TO_MM, type CastOnInput, type CompilePatternOptions, type CompileRowDslOptions, type CompileRowInput, type CompileRowResult, type CompiledPattern, type CompiledPatternRow, type EdgeElement, INCH_TO_CM, KnittingMathError, type KnittingMathErrorCode, type KnittingMathErrorContext, KnittingMathExpansionError, KnittingMathParseError, KnittingMathValidationError, MM_TO_CM, type Pattern, type PatternCompileMode, type RepeatBlock, type ResolveOptions, type Row, type RowDslAst, type RowDslAstElement, type RowElement, type RowShapingOperation, type ShapeRowInput, type ShapeRowResult, type ShapedRowResult, type ShapedStitch, type ShapingDebugLine, type ShapingDebugOptions, type ShapingDslAst, type ShapingDslElement, type ShapingDslParseResult, type ShapingElement, type ShapingOperationGroup, type StitchInstruction, type StitchRegistry, type StitchType, type StitchVariant, type StitchVariantParameterValue, type StitchVariantParameters, calculateCastOn, calculateCastOnStitches, compileCompatibleRowDsl, compilePattern, compileRow, compileRowDsl, compileShapingDsl, countStitchesInInstructions, createDefaultStitchRegistry, createEmptyStitchRegistry, createRepeatRow, ensureKnittingMathError, expandPattern, expandRow, expandRowToStitchTypes, expandRowToStitches, groupShapedStitchesByOperation, isRowDslAstWellFormed, parseCompatibleRowDsl, parseCompatibleRowDslAst, parseRowDsl, parseRowDslAst, parseShapingDsl, parseShapingDslAst, printPattern, printShapingDebug, renderShapingDebug, renderShapingDebugForInput, resolveRowDslAst, rowToRowDsl, shapeRow, shapedExpandPattern };
|
package/dist/index.js
CHANGED
|
@@ -4,58 +4,26 @@ var CM_TO_INCH = 1 / INCH_TO_CM;
|
|
|
4
4
|
var MM_TO_CM = 0.1;
|
|
5
5
|
var CM_TO_MM = 10;
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
return unitToCm(gauge.rowsPer4in, "imperial");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// src/gauge/conversion.ts
|
|
26
|
-
function cmToStitches(cm, gauge) {
|
|
27
|
-
return Math.round(stitchesPerCm(gauge) * cm);
|
|
28
|
-
}
|
|
29
|
-
function cmToRows(cm, gauge) {
|
|
30
|
-
return Math.round(rowsPerCm(gauge) * cm);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// src/measurement/measurement.ts
|
|
34
|
-
function convertLength(value, from, to) {
|
|
35
|
-
if (from === to) {
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
if (from === "cm" && to === "in") {
|
|
39
|
-
return value * CM_TO_INCH;
|
|
40
|
-
}
|
|
41
|
-
if (from === "in" && to === "cm") {
|
|
42
|
-
return value * INCH_TO_CM;
|
|
43
|
-
}
|
|
44
|
-
throw new Error(`Unsupported conversion from ${from} \u2192 ${to}`);
|
|
45
|
-
}
|
|
46
|
-
function normaliseMeasurement(value, unit) {
|
|
47
|
-
return convertLength(value, unit, "cm");
|
|
48
|
-
}
|
|
49
|
-
function roundStitches(stitches) {
|
|
50
|
-
if (!Number.isFinite(stitches)) {
|
|
51
|
-
throw new Error("Stitch count must be a finite number.");
|
|
52
|
-
}
|
|
53
|
-
return Math.round(stitches);
|
|
54
|
-
}
|
|
7
|
+
// src/public/index.ts
|
|
8
|
+
import {
|
|
9
|
+
CM_TO_INCH as CM_TO_INCH2,
|
|
10
|
+
CM_TO_MM as CM_TO_MM2,
|
|
11
|
+
INCH_TO_CM as INCH_TO_CM2,
|
|
12
|
+
MM_TO_CM as MM_TO_CM2,
|
|
13
|
+
cmToRows,
|
|
14
|
+
cmToStitches,
|
|
15
|
+
convertLength,
|
|
16
|
+
normaliseMeasurement as normaliseMeasurement2,
|
|
17
|
+
roundStitches as roundStitches2,
|
|
18
|
+
rowsPerCm,
|
|
19
|
+
stitchesPerCm as stitchesPerCm2
|
|
20
|
+
} from "@knitting-tools/math";
|
|
55
21
|
|
|
56
22
|
// src/pattern/row.ts
|
|
57
23
|
var InstructionBuffer = class {
|
|
58
|
-
|
|
24
|
+
constructor() {
|
|
25
|
+
this.items = [];
|
|
26
|
+
}
|
|
59
27
|
append(instruction, count = instruction.count) {
|
|
60
28
|
this.items[this.items.length] = cloneInstruction(instruction, count);
|
|
61
29
|
}
|
|
@@ -71,6 +39,15 @@ var SHAPING_PRODUCE = {
|
|
|
71
39
|
decrease: 1,
|
|
72
40
|
increase: 2
|
|
73
41
|
};
|
|
42
|
+
function getInstructionOperation(instruction) {
|
|
43
|
+
if (instruction.operation !== void 0) {
|
|
44
|
+
return instruction.operation;
|
|
45
|
+
}
|
|
46
|
+
if (instruction.type === "decrease" || instruction.type === "increase") {
|
|
47
|
+
return instruction.type;
|
|
48
|
+
}
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
74
51
|
function isRecord(value) {
|
|
75
52
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
53
|
}
|
|
@@ -87,10 +64,10 @@ function isStitchInstruction(element) {
|
|
|
87
64
|
return !isRepeatBlock(element) && !isShapingElement(element);
|
|
88
65
|
}
|
|
89
66
|
function countEdgeSourceStitches(stitches) {
|
|
90
|
-
return
|
|
67
|
+
return stitches?.reduce(
|
|
91
68
|
(sum, stitch) => sum + (isEdgeShapingElement(stitch) ? stitch.count * SHAPING_CONSUME[stitch.shapingType] : stitch.count),
|
|
92
69
|
0
|
|
93
|
-
)
|
|
70
|
+
) ?? 0;
|
|
94
71
|
}
|
|
95
72
|
function countSequenceStitches(elements) {
|
|
96
73
|
let total = 0;
|
|
@@ -119,12 +96,21 @@ function cloneVariantParameters(variantParameters) {
|
|
|
119
96
|
return variantParameters ? { ...variantParameters } : void 0;
|
|
120
97
|
}
|
|
121
98
|
function cloneInstruction(instruction, count = instruction.count) {
|
|
122
|
-
|
|
99
|
+
const clone = {
|
|
123
100
|
count,
|
|
124
|
-
type: instruction.type
|
|
125
|
-
variant: instruction.variant,
|
|
126
|
-
variantParameters: cloneVariantParameters(instruction.variantParameters)
|
|
101
|
+
type: instruction.type
|
|
127
102
|
};
|
|
103
|
+
if (instruction.operation !== void 0) {
|
|
104
|
+
clone.operation = instruction.operation;
|
|
105
|
+
}
|
|
106
|
+
if (instruction.variant !== void 0) {
|
|
107
|
+
clone.variant = instruction.variant;
|
|
108
|
+
}
|
|
109
|
+
const clonedVariantParameters = cloneVariantParameters(instruction.variantParameters);
|
|
110
|
+
if (clonedVariantParameters !== void 0) {
|
|
111
|
+
clone.variantParameters = clonedVariantParameters;
|
|
112
|
+
}
|
|
113
|
+
return clone;
|
|
128
114
|
}
|
|
129
115
|
function inferDecreaseVariant(element) {
|
|
130
116
|
const isPurl = element.stitch === "purl";
|
|
@@ -155,12 +141,18 @@ function inferShapingVariant(element) {
|
|
|
155
141
|
}
|
|
156
142
|
function createInstructionFromShaping(element, operationCount) {
|
|
157
143
|
const inferredVariant = element.variant ?? inferShapingVariant(element);
|
|
158
|
-
|
|
144
|
+
const instruction = {
|
|
159
145
|
count: operationCount * SHAPING_PRODUCE[element.shapingType],
|
|
160
|
-
type: element.shapingType
|
|
161
|
-
variant: inferredVariant,
|
|
162
|
-
variantParameters: cloneVariantParameters(element.variantParameters)
|
|
146
|
+
type: element.shapingType
|
|
163
147
|
};
|
|
148
|
+
if (inferredVariant !== void 0) {
|
|
149
|
+
instruction.variant = inferredVariant;
|
|
150
|
+
}
|
|
151
|
+
const clonedVariantParameters = cloneVariantParameters(element.variantParameters);
|
|
152
|
+
if (clonedVariantParameters !== void 0) {
|
|
153
|
+
instruction.variantParameters = clonedVariantParameters;
|
|
154
|
+
}
|
|
155
|
+
return instruction;
|
|
164
156
|
}
|
|
165
157
|
function areVariantParametersEqual(left, right) {
|
|
166
158
|
if (left === right) {
|
|
@@ -182,7 +174,7 @@ function areVariantParametersEqual(left, right) {
|
|
|
182
174
|
return true;
|
|
183
175
|
}
|
|
184
176
|
function canMergeInstructions(left, right) {
|
|
185
|
-
return left.type === right.type && left.variant === right.variant && areVariantParametersEqual(left.variantParameters, right.variantParameters);
|
|
177
|
+
return left.operation === right.operation && left.type === right.type && left.variant === right.variant && areVariantParametersEqual(left.variantParameters, right.variantParameters);
|
|
186
178
|
}
|
|
187
179
|
function assertPositiveInteger(value, label) {
|
|
188
180
|
if (!Number.isInteger(value) || value <= 0) {
|
|
@@ -214,6 +206,9 @@ function validateStitchInstruction(instruction, path) {
|
|
|
214
206
|
if (!isRecord(instruction)) {
|
|
215
207
|
throw new Error(`${path} must be an object.`);
|
|
216
208
|
}
|
|
209
|
+
if (instruction.operation !== void 0 && instruction.operation !== "decrease" && instruction.operation !== "increase") {
|
|
210
|
+
throw new Error(`${path}.operation must be 'decrease' or 'increase' when provided.`);
|
|
211
|
+
}
|
|
217
212
|
assertPositiveInteger(instruction.count, `${path}.count`);
|
|
218
213
|
validateVariantParameters(instruction.variantParameters, `${path}.variantParameters`);
|
|
219
214
|
}
|
|
@@ -861,7 +856,6 @@ function collectRowShaping(row) {
|
|
|
861
856
|
return ops;
|
|
862
857
|
}
|
|
863
858
|
function compilePattern(pattern, initialStitches, options = {}) {
|
|
864
|
-
var _a;
|
|
865
859
|
const mode = options.mode ?? "shaped";
|
|
866
860
|
const rows = [];
|
|
867
861
|
let currentStitches = initialStitches;
|
|
@@ -893,7 +887,7 @@ function compilePattern(pattern, initialStitches, options = {}) {
|
|
|
893
887
|
}
|
|
894
888
|
}
|
|
895
889
|
return {
|
|
896
|
-
finalStitches:
|
|
890
|
+
finalStitches: rows.at(-1)?.carriedStitches ?? initialStitches,
|
|
897
891
|
initialStitches,
|
|
898
892
|
mode,
|
|
899
893
|
rows
|
|
@@ -917,10 +911,11 @@ function printPattern(pattern, stitchesPerRow) {
|
|
|
917
911
|
const expanded = expandPattern(pattern, stitchesPerRow);
|
|
918
912
|
expanded.forEach((row, rowIndex) => {
|
|
919
913
|
const rowStr = row.map((stitch) => {
|
|
920
|
-
const
|
|
914
|
+
const operation = getInstructionOperation(stitch);
|
|
915
|
+
const symbol = operation ? stitchSymbols[operation] : stitchSymbols[stitch.type] ?? "?";
|
|
921
916
|
return symbol.repeat(stitch.count);
|
|
922
917
|
}).join(" ");
|
|
923
|
-
patternDebugLogger
|
|
918
|
+
patternDebugLogger?.log(`Row ${rowIndex + 1}: ${rowStr}`);
|
|
924
919
|
});
|
|
925
920
|
}
|
|
926
921
|
function createRepeatRow(sequence, times) {
|
|
@@ -937,8 +932,6 @@ function createRepeatRow(sequence, times) {
|
|
|
937
932
|
|
|
938
933
|
// src/public/errors.ts
|
|
939
934
|
var KnittingMathError = class extends Error {
|
|
940
|
-
code;
|
|
941
|
-
context;
|
|
942
935
|
constructor(name, code, message, context, cause) {
|
|
943
936
|
super(message);
|
|
944
937
|
this.name = name;
|
|
@@ -1019,6 +1012,11 @@ function printPattern2(pattern, stitchesPerRow) {
|
|
|
1019
1012
|
}
|
|
1020
1013
|
|
|
1021
1014
|
// src/stitches/castOn.ts
|
|
1015
|
+
import {
|
|
1016
|
+
normaliseMeasurement,
|
|
1017
|
+
roundStitches,
|
|
1018
|
+
stitchesPerCm
|
|
1019
|
+
} from "@knitting-tools/math";
|
|
1022
1020
|
function calculateCastOn(input) {
|
|
1023
1021
|
return calculateCastOnStitches({ ...input, ease: 0 });
|
|
1024
1022
|
}
|
|
@@ -1034,6 +1032,163 @@ function calculateCastOnStitches(input) {
|
|
|
1034
1032
|
return roundStitches(stitches);
|
|
1035
1033
|
}
|
|
1036
1034
|
|
|
1035
|
+
// src/shaping/distribution.ts
|
|
1036
|
+
function distributeEvenly(totalStitches, operations) {
|
|
1037
|
+
if (operations <= 0) {
|
|
1038
|
+
return { spacing: [] };
|
|
1039
|
+
}
|
|
1040
|
+
const baseSpacing = Math.floor(totalStitches / operations);
|
|
1041
|
+
const remainder = totalStitches % operations;
|
|
1042
|
+
const spacing = [];
|
|
1043
|
+
for (let i = 0; i < operations; i++) {
|
|
1044
|
+
const shouldAddExtra = Math.floor(i * remainder / operations) !== Math.floor((i + 1) * remainder / operations);
|
|
1045
|
+
spacing.push(baseSpacing + (shouldAddExtra ? 1 : 0));
|
|
1046
|
+
}
|
|
1047
|
+
return { spacing };
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// src/shaping/shapingRules.ts
|
|
1051
|
+
var SHAPING_RULES = {
|
|
1052
|
+
decrease: { consume: 2, produce: 1 },
|
|
1053
|
+
// k2tog - A decrease consumes 2 stitches and produces 1.
|
|
1054
|
+
increase: { consume: 1, produce: 2 }
|
|
1055
|
+
// M1 - An increase consumes 1 stitch and produces 2 stitches.
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
// src/shaping/shaping.ts
|
|
1059
|
+
function getShapingPositions(length, count, alignment = "center") {
|
|
1060
|
+
if (count <= 0 || length <= 0) {
|
|
1061
|
+
return [];
|
|
1062
|
+
}
|
|
1063
|
+
const { spacing } = distributeEvenly(length, count);
|
|
1064
|
+
const positions = [];
|
|
1065
|
+
let current = 0;
|
|
1066
|
+
for (const gap of spacing) {
|
|
1067
|
+
let position;
|
|
1068
|
+
switch (alignment) {
|
|
1069
|
+
case "left":
|
|
1070
|
+
position = current;
|
|
1071
|
+
break;
|
|
1072
|
+
case "center":
|
|
1073
|
+
position = current + Math.floor(gap / 2);
|
|
1074
|
+
break;
|
|
1075
|
+
case "right":
|
|
1076
|
+
position = current + gap - 1;
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
positions.push(position);
|
|
1080
|
+
current += gap;
|
|
1081
|
+
}
|
|
1082
|
+
return positions.filter((position) => position >= 0 && position < length);
|
|
1083
|
+
}
|
|
1084
|
+
function applyPhysicalShaping(stitches, shapingType, positions) {
|
|
1085
|
+
const rule = SHAPING_RULES[shapingType];
|
|
1086
|
+
const result = [];
|
|
1087
|
+
let i = 0;
|
|
1088
|
+
let positionIndex = 0;
|
|
1089
|
+
while (i < stitches.length) {
|
|
1090
|
+
if (positionIndex < positions.length && i === positions[positionIndex]) {
|
|
1091
|
+
if (i + rule.consume > stitches.length) {
|
|
1092
|
+
throw new Error("Shaping operation exceeds available stitches at the selected position.");
|
|
1093
|
+
}
|
|
1094
|
+
for (let j = 0; j < rule.produce; j++) {
|
|
1095
|
+
result.push({
|
|
1096
|
+
isShaping: true,
|
|
1097
|
+
operationIndex: positionIndex,
|
|
1098
|
+
shapingType,
|
|
1099
|
+
sourceIndex: i,
|
|
1100
|
+
type: shapingType
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
i += rule.consume;
|
|
1104
|
+
positionIndex++;
|
|
1105
|
+
} else {
|
|
1106
|
+
result.push(stitches[i]);
|
|
1107
|
+
i++;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return result;
|
|
1111
|
+
}
|
|
1112
|
+
function compressStitches(stitches) {
|
|
1113
|
+
const result = [];
|
|
1114
|
+
for (const stitch of stitches) {
|
|
1115
|
+
const last = result.at(-1);
|
|
1116
|
+
if (last?.type === stitch.type) {
|
|
1117
|
+
last.count++;
|
|
1118
|
+
} else {
|
|
1119
|
+
result.push({ type: stitch.type, count: 1 });
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return result;
|
|
1123
|
+
}
|
|
1124
|
+
function shapeRow(input) {
|
|
1125
|
+
const { row, totalStitches, shaping } = input;
|
|
1126
|
+
if (shaping.count < 0) {
|
|
1127
|
+
throw new Error("Shaping count must be 0 or greater.");
|
|
1128
|
+
}
|
|
1129
|
+
const expanded = expandRowToStitchTypes(row, totalStitches).map((type, sourceIndex) => ({
|
|
1130
|
+
sourceIndex,
|
|
1131
|
+
type
|
|
1132
|
+
}));
|
|
1133
|
+
const rule = SHAPING_RULES[shaping.type];
|
|
1134
|
+
if (shaping.count * rule.consume > expanded.length) {
|
|
1135
|
+
throw new Error("Cannot apply more shaping operations than stitches in the row.");
|
|
1136
|
+
}
|
|
1137
|
+
const shapingPositions = getShapingPositions(
|
|
1138
|
+
expanded.length,
|
|
1139
|
+
shaping.count,
|
|
1140
|
+
input.alignment ?? "center"
|
|
1141
|
+
);
|
|
1142
|
+
const shapedStitches = applyPhysicalShaping(expanded, shaping.type, shapingPositions);
|
|
1143
|
+
const stitches = compressStitches(shapedStitches);
|
|
1144
|
+
const resultingStitches = totalStitches + shaping.count * (rule.produce - rule.consume);
|
|
1145
|
+
return { shapedStitches, stitches, totalStitches: resultingStitches };
|
|
1146
|
+
}
|
|
1147
|
+
function groupShapedStitchesByOperation(shapedStitches) {
|
|
1148
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1149
|
+
for (let outputIndex = 0; outputIndex < shapedStitches.length; outputIndex += 1) {
|
|
1150
|
+
const stitch = shapedStitches[outputIndex];
|
|
1151
|
+
if (!stitch.isShaping || stitch.operationIndex === void 0 || stitch.shapingType === void 0) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const existing = groups.get(stitch.operationIndex);
|
|
1155
|
+
if (existing) {
|
|
1156
|
+
if (existing.shapingType !== stitch.shapingType) {
|
|
1157
|
+
throw new Error("Shaping operation contains mixed shaping types.");
|
|
1158
|
+
}
|
|
1159
|
+
existing.stitches.push(stitch);
|
|
1160
|
+
existing.outputIndices.push(outputIndex);
|
|
1161
|
+
existing.sourceIndices.push(stitch.sourceIndex);
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
groups.set(stitch.operationIndex, {
|
|
1165
|
+
operationIndex: stitch.operationIndex,
|
|
1166
|
+
outputIndices: [outputIndex],
|
|
1167
|
+
shapingType: stitch.shapingType,
|
|
1168
|
+
sourceIndices: [stitch.sourceIndex],
|
|
1169
|
+
stitches: [stitch]
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return Array.from(groups.values());
|
|
1173
|
+
}
|
|
1174
|
+
function compileRow(input) {
|
|
1175
|
+
const { row, totalStitches, shaping, alignment } = input;
|
|
1176
|
+
if (!shaping || shaping.count === 0) {
|
|
1177
|
+
return { stitches: expandRow(row, totalStitches), totalStitches };
|
|
1178
|
+
}
|
|
1179
|
+
const {
|
|
1180
|
+
shapedStitches,
|
|
1181
|
+
stitches,
|
|
1182
|
+
totalStitches: resultingStitches
|
|
1183
|
+
} = shapeRow({
|
|
1184
|
+
alignment,
|
|
1185
|
+
row,
|
|
1186
|
+
shaping,
|
|
1187
|
+
totalStitches
|
|
1188
|
+
});
|
|
1189
|
+
return { shapedStitches, stitches, totalStitches: resultingStitches };
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1037
1192
|
// src/parser/compatibilityNormaliser.ts
|
|
1038
1193
|
function isWordBoundaryCharacter(char) {
|
|
1039
1194
|
return char === void 0 || /[^a-z]/i.test(char);
|
|
@@ -1346,10 +1501,12 @@ var ERR_DEFAULT_COUNT_INVALID = "Default count must be a positive integer.";
|
|
|
1346
1501
|
var ERR_RESOLVE_OPTIONS_INVALID = "Resolve options must be an object when provided.";
|
|
1347
1502
|
var ERR_RESOLVE_THROW_ON_UNKNOWN_INVALID = "Resolve option 'throwOnUnknown' must be a boolean when provided.";
|
|
1348
1503
|
var InMemoryStitchRegistry = class {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1504
|
+
constructor() {
|
|
1505
|
+
this.typeMap = /* @__PURE__ */ new Map();
|
|
1506
|
+
this.abbreviationMap = /* @__PURE__ */ new Map();
|
|
1507
|
+
this.defaultCounts = /* @__PURE__ */ new Map();
|
|
1508
|
+
this.lockedCountVariants = /* @__PURE__ */ new Set();
|
|
1509
|
+
}
|
|
1353
1510
|
/**
|
|
1354
1511
|
* Normalise a required token for storage.
|
|
1355
1512
|
*
|
|
@@ -1524,10 +1681,8 @@ function createEmptyStitchRegistry() {
|
|
|
1524
1681
|
var SHAPING_TOKEN_RE = /^shape-(dec|decrease|inc|increase)(?:-(l|left|r|right|c|centre|center))?(\d+)$/;
|
|
1525
1682
|
var COMPACT_STITCH_TOKEN_RE = /^([a-z-]+)(\d+)$/i;
|
|
1526
1683
|
var RowDslParser = class {
|
|
1527
|
-
input;
|
|
1528
|
-
registry;
|
|
1529
|
-
index = 0;
|
|
1530
1684
|
constructor(input, registry) {
|
|
1685
|
+
this.index = 0;
|
|
1531
1686
|
this.input = input;
|
|
1532
1687
|
this.registry = registry ?? createDefaultStitchRegistry();
|
|
1533
1688
|
}
|
|
@@ -2067,6 +2222,9 @@ function resolveRowDslAst(ast) {
|
|
|
2067
2222
|
}
|
|
2068
2223
|
|
|
2069
2224
|
// src/parser/rowDsl.ts
|
|
2225
|
+
function isStitchRegistry(value) {
|
|
2226
|
+
return typeof value.isKnown === "function";
|
|
2227
|
+
}
|
|
2070
2228
|
function parseWithAstParser(parser, input, registry) {
|
|
2071
2229
|
return resolveRowDslAst(parser(input, registry));
|
|
2072
2230
|
}
|
|
@@ -2079,11 +2237,24 @@ function parseRowDsl(input, registry) {
|
|
|
2079
2237
|
function parseCompatibleRowDsl(input, registry) {
|
|
2080
2238
|
return parseWithAstParser(parseCompatibleRowDslAst, input, registry);
|
|
2081
2239
|
}
|
|
2082
|
-
function compileRowDsl(input, totalStitches,
|
|
2083
|
-
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2240
|
+
function compileRowDsl(input, totalStitches, registryOrOptions) {
|
|
2241
|
+
if (registryOrOptions !== void 0 && !isStitchRegistry(registryOrOptions)) {
|
|
2242
|
+
const { registry, shaping, alignment } = registryOrOptions;
|
|
2243
|
+
return compileRow({ row: parseRowDsl(input, registry), totalStitches, shaping, alignment });
|
|
2244
|
+
}
|
|
2245
|
+
return compileWithParser(parseRowDsl, input, totalStitches, registryOrOptions);
|
|
2246
|
+
}
|
|
2247
|
+
function compileCompatibleRowDsl(input, totalStitches, registryOrOptions) {
|
|
2248
|
+
if (registryOrOptions !== void 0 && !isStitchRegistry(registryOrOptions)) {
|
|
2249
|
+
const { registry, shaping, alignment } = registryOrOptions;
|
|
2250
|
+
return compileRow({
|
|
2251
|
+
row: parseCompatibleRowDsl(input, registry),
|
|
2252
|
+
totalStitches,
|
|
2253
|
+
shaping,
|
|
2254
|
+
alignment
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
return compileWithParser(parseCompatibleRowDsl, input, totalStitches, registryOrOptions);
|
|
2087
2258
|
}
|
|
2088
2259
|
|
|
2089
2260
|
// src/public/parser.ts
|
|
@@ -2119,16 +2290,34 @@ function parseCompatibleRowDsl2(input, registry) {
|
|
|
2119
2290
|
throw wrapParseError(error, "parseCompatibleRowDsl");
|
|
2120
2291
|
}
|
|
2121
2292
|
}
|
|
2122
|
-
function compileRowDsl2(input, totalStitches,
|
|
2293
|
+
function compileRowDsl2(input, totalStitches, registryOrOptions) {
|
|
2123
2294
|
try {
|
|
2124
|
-
|
|
2295
|
+
if (registryOrOptions !== void 0 && typeof registryOrOptions.isKnown !== "function") {
|
|
2296
|
+
return compileRowDsl(input, totalStitches, registryOrOptions);
|
|
2297
|
+
}
|
|
2298
|
+
return compileRowDsl(
|
|
2299
|
+
input,
|
|
2300
|
+
totalStitches,
|
|
2301
|
+
registryOrOptions
|
|
2302
|
+
);
|
|
2125
2303
|
} catch (error) {
|
|
2126
2304
|
throw wrapExpansionError2(error, "compileRowDsl");
|
|
2127
2305
|
}
|
|
2128
2306
|
}
|
|
2129
|
-
function compileCompatibleRowDsl2(input, totalStitches,
|
|
2307
|
+
function compileCompatibleRowDsl2(input, totalStitches, registryOrOptions) {
|
|
2130
2308
|
try {
|
|
2131
|
-
|
|
2309
|
+
if (registryOrOptions !== void 0 && typeof registryOrOptions.isKnown !== "function") {
|
|
2310
|
+
return compileCompatibleRowDsl(
|
|
2311
|
+
input,
|
|
2312
|
+
totalStitches,
|
|
2313
|
+
registryOrOptions
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
return compileCompatibleRowDsl(
|
|
2317
|
+
input,
|
|
2318
|
+
totalStitches,
|
|
2319
|
+
registryOrOptions
|
|
2320
|
+
);
|
|
2132
2321
|
} catch (error) {
|
|
2133
2322
|
throw wrapExpansionError2(error, "compileCompatibleRowDsl");
|
|
2134
2323
|
}
|
|
@@ -2162,127 +2351,6 @@ function resolveRowDslAst2(ast) {
|
|
|
2162
2351
|
}
|
|
2163
2352
|
}
|
|
2164
2353
|
|
|
2165
|
-
// src/shaping/distribution.ts
|
|
2166
|
-
function distributeEvenly(totalStitches, operations) {
|
|
2167
|
-
if (operations <= 0) {
|
|
2168
|
-
return { spacing: [] };
|
|
2169
|
-
}
|
|
2170
|
-
const baseSpacing = Math.floor(totalStitches / operations);
|
|
2171
|
-
const remainder = totalStitches % operations;
|
|
2172
|
-
const spacing = [];
|
|
2173
|
-
for (let i = 0; i < operations; i++) {
|
|
2174
|
-
const shouldAddExtra = Math.floor(i * remainder / operations) !== Math.floor((i + 1) * remainder / operations);
|
|
2175
|
-
spacing.push(baseSpacing + (shouldAddExtra ? 1 : 0));
|
|
2176
|
-
}
|
|
2177
|
-
return { spacing };
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// src/shaping/shapingRules.ts
|
|
2181
|
-
var SHAPING_RULES = {
|
|
2182
|
-
decrease: { consume: 2, produce: 1 },
|
|
2183
|
-
// k2tog - A decrease consumes 2 stitches and produces 1.
|
|
2184
|
-
increase: { consume: 1, produce: 2 }
|
|
2185
|
-
// M1 - An increase consumes 1 stitch and produces 2 stitches.
|
|
2186
|
-
};
|
|
2187
|
-
|
|
2188
|
-
// src/shaping/shaping.ts
|
|
2189
|
-
function getShapingPositions(length, count) {
|
|
2190
|
-
if (count <= 0 || length <= 0) {
|
|
2191
|
-
return [];
|
|
2192
|
-
}
|
|
2193
|
-
const { spacing } = distributeEvenly(length, count);
|
|
2194
|
-
const positions = [];
|
|
2195
|
-
let current = 0;
|
|
2196
|
-
for (const gap of spacing) {
|
|
2197
|
-
positions.push(current);
|
|
2198
|
-
current += gap;
|
|
2199
|
-
}
|
|
2200
|
-
return positions.filter((position) => position >= 0 && position < length);
|
|
2201
|
-
}
|
|
2202
|
-
function applyPhysicalShaping(stitches, shapingType, positions) {
|
|
2203
|
-
const rule = SHAPING_RULES[shapingType];
|
|
2204
|
-
const result = [];
|
|
2205
|
-
let i = 0;
|
|
2206
|
-
let positionIndex = 0;
|
|
2207
|
-
while (i < stitches.length) {
|
|
2208
|
-
if (positionIndex < positions.length && i === positions[positionIndex]) {
|
|
2209
|
-
for (let j = 0; j < rule.produce; j++) {
|
|
2210
|
-
result.push({
|
|
2211
|
-
isShaping: true,
|
|
2212
|
-
operationIndex: positionIndex,
|
|
2213
|
-
shapingType,
|
|
2214
|
-
sourceIndex: i,
|
|
2215
|
-
type: shapingType
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2218
|
-
i += rule.consume;
|
|
2219
|
-
positionIndex++;
|
|
2220
|
-
} else {
|
|
2221
|
-
result.push(stitches[i]);
|
|
2222
|
-
i++;
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
return result;
|
|
2226
|
-
}
|
|
2227
|
-
function compressStitches(stitches) {
|
|
2228
|
-
const result = [];
|
|
2229
|
-
for (const stitch of stitches) {
|
|
2230
|
-
const last = result.at(-1);
|
|
2231
|
-
if ((last == null ? void 0 : last.type) === stitch.type) {
|
|
2232
|
-
last.count++;
|
|
2233
|
-
} else {
|
|
2234
|
-
result.push({ type: stitch.type, count: 1 });
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
return result;
|
|
2238
|
-
}
|
|
2239
|
-
function shapeRow(input) {
|
|
2240
|
-
const { row, totalStitches, shaping } = input;
|
|
2241
|
-
if (shaping.count < 0) {
|
|
2242
|
-
throw new Error("Shaping count must be 0 or greater.");
|
|
2243
|
-
}
|
|
2244
|
-
const expanded = expandRowToStitchTypes(row, totalStitches).map((type, sourceIndex) => ({
|
|
2245
|
-
sourceIndex,
|
|
2246
|
-
type
|
|
2247
|
-
}));
|
|
2248
|
-
const rule = SHAPING_RULES[shaping.type];
|
|
2249
|
-
if (shaping.count * rule.consume > expanded.length) {
|
|
2250
|
-
throw new Error("Cannot apply more shaping operations than stitches in the row.");
|
|
2251
|
-
}
|
|
2252
|
-
const shapingPositions = getShapingPositions(expanded.length, shaping.count);
|
|
2253
|
-
const shapedStitches = applyPhysicalShaping(expanded, shaping.type, shapingPositions);
|
|
2254
|
-
const stitches = compressStitches(shapedStitches);
|
|
2255
|
-
const resultingStitches = totalStitches + shaping.count * (rule.produce - rule.consume);
|
|
2256
|
-
return { shapedStitches, stitches, totalStitches: resultingStitches };
|
|
2257
|
-
}
|
|
2258
|
-
function groupShapedStitchesByOperation(shapedStitches) {
|
|
2259
|
-
const groups = /* @__PURE__ */ new Map();
|
|
2260
|
-
for (let outputIndex = 0; outputIndex < shapedStitches.length; outputIndex += 1) {
|
|
2261
|
-
const stitch = shapedStitches[outputIndex];
|
|
2262
|
-
if (!stitch.isShaping || stitch.operationIndex === void 0 || stitch.shapingType === void 0) {
|
|
2263
|
-
continue;
|
|
2264
|
-
}
|
|
2265
|
-
const existing = groups.get(stitch.operationIndex);
|
|
2266
|
-
if (existing) {
|
|
2267
|
-
if (existing.shapingType !== stitch.shapingType) {
|
|
2268
|
-
throw new Error("Shaping operation contains mixed shaping types.");
|
|
2269
|
-
}
|
|
2270
|
-
existing.stitches.push(stitch);
|
|
2271
|
-
existing.outputIndices.push(outputIndex);
|
|
2272
|
-
existing.sourceIndices.push(stitch.sourceIndex);
|
|
2273
|
-
continue;
|
|
2274
|
-
}
|
|
2275
|
-
groups.set(stitch.operationIndex, {
|
|
2276
|
-
operationIndex: stitch.operationIndex,
|
|
2277
|
-
outputIndices: [outputIndex],
|
|
2278
|
-
shapingType: stitch.shapingType,
|
|
2279
|
-
sourceIndices: [stitch.sourceIndex],
|
|
2280
|
-
stitches: [stitch]
|
|
2281
|
-
});
|
|
2282
|
-
}
|
|
2283
|
-
return Array.from(groups.values());
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
2354
|
// src/shaping/shapingDsl.ts
|
|
2287
2355
|
var SHAPING_CLAUSE_RE = /^(?:shape|shaping)\s*:?\s*(increase|decrease)\s+(\d+)$/i;
|
|
2288
2356
|
var TOTAL_CLAUSE_RE = /^(?:total|total-stitches|stitches)\s*:?\s*(\d+)$/i;
|
|
@@ -2405,8 +2473,8 @@ function resolveShapingDslAst(ast) {
|
|
|
2405
2473
|
function parseShapingDsl(input) {
|
|
2406
2474
|
return resolveShapingDslAst(parseShapingDslAst(input));
|
|
2407
2475
|
}
|
|
2408
|
-
function compileShapingDsl(input) {
|
|
2409
|
-
return shapeRow(parseShapingDsl(input));
|
|
2476
|
+
function compileShapingDsl(input, alignment) {
|
|
2477
|
+
return shapeRow({ ...parseShapingDsl(input), alignment });
|
|
2410
2478
|
}
|
|
2411
2479
|
|
|
2412
2480
|
// src/shaping/shapingDebug.ts
|
|
@@ -2461,11 +2529,11 @@ function renderShapingDebugForInput(input, options = {}) {
|
|
|
2461
2529
|
function printShapingDebug(shapedStitches, label, options = {}) {
|
|
2462
2530
|
const { legend, markers, row } = renderShapingDebug(shapedStitches, options);
|
|
2463
2531
|
if (label) {
|
|
2464
|
-
debugLogger
|
|
2532
|
+
debugLogger?.log(label);
|
|
2465
2533
|
}
|
|
2466
|
-
debugLogger
|
|
2467
|
-
debugLogger
|
|
2468
|
-
debugLogger
|
|
2534
|
+
debugLogger?.log(row);
|
|
2535
|
+
debugLogger?.log(markers);
|
|
2536
|
+
debugLogger?.log(legend);
|
|
2469
2537
|
}
|
|
2470
2538
|
|
|
2471
2539
|
// src/public/shaping.ts
|
|
@@ -2508,6 +2576,13 @@ function compileShapingDsl2(input) {
|
|
|
2508
2576
|
throw wrapExpansionError3(error, "compileShapingDsl");
|
|
2509
2577
|
}
|
|
2510
2578
|
}
|
|
2579
|
+
function compileRow2(input) {
|
|
2580
|
+
try {
|
|
2581
|
+
return compileRow(input);
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
throw wrapExpansionError3(error, "compileRow");
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2511
2586
|
function shapeRow2(input) {
|
|
2512
2587
|
try {
|
|
2513
2588
|
return shapeRow(input);
|
|
@@ -2537,6 +2612,7 @@ export {
|
|
|
2537
2612
|
cmToStitches,
|
|
2538
2613
|
compileCompatibleRowDsl2 as compileCompatibleRowDsl,
|
|
2539
2614
|
compilePattern2 as compilePattern,
|
|
2615
|
+
compileRow2 as compileRow,
|
|
2540
2616
|
compileRowDsl2 as compileRowDsl,
|
|
2541
2617
|
compileShapingDsl2 as compileShapingDsl,
|
|
2542
2618
|
convertLength,
|
|
@@ -2551,7 +2627,7 @@ export {
|
|
|
2551
2627
|
expandRowToStitches,
|
|
2552
2628
|
groupShapedStitchesByOperation2 as groupShapedStitchesByOperation,
|
|
2553
2629
|
isRowDslAstWellFormed,
|
|
2554
|
-
normaliseMeasurement,
|
|
2630
|
+
normaliseMeasurement2 as normaliseMeasurement,
|
|
2555
2631
|
parseCompatibleRowDsl2 as parseCompatibleRowDsl,
|
|
2556
2632
|
parseCompatibleRowDslAst2 as parseCompatibleRowDslAst,
|
|
2557
2633
|
parseRowDsl2 as parseRowDsl,
|
|
@@ -2563,10 +2639,10 @@ export {
|
|
|
2563
2639
|
renderShapingDebug,
|
|
2564
2640
|
renderShapingDebugForInput,
|
|
2565
2641
|
resolveRowDslAst2 as resolveRowDslAst,
|
|
2566
|
-
roundStitches,
|
|
2642
|
+
roundStitches2 as roundStitches,
|
|
2567
2643
|
rowToRowDsl2 as rowToRowDsl,
|
|
2568
2644
|
rowsPerCm,
|
|
2569
2645
|
shapeRow2 as shapeRow,
|
|
2570
2646
|
shapedExpandPattern2 as shapedExpandPattern,
|
|
2571
|
-
stitchesPerCm
|
|
2647
|
+
stitchesPerCm2 as stitchesPerCm
|
|
2572
2648
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knitting-tools/core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Knitting maths, row parsing, shaping, and pattern compilation utilities.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -36,19 +36,21 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"test": "vitest run",
|
|
42
|
-
"typecheck": "tsc -p tsconfig.src.json --noEmit",
|
|
43
|
-
"test:watch": "vitest",
|
|
44
|
-
"test:coverage": "vitest run --coverage",
|
|
45
|
-
"check": "pnpm build && pnpm typecheck && pnpm test:coverage",
|
|
46
|
-
"prepack": "pnpm build"
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@knitting-tools/math": "3.0.0"
|
|
47
41
|
},
|
|
48
42
|
"devDependencies": {
|
|
49
43
|
"@vitest/coverage-v8": "4.1.0",
|
|
50
44
|
"tsup": "^8.5.1",
|
|
51
45
|
"typescript": "^5.9.3",
|
|
52
46
|
"vitest": "^4.1.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.src.json --noEmit",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"test:coverage": "vitest run --coverage",
|
|
54
|
+
"check": "pnpm build && pnpm typecheck && pnpm test:coverage"
|
|
53
55
|
}
|
|
54
|
-
}
|
|
56
|
+
}
|