diff --git a/examples/autoFill.html b/examples/autoFill.html new file mode 100644 index 0000000..0da9d0b --- /dev/null +++ b/examples/autoFill.html @@ -0,0 +1,56 @@ + + + + + + AutoFill Example + + + + + + diff --git a/package.json b/package.json index 0604e20..837efad 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "module": "dist/stitch.mjs", "types": "dist/stitch.d.ts", "scripts": { - "build": "tsup --entry.stitch src/index.ts --format cjs,esm,iife --dts --clean --minify --global-name Stitch", + "build": "pnpm run lint:write && tsup --entry.stitch src/index.ts --format cjs,esm,iife --dts --clean --minify --global-name Stitch", "release": "pnpm run lint:check && pnpm run build && changeset publish", "lint:write": "prettier --write \"**/*.{ts, tsx}\" && tsc", "lint:check": "prettier --check \"**/*.{ts, tsx}\" && tsc" diff --git a/src/Browser/Utils.ts b/src/Browser/Utils.ts index f43041e..e6e6b98 100644 --- a/src/Browser/Utils.ts +++ b/src/Browser/Utils.ts @@ -9,14 +9,19 @@ export const Utils = { for (const [name, value] of Object.entries(attributes)) element.setAttribute(name, value); }, - setStyles: function (element: HTMLElement, styles: { [name: string]: string }): void { - for (const [name, value] of Object.entries(styles)) element.style[name] = value; + setStyles: function (element: HTMLElement, styles: { [key: string]: string }): void { + for (const [key, value] of Object.entries(styles)) + element.style.setProperty(key, value); }, setProperties: function ( element: HTMLElement, - properties: { [name: string]: string }, + properties: { [key: string]: any }, ): void { - for (const [name, value] of Object.entries(properties)) element[name] = value; + for (const [key, value] of Object.entries(properties)) { + if (element.hasOwnProperty(key)) { + (element as any)[key] = value; + } + } }, createElement: function ( elementName: string, @@ -30,8 +35,8 @@ export const Utils = { if (properties) this.setProperties(element, properties); return element; }, - debounce: function (func, time = 0): (event: Event) => void { - let timer; + debounce: function (func: TimerHandler, time = 0): (event: Event) => void { + let timer: number; return function (event) { if (timer) clearTimeout(timer); timer = setTimeout(func, time, event); diff --git a/src/Graphics/getSvg.ts b/src/Graphics/getSvg.ts index f9b91d1..035ce0c 100644 --- a/src/Graphics/getSvg.ts +++ b/src/Graphics/getSvg.ts @@ -18,12 +18,9 @@ export function getSvg( pattern: Pattern, widthPx: number, heightPx: number, - svgOptions: ISvgOptions, + svgOptions: ISvgOptions = defaultSvgOptions, ): SVGElement { - // if (svgOptions !== undefined) { - // for (const [key, value] of Object.entries(defaultSvgOptions)) - // if (!(key as keyof ISvgOptions in svgOptions)) svgOptions[key] = value; - // } else svgOptions = defaultSvgOptions; + svgOptions = { ...svgOptions, ...defaultSvgOptions }; const resolvedStitches = pattern.getStitches( widthPx, heightPx, diff --git a/src/IO/Writers/IWriter.ts b/src/IO/Writers/IWriter.ts index 74d067a..1557491 100644 --- a/src/IO/Writers/IWriter.ts +++ b/src/IO/Writers/IWriter.ts @@ -1,3 +1,5 @@ +import { IResolvedStitches } from '../../Core/Pattern'; + export interface IWriter { - write: (IResolvedStitches, string) => void; + write: (resolvedStitches: IResolvedStitches, filename: string) => void; } diff --git a/src/Math/Graph.ts b/src/Math/Graph.ts index 4f858ae..68b2640 100644 --- a/src/Math/Graph.ts +++ b/src/Math/Graph.ts @@ -24,15 +24,13 @@ export class Graph { this.vertices[vertexId] = properties; this.adjacency[vertexId] = []; } - addEdge(vertexId1: string, vertexId2: string, properties: EdgePropertyType): void { + addEdge(vertexId1: string, vertexId2: string, properties: EdgePropertyType) { const edgeId = Object.keys(this.edges).length; - if (vertexId1 in this.adjacency) { - this.adjacency[vertexId1].push({ edgeId: edgeId, vertexId: vertexId2 }); - } + this.adjacency[vertexId1].push({ edgeId: edgeId, vertexId: vertexId2 }); this.adjacency[vertexId2].push({ edgeId: edgeId, vertexId: vertexId1 }); this.edges[edgeId] = { vertexId1, vertexId2, properties }; } - removeEdge(edgeId) { + removeEdge(edgeId: number) { const edge = this.edges[edgeId]; for (let i = 0; i < this.adjacency[edge.vertexId1].length; i++) { if (this.adjacency[edge.vertexId1][i].edgeId === edgeId) { diff --git a/src/Math/Polyline.ts b/src/Math/Polyline.ts index 1f8ed21..797953a 100644 --- a/src/Math/Polyline.ts +++ b/src/Math/Polyline.ts @@ -14,7 +14,7 @@ export class Polyline { polyline.vertices = vectors; return polyline; } - static fromArrays(arrays: number[][], isClosed = false) { + static fromArrays(arrays: [number, number][], isClosed = false) { const polyline = new Polyline(isClosed); for (const array of arrays) polyline.addVertex(array[0], array[1]); return polyline; @@ -27,13 +27,13 @@ export class Polyline { addVertex(x: number, y: number): void { this.vertices.push(new Vector(x, y)); } - translate(x, y) { + translate(x: number, y: number): Polyline { const translatePolyline = new Polyline(this.isClosed); for (const vertex of this.vertices) translatePolyline.addVertex(vertex.x + x, vertex.y + y); return translatePolyline; } - getRounded(radius, stepAngle = 0.1) { + getRounded(radius: number, stepAngle = 0.1): Polyline { const roundedPolyline = new Polyline(this.isClosed); if (!this.isClosed) roundedPolyline.addVertex(this.vertices[0].x, this.vertices[0].y); for ( @@ -124,7 +124,7 @@ export class Polyline { ); return roundedPolyline; } - getResampled(spacing) { + getResampled(spacing: number): Polyline { if (spacing <= 0 || this.vertices.length === 0) return this; let totalLength = 0; const lengths = [0]; @@ -157,7 +157,7 @@ export class Polyline { ); return resampledPolyline; } - getOffset(distance) { + getOffset(distance: number): Polyline { const offsetPolyline = new Polyline(this.isClosed); for (let i = 0; i < this.vertices.length; i++) { const prev = this.vertices[(i - 1 + this.vertices.length) % this.vertices.length]; @@ -180,14 +180,14 @@ export class Polyline { continue; } const intersection = Utils.lineLineIntersection(p1, p2, q1, q2); - if (intersection) offsetPolyline.vertices.push(intersection); + if (intersection instanceof Vector) offsetPolyline.vertices.push(intersection); } return offsetPolyline; } - getSimplified(tolerance) { + getSimplified(tolerance: number): Polyline { // https://gist.github.com/adammiller/826148?permalink_comment_id=317898#gistcomment-317898 const squaredTolerance = tolerance * tolerance; - const simplifyDP = function (v, j, k, mk) { + const simplifyDP = function (v: Vector[], j: number, k: number, mk: number[]) { if (k < j) return; // let [maxi, maxd2, S] = [j, 0, [v[j], v[k]]]; let maxi = j; @@ -229,7 +229,7 @@ export class Polyline { for (i = m = 0; i < k; i++) if (mk[i]) simplifiedPolyline.vertices.push(vt[i]); return simplifiedPolyline; } - getClosestVertex(position) { + getClosestVertex(position: Vector): Vector { let [minDistance, result] = [Infinity, new Vector(0, 0)]; for (let i = 0; i < this.vertices.length; i++) { const distance = position.distance(this.vertices[i]); @@ -273,7 +273,7 @@ export class Polyline { } return centroid.multiply(1 / 6 / this.getArea()); } - containsPoint(point) { + containsPoint(point: Vector): boolean { let contains = false; for (let i = 0, j = this.vertices.length - 1; i < this.vertices.length; j = i++) { if ( diff --git a/src/Math/Random.ts b/src/Math/Random.ts index 6101785..431cfee 100644 --- a/src/Math/Random.ts +++ b/src/Math/Random.ts @@ -23,7 +23,7 @@ export class Random { prngA: () => number; prngB: () => number; constructor(hash: string) { - if (!hash) { + if (hash === undefined) { hash = this.randomHash(); } this.useA = false; @@ -39,7 +39,7 @@ export class Random { for (let i = 0; i < 64; i++) hash += Math.floor(Math.random() * 16).toString(16); return hash; } - random_dec(): number { + random_dec() { this.useA = !this.useA; return this.useA ? this.prngA() : this.prngB(); } diff --git a/src/Math/Utils.ts b/src/Math/Utils.ts index 881c94a..3fc49be 100644 --- a/src/Math/Utils.ts +++ b/src/Math/Utils.ts @@ -1,22 +1,29 @@ import { Vector } from './Vector'; export const Utils = { - constrain: function (value, low, high) { + constrain: function (value: number, low: number, high: number) { return Math.max(Math.min(value, high), low); }, - map: function (value, a, b, c, d, clamp = false) { + map: function ( + value: number, + a: number, + b: number, + c: number, + d: number, + clamp = false, + ) { const mapped = ((value - a) / (b - a)) * (d - c) + c; if (!clamp) return mapped; if (c < d) return Utils.constrain(mapped, c, d); else return Utils.constrain(mapped, d, c); }, - isPointLeft: function (start, end, point) { + isPointLeft: function (start: Vector, end: Vector, point: Vector) { return ( (end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x) > 0 ); }, - lineSegmentIntersection: function (p1, p2, q1, q2) { + lineSegmentIntersection: function (p1: Vector, p2: Vector, q1: Vector, q2: Vector) { const s1 = p2.subtract(p1); const s2 = q2.subtract(q1); const d = -s2.x * s1.y + s1.x * s2.y; @@ -26,12 +33,12 @@ export const Utils = { return new Vector(p1.x + t * s1.x, p1.y + t * s1.y); else return null; }, - getAngleBetween: function (a, b, c) { + getAngleBetween: function (a: Vector, b: Vector, c: Vector) { const v1 = b.subtract(a).normalized(); const v2 = c.subtract(b).normalized(); return Math.atan2(v2.y * v1.x - v2.x * v1.y, v2.x * v1.x + v2.y * v1.y) + Math.PI; }, - lineLineIntersection: function (p1, p2, q1, q2) { + lineLineIntersection: function (p1: Vector, p2: Vector, q1: Vector, q2: Vector) { const denominator = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y); if (denominator === 0) return false; const ua = @@ -40,7 +47,7 @@ export const Utils = { ((p2.x - p1.x) * (p1.y - q1.y) - (p2.y - p1.y) * (p1.x - q1.x)) / denominator; return p1.add(p2.subtract(p1).multiply(ua)); }, - sdfLine: function (p1, p2, v) { + sdfLine: function (p1: Vector, p2: Vector, v: Vector) { const m = (p2.y - p1.y) / (p2.x - p1.x); const b = p1.y - m * p1.x; return Math.min( @@ -49,13 +56,13 @@ export const Utils = { Math.abs(v.y - m * v.x - b) / Math.sqrt(m * m + 1), ); }, - distance(x1, y1, x2, y2) { + distance(x1: number, y1: number, x2: number, y2: number) { return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); }, - inToMm: function (inches) { + inToMm: function (inches: number) { return inches * 25.4; }, - mmToIn: function (millimeters) { + mmToIn: function (millimeters: number) { return millimeters / 25.4; }, }; diff --git a/src/Runs/AutoFill.ts b/src/Runs/AutoFill.ts index 9d76d68..c6c57dd 100644 --- a/src/Runs/AutoFill.ts +++ b/src/Runs/AutoFill.ts @@ -32,7 +32,14 @@ export class AutoFill { bounds: BoundingBox; center: Vector; distance: number; - constructor(shape, angle, rowSpacingMm, stitchSpacingMm, startPosition, endPosition) { + constructor( + shape: Polyline[], + angle: number, + rowSpacingMm: number, + stitchSpacingMm: number, + startPosition: Vector, + endPosition: Vector, + ) { this.shape = shape; this.angle = angle; this.rowSpacingMm = rowSpacingMm; @@ -55,14 +62,14 @@ export class AutoFill { this.center = this.bounds.min.add(this.bounds.max).multiply(0.5); this.distance = this.center.distance(this.bounds.min); } - getStitches(pixelsPerMm) { + getStitches(pixelsPerMm: number) { const rows = this.getRows(pixelsPerMm); const fillStitchGraph = this.getFillStitchGraph(rows); const travelGraph = this.getTravelGraph(fillStitchGraph); const stitchPath = this.getStitchPath(fillStitchGraph, travelGraph); return this.getStitchesFromPath(stitchPath, fillStitchGraph, pixelsPerMm); } - getRows(pixelsPerMm) { + getRows(pixelsPerMm: number) { const rows = [] as IIntersection[][][]; for ( let offset = -this.distance; @@ -105,7 +112,7 @@ export class AutoFill { } return rows; } - getFillStitchGraph(rows) { + getFillStitchGraph(rows: IIntersection[][][]) { const fillStitchGraph = new Graph(); for (let i = 0; i < rows.length; i++) { for (let j = 0; j < rows[i].length; j++) { @@ -243,7 +250,11 @@ export class AutoFill { return newPath; } - getStitchesFromPath(stitchPath, fillStitchGraph, pixelsPerMm) { + getStitchesFromPath( + stitchPath: IPathStep[], + fillStitchGraph: Graph, + pixelsPerMm: number, + ) { let stitches = [] as Vector[]; if (stitchPath[0].key !== 'segment') { stitches.push(fillStitchGraph.vertices[stitchPath[0].from].position); @@ -268,7 +279,7 @@ export class AutoFill { } return stitches; } - stitchRow(from, to, pixelsPerMm) { + stitchRow(from: IIntersection, to: IIntersection, pixelsPerMm: number) { const rowStitches = [] as Vector[]; const offset = Utils.map( from.rowIndex % 2, @@ -300,12 +311,12 @@ export class AutoFill { findPath( from: Vector, to: Vector, - maxStepSize, - minStepSize, + maxStepSize: number, + minStepSize: number, nLayer = 0, - originalStepSize = null, - ) { - if (originalStepSize === null) originalStepSize = maxStepSize; + originalStepSize?: number, + ): Vector[] { + if (originalStepSize === undefined) originalStepSize = maxStepSize; const queue: Vector[][] = [[from]]; const visited = new Set(); while (queue.length > 0) { diff --git a/src/Runs/ClassicSatin.ts b/src/Runs/ClassicSatin.ts index 0263c74..a318f54 100644 --- a/src/Runs/ClassicSatin.ts +++ b/src/Runs/ClassicSatin.ts @@ -17,7 +17,7 @@ export class ClassicSatin { this.segments = []; } - static fromQuadStripVectors(vectors, densityMm = 0.4) { + static fromQuadStripVectors(vectors: Vector[], densityMm = 0.4) { const run = new ClassicSatin(densityMm); for (let i = 0; i < vectors.length; i += 2) { run.addVector(vectors[i]); @@ -25,11 +25,11 @@ export class ClassicSatin { return run; } - addVertex(x, y) { + addVertex(x: number, y: number) { this.addVector(new Vector(x, y)); } - addVector(v) { + addVector(v: Vector) { this.vertices.push(v); const vertexCount = this.vertices.length; if (vertexCount > 2 && vertexCount % 2 === 0) { @@ -48,7 +48,7 @@ export class ClassicSatin { } } - getStitches(pixelsPerMm) { + getStitches(pixelsPerMm: number) { const run = [] as Vector[]; run.push(this.vertices[this.segments[0].side0.startIndex]); run.push(this.vertices[this.segments[0].side1.startIndex]); diff --git a/src/Runs/Satin.ts b/src/Runs/Satin.ts index 0d9c7ca..a9ea327 100644 --- a/src/Runs/Satin.ts +++ b/src/Runs/Satin.ts @@ -5,10 +5,10 @@ export class Satin { polyline: Polyline; widthPx: number; densityMm: number; - constructor(polyline, widthPx, densityMm) { + constructor(polyline: Polyline, widthPx: number, densityMm: number) { [this.polyline, this.widthPx, this.densityMm] = [polyline, widthPx, densityMm]; } - getStitches(pixelsPerMm) { + getStitches(pixelsPerMm: number) { const resampled = this.polyline.getResampled(pixelsPerMm * this.densityMm).vertices; if (this.polyline.isClosed) resampled.push(resampled[0]); const stitches = [] as Vector[]; diff --git a/src/index.ts b/src/index.ts index f3f790a..ededf31 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,4 +5,5 @@ import { IO } from './IO'; import { Math } from './Math'; import { Runs } from './Runs'; +// export { Browser, Core, Graphics, IO, Math, Runs }; export { Browser, Core, Graphics, IO, Math, Runs }; diff --git a/tsconfig.json b/tsconfig.json index 5f2eb15..d4a25f7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -82,8 +82,8 @@ /* Type Checking */ "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - "strictNullChecks": false, /* When type checking, take into account 'null' and 'undefined'. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ @@ -95,7 +95,7 @@ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */