This directory contains the core loom representation and computation logic for AdaCAD. It provides a flexible system for representing different types of looms (jacquard, frame/shaft-treadle, and direct-tie/dobby) and converting between loom configurations and drawdowns.
Defines the core TypeScript types used throughout the loom system:
-
Loom: The fundamental loom data structure containing:threading: Array of numbers representing which frame/shaft each warp thread is assigned totieup: 2D array of booleans representing connections between frames and treadlestreadling: 2D array of numbers representing which treadles are pressed for each pick
-
LoomSettings: User-defined preferences and constraints:type: The loom type identifier ('jacquard', 'frame', or 'direct')epi: Ends per unit length (warp density)ppi: Picks per unit length (weft density)units: Measurement units ('cm' - per 10cm or 'in' - per inch)frames: Maximum number of frames/shafts availabletreadles: Maximum number of treadles available (-1 for unlimited)
-
LoomUtil: Interface defining the operations a loom type must support. Each loom type implements this interface to provide type-specific behavior.
Contains generic/shared functions used across all loom types:
Loom Creation & Manipulation:
initLoom(): Creates an empty loom structurecopyLoom(): Deep copies a loom objectcopyLoomSettings(): Copies loom settings with defaults
Conversion Functions:
convertLoom(): Converts a loom from one type to anotherconvertTieupToLiftPlan(): Converts frame/treadle loom to direct-tie formatconvertLiftPlanToTieup(): Converts direct-tie loom to frame/treadle formatcomputeDrawdown(): Generates a drawdown from a loom configurationgenerateThreading(): Generates threading pattern from a drawdowngenerateTreadlingforFrameLoom(): Generates treadling pattern from a drawdown
Utility Functions:
getLoomUtilByType(): Returns the appropriate LoomUtil implementation for a loom typenumFrames(): Calculates the number of frames used in a loomnumTreadles(): Calculates the number of treadles used in a loomcalcWidth()/calcLength(): Calculate physical dimensions from drawdown and settingsconvertEPItoMM(): Converts ends per inch/cm to millimeters- Range checking functions:
isInThreadingRange(),isInTreadlingRange(),isInTieupRange(), etc. - Flipping functions:
flipLoom(),flipThreading(),flipTreadling(),flipTieUp()
Implements the jacquard loom type (jacquard_utils):
- Type:
'jacquard' - Display Name: "jacquard loom"
- Description: Drafts exclusively from drawdown, disregarding frame and treadle information
- Key Features:
- Minimal implementation - jacquard looms don't use threading/tieup/treadling
- Only implements
getDressingInfo()to display loom information - Used when working directly with drawdowns without loom constraints
Implements the direct-tie/dobby loom type (direct_utils):
- Type:
'direct' - Display Name: "direct-tie or dobby loom"
- Description: Uses a direct tieup where each frame connects directly to a corresponding treadle, with support for multiple treadle assignments per pick
- Key Functions:
computeLoomFromDrawdown(): Generates threading and treadling from drawdown, creates direct tieupcomputeDrawdownFromLoom(): Computes drawdown from loom configurationrecomputeLoomFromThreadingAndDrawdown(): Updates treadling and tieup based on threading and drawdownupdateThreading()/updateTreadling()/updateTieup(): Handle individual cell editspasteThreading()/pasteTreadling(): Handle pasting patterns into threading/treadlinggetDressingInfo(): Provides detailed loom statistics
Implements the frame/shaft-treadle loom type (frame_utils):
- Type:
'frame' - Display Name: "shaft/treadle loom"
- Description: Uses a tieup matrix to connect frames to treadles, with one treadle per pick
- Key Functions:
computeLoomFromDrawdown(): Generates threading, treadling, and tieup from drawdowncomputeDrawdownFromLoom(): Computes drawdown from loom configurationrecomputeLoomFromThreadingAndDrawdown(): Updates tieup and treadling based on threading and drawdownupdateThreading()/updateTreadling()/updateTieup(): Handle individual cell edits with tieup expansionpasteThreading()/pasteTreadling()/pasteTieup(): Handle pasting patternsgetDressingInfo(): Provides detailed loom statistics including tieup information
Exports all public functions and types from the loom directory for use throughout the codebase.
To add a new loom type to AdaCAD, follow these steps:
Create a new TypeScript file (e.g., mycustomloom.ts) in the loom directory. Import the necessary types and functions:
import { Drawdown, InterlacementVal, warps, wefts, getCellValue } from "../draft";
import { LoomUtil, LoomSettings, Loom } from "./types";
import {
generateThreading,
computeDrawdown,
calcWidth,
// ... other shared functions you need
} from "./loom";Create a LoomUtil object that implements all required and optional methods:
export const mycustom_utils: LoomUtil = {
type: 'mycustom', // Your unique type identifier
displayname: 'My Custom Loom', // Display name shown in UI
dx: "Description of how this loom type works",
// Required: Provide dressing information
getDressingInfo: (dd: Drawdown, loom: Loom, ls: LoomSettings) => {
return [
{ label: 'loom type', value: 'My Custom Loom' },
// ... add more info as needed
];
},
// Optional: Compute loom from drawdown
computeLoomFromDrawdown: async (d: Drawdown, loom_settings: LoomSettings): Promise<Loom> => {
// Your implementation here
// Generate threading, tieup, and treadling from the drawdown
},
// Optional: Compute drawdown from loom
computeDrawdownFromLoom: async (l: Loom): Promise<Drawdown> => {
// Your implementation here
// Use computeDrawdown() or implement custom logic
},
// Optional: Recompute loom when threading changes
recomputeLoomFromThreadingAndDrawdown: async (
l: Loom,
loom_settings: LoomSettings,
d: Drawdown
): Promise<Loom> => {
// Your implementation here
},
// Optional: Handle individual cell updates
updateThreading: (loom: Loom, ndx: InterlacementVal): Loom => {
// Update threading when user edits a cell
return loom;
},
updateTreadling: (loom: Loom, ndx: InterlacementVal): Loom => {
// Update treadling when user edits a cell
return loom;
},
updateTieup: (loom: Loom, ndx: InterlacementVal): Loom => {
// Update tieup when user edits a cell
return loom;
},
// Optional: Handle insertions
insertIntoThreading: (loom: Loom, j: number, val: number): Loom => {
// Insert a value into threading at position j
return loom;
},
insertIntoTreadling: (loom: Loom, i: number, val: Array<number>): Loom => {
// Insert a value into treadling at position i
return loom;
},
// Optional: Handle deletions
deleteFromThreading: (loom: Loom, j: number): Loom => {
// Delete from threading at position j
return loom;
},
deleteFromTreadling: (loom: Loom, i: number): Loom => {
// Delete from treadling at position i
return loom;
},
// Optional: Handle pasting operations
pasteThreading: (
loom: Loom,
drawdown: Drawdown,
ndx: InterlacementVal,
width: number,
height: number
): Loom => {
// Paste drawdown pattern into threading
return loom;
},
pasteTreadling: (
loom: Loom,
drawdown: Drawdown,
ndx: InterlacementVal,
width: number,
height: number
): Loom => {
// Paste drawdown pattern into treadling
return loom;
},
pasteTieup: (
loom: Loom,
drawdown: Drawdown,
ndx: InterlacementVal,
width: number,
height: number
): Loom => {
// Paste drawdown pattern into tieup
return loom;
},
};Add your loom type to the getLoomUtilByType() function in loom.ts:
export const getLoomUtilByType = (type: 'frame' | 'direct' | 'jacquard' | 'mycustom' | string): LoomUtil => {
switch (type) {
case 'frame': return frame_utils;
case 'direct': return direct_utils;
case 'jacquard': return jacquard_utils;
case 'mycustom': return mycustom_utils; // Add your type here
default: return jacquard_utils;
}
}Update the LoomUtil type in types.ts to include your new type:
export type LoomUtil = {
type: 'jacquard' | 'frame' | 'direct' | 'mycustom', // Add your type
// ... rest of the interface
}Add your export to index.ts:
export * from './mycustomloom';If your loom type needs to convert to/from other loom types, add conversion logic to the convertLoom() function in loom.ts:
export const convertLoom = (drawdown: Drawdown, l: Loom, from_ls: LoomSettings, to_ls: LoomSettings): Promise<Loom | null> => {
// ... existing conversions ...
// Add your conversion cases
else if (from_ls.type === 'mycustom' && to_ls.type === 'frame') {
// Conversion logic here
}
// ... rest of function
}If your loom type requires special UI handling, you'll need to update the Angular components in projects/ui/src/app/editor/loom/ to support your new loom type. This may include:
- Adding the type to loom type selection dropdowns
- Implementing custom rendering for your loom's specific features
- Adding validation for your loom's constraints
-
Study Existing Implementations: Look at
dobby.tsandshafttreadle.tsfor examples of how to implement the various functions. -
Use Shared Functions: Many functions in
loom.tsare designed to be reused. Functions likegenerateThreading(),computeDrawdown(), andpasteDirectAndFrameThreading()can often be used directly or adapted. -
Handle Edge Cases: Make sure your implementation handles:
- Empty drawdowns
- Blank columns/rows
- Invalid indices
- User-defined frame/treadle limits
-
Maintain Immutability: The codebase prefers immutable data structures. When updating looms, create new objects rather than mutating existing ones where possible.
-
Async Functions: Some functions return
Promise<Loom>orPromise<Drawdown>. UsePromise.resolve()for synchronous operations or implement actual async logic if needed. -
Testing: Consider adding tests for your loom type in the test suite to ensure it works correctly with various drawdowns and settings.
Here's a minimal example of a new loom type that only provides basic information:
import { Drawdown } from "../draft";
import { LoomUtil, Loom, LoomSettings } from "./types";
export const minimal_utils: LoomUtil = {
type: 'minimal',
displayname: 'Minimal Loom',
dx: "A minimal loom implementation",
getDressingInfo: (dd: Drawdown, loom: Loom, ls: LoomSettings) => {
return [
{ label: 'loom type', value: 'Minimal Loom' },
{ label: 'warp ends', value: dd[0]?.length.toString() || '0' },
{ label: 'weft picks', value: dd.length.toString() },
];
},
};This minimal implementation can be extended with additional functions as needed.