@fgv/ts-sudoku-ui 5.0.1-2
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/.rush/temp/chunked-rush-logs/ts-sudoku-ui.build.chunks.jsonl +12 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +12 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +1087 -0
- package/.test-failures-catalog.md +470 -0
- package/CSS_MODULES_MIGRATION_SUMMARY.md +189 -0
- package/dist/ts-sudoku-ui.d.ts +668 -0
- package/etc/ts-sudoku-ui.api.md +481 -0
- package/lib/components/CageOverlay.d.ts +8 -0
- package/lib/components/CageOverlay.d.ts.map +1 -0
- package/lib/components/CageOverlay.js +133 -0
- package/lib/components/CageOverlay.js.map +1 -0
- package/lib/components/CageSumIndicator.d.ts +8 -0
- package/lib/components/CageSumIndicator.d.ts.map +1 -0
- package/lib/components/CageSumIndicator.js +66 -0
- package/lib/components/CageSumIndicator.js.map +1 -0
- package/lib/components/CombinationCard.d.ts +8 -0
- package/lib/components/CombinationCard.d.ts.map +1 -0
- package/lib/components/CombinationCard.js +64 -0
- package/lib/components/CombinationCard.js.map +1 -0
- package/lib/components/CombinationGrid.d.ts +8 -0
- package/lib/components/CombinationGrid.d.ts.map +1 -0
- package/lib/components/CombinationGrid.js +51 -0
- package/lib/components/CombinationGrid.js.map +1 -0
- package/lib/components/CompactControlRibbon.d.ts +45 -0
- package/lib/components/CompactControlRibbon.d.ts.map +1 -0
- package/lib/components/CompactControlRibbon.js +141 -0
- package/lib/components/CompactControlRibbon.js.map +1 -0
- package/lib/components/DualKeypad.d.ts +39 -0
- package/lib/components/DualKeypad.d.ts.map +1 -0
- package/lib/components/DualKeypad.js +137 -0
- package/lib/components/DualKeypad.js.map +1 -0
- package/lib/components/KillerCombinationsExplorer.d.ts +9 -0
- package/lib/components/KillerCombinationsExplorer.d.ts.map +1 -0
- package/lib/components/KillerCombinationsExplorer.js +69 -0
- package/lib/components/KillerCombinationsExplorer.js.map +1 -0
- package/lib/components/KillerCombinationsModal.d.ts +20 -0
- package/lib/components/KillerCombinationsModal.d.ts.map +1 -0
- package/lib/components/KillerCombinationsModal.js +135 -0
- package/lib/components/KillerCombinationsModal.js.map +1 -0
- package/lib/components/KillerCombinationsPanel.d.ts +20 -0
- package/lib/components/KillerCombinationsPanel.d.ts.map +1 -0
- package/lib/components/KillerCombinationsPanel.js +79 -0
- package/lib/components/KillerCombinationsPanel.js.map +1 -0
- package/lib/components/NumberKeypad.d.ts +30 -0
- package/lib/components/NumberKeypad.d.ts.map +1 -0
- package/lib/components/NumberKeypad.js +181 -0
- package/lib/components/NumberKeypad.js.map +1 -0
- package/lib/components/SudokuCell.d.ts +8 -0
- package/lib/components/SudokuCell.d.ts.map +1 -0
- package/lib/components/SudokuCell.js +240 -0
- package/lib/components/SudokuCell.js.map +1 -0
- package/lib/components/SudokuControls.d.ts +8 -0
- package/lib/components/SudokuControls.d.ts.map +1 -0
- package/lib/components/SudokuControls.js +140 -0
- package/lib/components/SudokuControls.js.map +1 -0
- package/lib/components/SudokuGrid.d.ts +8 -0
- package/lib/components/SudokuGrid.d.ts.map +1 -0
- package/lib/components/SudokuGrid.js +177 -0
- package/lib/components/SudokuGrid.js.map +1 -0
- package/lib/components/SudokuGridEntry.d.ts +8 -0
- package/lib/components/SudokuGridEntry.d.ts.map +1 -0
- package/lib/components/SudokuGridEntry.js +373 -0
- package/lib/components/SudokuGridEntry.js.map +1 -0
- package/lib/components/ValidationDisplay.d.ts +8 -0
- package/lib/components/ValidationDisplay.d.ts.map +1 -0
- package/lib/components/ValidationDisplay.js +95 -0
- package/lib/components/ValidationDisplay.js.map +1 -0
- package/lib/contexts/DiagnosticLoggerContext.d.ts +81 -0
- package/lib/contexts/DiagnosticLoggerContext.d.ts.map +1 -0
- package/lib/contexts/DiagnosticLoggerContext.js +98 -0
- package/lib/contexts/DiagnosticLoggerContext.js.map +1 -0
- package/lib/contexts/index.d.ts +3 -0
- package/lib/contexts/index.d.ts.map +1 -0
- package/lib/contexts/index.js +25 -0
- package/lib/contexts/index.js.map +1 -0
- package/lib/hooks/useCombinationElimination.d.ts +10 -0
- package/lib/hooks/useCombinationElimination.d.ts.map +1 -0
- package/lib/hooks/useCombinationElimination.js +123 -0
- package/lib/hooks/useCombinationElimination.js.map +1 -0
- package/lib/hooks/useKeyboardShortcut.d.ts +19 -0
- package/lib/hooks/useKeyboardShortcut.d.ts.map +1 -0
- package/lib/hooks/useKeyboardShortcut.js +55 -0
- package/lib/hooks/useKeyboardShortcut.js.map +1 -0
- package/lib/hooks/useKillerCombinations.d.ts +11 -0
- package/lib/hooks/useKillerCombinations.d.ts.map +1 -0
- package/lib/hooks/useKillerCombinations.js +65 -0
- package/lib/hooks/useKillerCombinations.js.map +1 -0
- package/lib/hooks/useLongPress.d.ts +31 -0
- package/lib/hooks/useLongPress.d.ts.map +1 -0
- package/lib/hooks/useLongPress.js +69 -0
- package/lib/hooks/useLongPress.js.map +1 -0
- package/lib/hooks/usePuzzleSession.d.ts +32 -0
- package/lib/hooks/usePuzzleSession.d.ts.map +1 -0
- package/lib/hooks/usePuzzleSession.js +398 -0
- package/lib/hooks/usePuzzleSession.js.map +1 -0
- package/lib/hooks/useResponsiveLayout.d.ts +44 -0
- package/lib/hooks/useResponsiveLayout.d.ts.map +1 -0
- package/lib/hooks/useResponsiveLayout.js +139 -0
- package/lib/hooks/useResponsiveLayout.js.map +1 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +49 -0
- package/lib/index.js.map +1 -0
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts +36 -0
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts.map +1 -0
- package/lib/test/fixtures/killerSudokuPuzzles.js +57 -0
- package/lib/test/fixtures/killerSudokuPuzzles.js.map +1 -0
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts +22 -0
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts.map +1 -0
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js +116 -0
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js.map +1 -0
- package/lib/tsdoc-metadata.json +11 -0
- package/lib/types/index.d.ts +234 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +25 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/CageLookupManager.d.ts +74 -0
- package/lib/utils/CageLookupManager.d.ts.map +1 -0
- package/lib/utils/CageLookupManager.js +283 -0
- package/lib/utils/CageLookupManager.js.map +1 -0
- package/lib/utils/CagePatternManager.d.ts +82 -0
- package/lib/utils/CagePatternManager.d.ts.map +1 -0
- package/lib/utils/CagePatternManager.js +139 -0
- package/lib/utils/CagePatternManager.js.map +1 -0
- package/lib/utils/puzzleDefinitionHelper.d.ts +25 -0
- package/lib/utils/puzzleDefinitionHelper.d.ts.map +1 -0
- package/lib/utils/puzzleDefinitionHelper.js +99 -0
- package/lib/utils/puzzleDefinitionHelper.js.map +1 -0
- package/lib-commonjs/components/CageOverlay.js +138 -0
- package/lib-commonjs/components/CageOverlay.js.map +1 -0
- package/lib-commonjs/components/CageSumIndicator.js +71 -0
- package/lib-commonjs/components/CageSumIndicator.js.map +1 -0
- package/lib-commonjs/components/CombinationCard.js +68 -0
- package/lib-commonjs/components/CombinationCard.js.map +1 -0
- package/lib-commonjs/components/CombinationGrid.js +56 -0
- package/lib-commonjs/components/CombinationGrid.js.map +1 -0
- package/lib-commonjs/components/CompactControlRibbon.js +146 -0
- package/lib-commonjs/components/CompactControlRibbon.js.map +1 -0
- package/lib-commonjs/components/DualKeypad.js +142 -0
- package/lib-commonjs/components/DualKeypad.js.map +1 -0
- package/lib-commonjs/components/KillerCombinationsExplorer.js +74 -0
- package/lib-commonjs/components/KillerCombinationsExplorer.js.map +1 -0
- package/lib-commonjs/components/KillerCombinationsModal.js +140 -0
- package/lib-commonjs/components/KillerCombinationsModal.js.map +1 -0
- package/lib-commonjs/components/KillerCombinationsPanel.js +84 -0
- package/lib-commonjs/components/KillerCombinationsPanel.js.map +1 -0
- package/lib-commonjs/components/NumberKeypad.js +186 -0
- package/lib-commonjs/components/NumberKeypad.js.map +1 -0
- package/lib-commonjs/components/SudokuCell.js +245 -0
- package/lib-commonjs/components/SudokuCell.js.map +1 -0
- package/lib-commonjs/components/SudokuControls.js +145 -0
- package/lib-commonjs/components/SudokuControls.js.map +1 -0
- package/lib-commonjs/components/SudokuGrid.js +182 -0
- package/lib-commonjs/components/SudokuGrid.js.map +1 -0
- package/lib-commonjs/components/SudokuGridEntry.js +378 -0
- package/lib-commonjs/components/SudokuGridEntry.js.map +1 -0
- package/lib-commonjs/components/ValidationDisplay.js +100 -0
- package/lib-commonjs/components/ValidationDisplay.js.map +1 -0
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js +104 -0
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js.map +1 -0
- package/lib-commonjs/contexts/index.js +32 -0
- package/lib-commonjs/contexts/index.js.map +1 -0
- package/lib-commonjs/hooks/useCombinationElimination.js +126 -0
- package/lib-commonjs/hooks/useCombinationElimination.js.map +1 -0
- package/lib-commonjs/hooks/useKeyboardShortcut.js +58 -0
- package/lib-commonjs/hooks/useKeyboardShortcut.js.map +1 -0
- package/lib-commonjs/hooks/useKillerCombinations.js +68 -0
- package/lib-commonjs/hooks/useKillerCombinations.js.map +1 -0
- package/lib-commonjs/hooks/useLongPress.js +72 -0
- package/lib-commonjs/hooks/useLongPress.js.map +1 -0
- package/lib-commonjs/hooks/usePuzzleSession.js +401 -0
- package/lib-commonjs/hooks/usePuzzleSession.js.map +1 -0
- package/lib-commonjs/hooks/useResponsiveLayout.js +142 -0
- package/lib-commonjs/hooks/useResponsiveLayout.js.map +1 -0
- package/lib-commonjs/index.js +73 -0
- package/lib-commonjs/index.js.map +1 -0
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js +60 -0
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js.map +1 -0
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js +125 -0
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js.map +1 -0
- package/lib-commonjs/types/index.js +26 -0
- package/lib-commonjs/types/index.js.map +1 -0
- package/lib-commonjs/utils/CageLookupManager.js +287 -0
- package/lib-commonjs/utils/CageLookupManager.js.map +1 -0
- package/lib-commonjs/utils/CagePatternManager.js +143 -0
- package/lib-commonjs/utils/CagePatternManager.js.map +1 -0
- package/lib-commonjs/utils/puzzleDefinitionHelper.js +103 -0
- package/lib-commonjs/utils/puzzleDefinitionHelper.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { CellId, ICellContents, NavigationDirection, IPuzzleDefinition, ICage } from '@fgv/ts-sudoku-lib';
|
|
2
|
+
import { KeypadLayoutMode } from '../hooks/useResponsiveLayout';
|
|
3
|
+
/**
|
|
4
|
+
* Validation error for display in the UI
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export interface IValidationError {
|
|
8
|
+
readonly cellId: CellId;
|
|
9
|
+
readonly type: 'duplicate-row' | 'duplicate-column' | 'duplicate-section' | 'duplicate-diagonal' | 'invalid-value';
|
|
10
|
+
readonly conflictingCells: CellId[];
|
|
11
|
+
readonly message: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Props for the main SudokuGridEntry component
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export interface ISudokuGridEntryProps {
|
|
18
|
+
/**
|
|
19
|
+
* Optional initial puzzle session to load. If not provided, an empty puzzle is created.
|
|
20
|
+
*/
|
|
21
|
+
readonly initialPuzzleDescription?: IPuzzleDefinition;
|
|
22
|
+
/**
|
|
23
|
+
* Callback fired when the puzzle state changes
|
|
24
|
+
*/
|
|
25
|
+
readonly onStateChange?: (isValid: boolean, isSolved: boolean) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Callback fired when validation errors occur
|
|
28
|
+
*/
|
|
29
|
+
readonly onValidationErrors?: (errors: IValidationError[]) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Optional CSS class name for styling
|
|
32
|
+
*/
|
|
33
|
+
readonly className?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Optional forced layout mode for testing (overrides responsive detection)
|
|
36
|
+
*/
|
|
37
|
+
readonly forceLayoutMode?: KeypadLayoutMode;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Props for the SudokuGrid component
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export interface ISudokuGridProps {
|
|
44
|
+
readonly numRows: number;
|
|
45
|
+
readonly numColumns: number;
|
|
46
|
+
readonly cells: ICellDisplayInfo[];
|
|
47
|
+
readonly cages?: ICageDisplayInfo[];
|
|
48
|
+
readonly selectedCell: CellId | null;
|
|
49
|
+
readonly selectedCells: CellId[];
|
|
50
|
+
readonly inputMode: InputMode;
|
|
51
|
+
readonly puzzleType?: string;
|
|
52
|
+
readonly puzzleDimensions?: {
|
|
53
|
+
readonly cageHeightInCells: number;
|
|
54
|
+
readonly cageWidthInCells: number;
|
|
55
|
+
readonly boardWidthInCages: number;
|
|
56
|
+
readonly boardHeightInCages: number;
|
|
57
|
+
readonly totalRows: number;
|
|
58
|
+
readonly totalColumns: number;
|
|
59
|
+
readonly maxValue: number;
|
|
60
|
+
readonly totalCages: number;
|
|
61
|
+
readonly basicCageTotal: number;
|
|
62
|
+
};
|
|
63
|
+
readonly onCellSelect: (cellId: CellId, event?: React.MouseEvent) => void;
|
|
64
|
+
readonly onLongPressToggle?: (cellId: CellId, event: React.TouchEvent | React.MouseEvent) => void;
|
|
65
|
+
readonly onCellValueChange: (cellId: CellId, value: number | undefined) => void;
|
|
66
|
+
readonly onNoteToggle: (cellId: CellId, note: number) => void;
|
|
67
|
+
readonly onClearAllNotes: (cellId: CellId) => void;
|
|
68
|
+
readonly onNavigate: (direction: NavigationDirection) => void;
|
|
69
|
+
readonly onDragOver?: (cellId: CellId) => void;
|
|
70
|
+
readonly onDragEnd?: () => void;
|
|
71
|
+
readonly isDragging?: boolean;
|
|
72
|
+
readonly className?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Display information for a single cell
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
export interface ICellDisplayInfo {
|
|
79
|
+
readonly id: CellId;
|
|
80
|
+
readonly row: number;
|
|
81
|
+
readonly column: number;
|
|
82
|
+
readonly contents: ICellContents;
|
|
83
|
+
readonly isImmutable: boolean;
|
|
84
|
+
readonly hasValidationError: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Input mode for cell interactions
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
export type InputMode = 'value' | 'notes';
|
|
91
|
+
/**
|
|
92
|
+
* Props for the SudokuCell component
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
export interface ISudokuCellProps {
|
|
96
|
+
readonly cellInfo: ICellDisplayInfo;
|
|
97
|
+
readonly isSelected: boolean;
|
|
98
|
+
readonly inputMode: InputMode;
|
|
99
|
+
readonly puzzleDimensions?: {
|
|
100
|
+
readonly numRows: number;
|
|
101
|
+
readonly numColumns: number;
|
|
102
|
+
readonly cageHeight: number;
|
|
103
|
+
readonly cageWidth: number;
|
|
104
|
+
};
|
|
105
|
+
readonly onSelect: (event?: React.MouseEvent) => void;
|
|
106
|
+
readonly onLongPressToggle?: (event: React.TouchEvent | React.MouseEvent) => void;
|
|
107
|
+
readonly onValueChange: (value: number | undefined) => void;
|
|
108
|
+
readonly onNoteToggle: (note: number) => void;
|
|
109
|
+
readonly onClearAllNotes: () => void;
|
|
110
|
+
readonly onDragOver?: () => void;
|
|
111
|
+
readonly className?: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Props for the SudokuControls component
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
export interface ISudokuControlsProps {
|
|
118
|
+
readonly canUndo: boolean;
|
|
119
|
+
readonly canRedo: boolean;
|
|
120
|
+
readonly canReset: boolean;
|
|
121
|
+
readonly isValid: boolean;
|
|
122
|
+
readonly isSolved: boolean;
|
|
123
|
+
readonly onUndo: () => void;
|
|
124
|
+
readonly onRedo: () => void;
|
|
125
|
+
readonly onReset: () => void;
|
|
126
|
+
readonly onExport: () => void;
|
|
127
|
+
readonly className?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Props for the ValidationDisplay component
|
|
131
|
+
* @public
|
|
132
|
+
*/
|
|
133
|
+
export interface IValidationDisplayProps {
|
|
134
|
+
readonly errors: IValidationError[];
|
|
135
|
+
readonly isValid: boolean;
|
|
136
|
+
readonly isSolved: boolean;
|
|
137
|
+
readonly className?: string;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Display information for a cage in Killer Sudoku
|
|
141
|
+
* @public
|
|
142
|
+
*/
|
|
143
|
+
export interface ICageDisplayInfo {
|
|
144
|
+
readonly cage: ICage;
|
|
145
|
+
readonly isHighlighted: boolean;
|
|
146
|
+
readonly currentSum?: number;
|
|
147
|
+
readonly isComplete: boolean;
|
|
148
|
+
readonly isValid: boolean;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Props for the CageOverlay component
|
|
152
|
+
* @public
|
|
153
|
+
*/
|
|
154
|
+
export interface ICageOverlayProps {
|
|
155
|
+
readonly cages: ICageDisplayInfo[];
|
|
156
|
+
readonly gridSize: {
|
|
157
|
+
width: number;
|
|
158
|
+
height: number;
|
|
159
|
+
};
|
|
160
|
+
readonly cellSize: number;
|
|
161
|
+
readonly numRows?: number;
|
|
162
|
+
readonly numColumns?: number;
|
|
163
|
+
readonly className?: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Props for the CageSumIndicator component
|
|
167
|
+
* @public
|
|
168
|
+
*/
|
|
169
|
+
export interface ICageSumIndicatorProps {
|
|
170
|
+
readonly cage: ICage;
|
|
171
|
+
readonly currentSum?: number;
|
|
172
|
+
readonly isComplete: boolean;
|
|
173
|
+
readonly isValid: boolean;
|
|
174
|
+
readonly position: {
|
|
175
|
+
top: number;
|
|
176
|
+
left: number;
|
|
177
|
+
};
|
|
178
|
+
readonly className?: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Display mode for killer combinations explorer
|
|
182
|
+
* @public
|
|
183
|
+
*/
|
|
184
|
+
export type IKillerCombinationsMode = 'panel' | 'modal';
|
|
185
|
+
/**
|
|
186
|
+
* Display information for a single combination
|
|
187
|
+
* @public
|
|
188
|
+
*/
|
|
189
|
+
export interface ICombinationDisplayInfo {
|
|
190
|
+
readonly combination: number[];
|
|
191
|
+
readonly signature: string;
|
|
192
|
+
readonly isEliminated: boolean;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* State management for combination elimination
|
|
196
|
+
* @public
|
|
197
|
+
*/
|
|
198
|
+
export interface IEliminationState {
|
|
199
|
+
readonly eliminatedSignatures: Set<string>;
|
|
200
|
+
readonly toggleElimination: (signature: string) => void;
|
|
201
|
+
readonly clearAll: () => void;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Props for the KillerCombinationsExplorer component
|
|
205
|
+
* @public
|
|
206
|
+
*/
|
|
207
|
+
export interface IKillerCombinationsExplorerProps {
|
|
208
|
+
readonly selectedCage: ICage | null;
|
|
209
|
+
readonly puzzleId?: string;
|
|
210
|
+
readonly isOpen: boolean;
|
|
211
|
+
readonly onClose: () => void;
|
|
212
|
+
readonly mobileBreakpoint?: number;
|
|
213
|
+
readonly className?: string;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Props for the CombinationCard component
|
|
217
|
+
* @public
|
|
218
|
+
*/
|
|
219
|
+
export interface ICombinationCardProps {
|
|
220
|
+
readonly combination: ICombinationDisplayInfo;
|
|
221
|
+
readonly onToggle: (signature: string) => void;
|
|
222
|
+
readonly className?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Props for the CombinationGrid component
|
|
226
|
+
* @public
|
|
227
|
+
*/
|
|
228
|
+
export interface ICombinationGridProps {
|
|
229
|
+
readonly combinations: ICombinationDisplayInfo[];
|
|
230
|
+
readonly onToggle: (signature: string) => void;
|
|
231
|
+
readonly mode: IKillerCombinationsMode;
|
|
232
|
+
readonly className?: string;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2023 Erik Fortune
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { CellId, ICellContents, NavigationDirection, IPuzzleDefinition, ICage } from '@fgv/ts-sudoku-lib';\nimport { KeypadLayoutMode } from '../hooks/useResponsiveLayout';\n\n/**\n * Validation error for display in the UI\n * @public\n */\nexport interface IValidationError {\n readonly cellId: CellId;\n readonly type:\n | 'duplicate-row'\n | 'duplicate-column'\n | 'duplicate-section'\n | 'duplicate-diagonal'\n | 'invalid-value';\n readonly conflictingCells: CellId[];\n readonly message: string;\n}\n\n/**\n * Props for the main SudokuGridEntry component\n * @public\n */\nexport interface ISudokuGridEntryProps {\n /**\n * Optional initial puzzle session to load. If not provided, an empty puzzle is created.\n */\n readonly initialPuzzleDescription?: IPuzzleDefinition;\n\n /**\n * Callback fired when the puzzle state changes\n */\n readonly onStateChange?: (isValid: boolean, isSolved: boolean) => void;\n\n /**\n * Callback fired when validation errors occur\n */\n readonly onValidationErrors?: (errors: IValidationError[]) => void;\n\n /**\n * Optional CSS class name for styling\n */\n readonly className?: string;\n\n /**\n * Optional forced layout mode for testing (overrides responsive detection)\n */\n readonly forceLayoutMode?: KeypadLayoutMode;\n}\n\n/**\n * Props for the SudokuGrid component\n * @public\n */\nexport interface ISudokuGridProps {\n readonly numRows: number;\n readonly numColumns: number;\n readonly cells: ICellDisplayInfo[];\n readonly cages?: ICageDisplayInfo[];\n readonly selectedCell: CellId | null;\n readonly selectedCells: CellId[];\n readonly inputMode: InputMode;\n readonly puzzleType?: string;\n readonly puzzleDimensions?: {\n readonly cageHeightInCells: number;\n readonly cageWidthInCells: number;\n readonly boardWidthInCages: number;\n readonly boardHeightInCages: number;\n readonly totalRows: number;\n readonly totalColumns: number;\n readonly maxValue: number;\n readonly totalCages: number;\n readonly basicCageTotal: number;\n };\n readonly onCellSelect: (cellId: CellId, event?: React.MouseEvent) => void;\n readonly onLongPressToggle?: (cellId: CellId, event: React.TouchEvent | React.MouseEvent) => void;\n readonly onCellValueChange: (cellId: CellId, value: number | undefined) => void;\n readonly onNoteToggle: (cellId: CellId, note: number) => void;\n readonly onClearAllNotes: (cellId: CellId) => void;\n readonly onNavigate: (direction: NavigationDirection) => void;\n readonly onDragOver?: (cellId: CellId) => void;\n readonly onDragEnd?: () => void;\n readonly isDragging?: boolean;\n readonly className?: string;\n}\n\n/**\n * Display information for a single cell\n * @public\n */\nexport interface ICellDisplayInfo {\n readonly id: CellId;\n readonly row: number;\n readonly column: number;\n readonly contents: ICellContents;\n readonly isImmutable: boolean;\n readonly hasValidationError: boolean;\n}\n\n/**\n * Input mode for cell interactions\n * @public\n */\nexport type InputMode = 'value' | 'notes';\n\n/**\n * Props for the SudokuCell component\n * @public\n */\nexport interface ISudokuCellProps {\n readonly cellInfo: ICellDisplayInfo;\n readonly isSelected: boolean;\n readonly inputMode: InputMode;\n readonly puzzleDimensions?: {\n readonly numRows: number;\n readonly numColumns: number;\n readonly cageHeight: number;\n readonly cageWidth: number;\n };\n readonly onSelect: (event?: React.MouseEvent) => void;\n readonly onLongPressToggle?: (event: React.TouchEvent | React.MouseEvent) => void;\n readonly onValueChange: (value: number | undefined) => void;\n readonly onNoteToggle: (note: number) => void;\n readonly onClearAllNotes: () => void;\n readonly onDragOver?: () => void;\n readonly className?: string;\n}\n\n/**\n * Props for the SudokuControls component\n * @public\n */\nexport interface ISudokuControlsProps {\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n readonly canReset: boolean;\n readonly isValid: boolean;\n readonly isSolved: boolean;\n readonly onUndo: () => void;\n readonly onRedo: () => void;\n readonly onReset: () => void;\n readonly onExport: () => void;\n readonly className?: string;\n}\n\n/**\n * Props for the ValidationDisplay component\n * @public\n */\nexport interface IValidationDisplayProps {\n readonly errors: IValidationError[];\n readonly isValid: boolean;\n readonly isSolved: boolean;\n readonly className?: string;\n}\n\n/**\n * Display information for a cage in Killer Sudoku\n * @public\n */\nexport interface ICageDisplayInfo {\n readonly cage: ICage;\n readonly isHighlighted: boolean;\n readonly currentSum?: number;\n readonly isComplete: boolean;\n readonly isValid: boolean;\n}\n\n/**\n * Props for the CageOverlay component\n * @public\n */\nexport interface ICageOverlayProps {\n readonly cages: ICageDisplayInfo[];\n readonly gridSize: { width: number; height: number };\n readonly cellSize: number;\n readonly numRows?: number;\n readonly numColumns?: number;\n readonly className?: string;\n}\n\n/**\n * Props for the CageSumIndicator component\n * @public\n */\nexport interface ICageSumIndicatorProps {\n readonly cage: ICage;\n readonly currentSum?: number;\n readonly isComplete: boolean;\n readonly isValid: boolean;\n readonly position: { top: number; left: number };\n readonly className?: string;\n}\n\n/**\n * Display mode for killer combinations explorer\n * @public\n */\nexport type IKillerCombinationsMode = 'panel' | 'modal';\n\n/**\n * Display information for a single combination\n * @public\n */\nexport interface ICombinationDisplayInfo {\n readonly combination: number[];\n readonly signature: string;\n readonly isEliminated: boolean;\n}\n\n/**\n * State management for combination elimination\n * @public\n */\nexport interface IEliminationState {\n readonly eliminatedSignatures: Set<string>;\n readonly toggleElimination: (signature: string) => void;\n readonly clearAll: () => void;\n}\n\n/**\n * Props for the KillerCombinationsExplorer component\n * @public\n */\nexport interface IKillerCombinationsExplorerProps {\n readonly selectedCage: ICage | null;\n readonly puzzleId?: string;\n readonly isOpen: boolean;\n readonly onClose: () => void;\n readonly mobileBreakpoint?: number;\n readonly className?: string;\n}\n\n/**\n * Props for the CombinationCard component\n * @public\n */\nexport interface ICombinationCardProps {\n readonly combination: ICombinationDisplayInfo;\n readonly onToggle: (signature: string) => void;\n readonly className?: string;\n}\n\n/**\n * Props for the CombinationGrid component\n * @public\n */\nexport interface ICombinationGridProps {\n readonly combinations: ICombinationDisplayInfo[];\n readonly onToggle: (signature: string) => void;\n readonly mode: IKillerCombinationsMode;\n readonly className?: string;\n}\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { INeighborAnalysis } from './CagePatternManager';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a cage cell's segment requirements as a 12-bit mask
|
|
4
|
+
*/
|
|
5
|
+
export interface ISegmentMask {
|
|
6
|
+
/** 4 bits: Edges (Top, Right, Bottom, Left) */
|
|
7
|
+
edges: number;
|
|
8
|
+
/** 4 bits: Corner connectors (TR, BR, BL, TL) */
|
|
9
|
+
corners: number;
|
|
10
|
+
/** 4 bits: Internal segments (Top, Right, Bottom, Left) */
|
|
11
|
+
internals: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Segment bit positions for the 12-bit mask
|
|
15
|
+
*/
|
|
16
|
+
export declare enum SegmentBit {
|
|
17
|
+
EDGE_TOP = 1,
|
|
18
|
+
EDGE_RIGHT = 2,
|
|
19
|
+
EDGE_BOTTOM = 4,
|
|
20
|
+
EDGE_LEFT = 8,
|
|
21
|
+
CORNER_TOP_RIGHT = 16,
|
|
22
|
+
CORNER_BOTTOM_RIGHT = 32,
|
|
23
|
+
CORNER_BOTTOM_LEFT = 64,
|
|
24
|
+
CORNER_TOP_LEFT = 128,
|
|
25
|
+
INTERNAL_TOP = 256,
|
|
26
|
+
INTERNAL_RIGHT = 512,
|
|
27
|
+
INTERNAL_BOTTOM = 1024,
|
|
28
|
+
INTERNAL_LEFT = 2048
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Two-stage lookup manager for cage border rendering
|
|
32
|
+
* Stage 1: 8-neighbor pattern → segment pattern ID (256 → 47)
|
|
33
|
+
* Stage 2: segment pattern ID → SVG path data (47 unique patterns)
|
|
34
|
+
*/
|
|
35
|
+
export declare class CageLookupManager {
|
|
36
|
+
private static _instance;
|
|
37
|
+
private readonly _neighborToSegmentLookup;
|
|
38
|
+
private readonly _segmentPatterns;
|
|
39
|
+
private readonly _segmentMaskToId;
|
|
40
|
+
private constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Get singleton instance
|
|
43
|
+
*/
|
|
44
|
+
static getInstance(): CageLookupManager;
|
|
45
|
+
/**
|
|
46
|
+
* Convert neighbor analysis to 8-bit pattern
|
|
47
|
+
*/
|
|
48
|
+
neighborsToPattern(neighbors: INeighborAnalysis): number;
|
|
49
|
+
/**
|
|
50
|
+
* Get SVG path data for a given neighbor pattern
|
|
51
|
+
*/
|
|
52
|
+
getPatternSVG(neighborPattern: number): string;
|
|
53
|
+
/**
|
|
54
|
+
* Get segment pattern ID for debugging
|
|
55
|
+
*/
|
|
56
|
+
getSegmentPatternId(neighborPattern: number): number;
|
|
57
|
+
/**
|
|
58
|
+
* Get total number of unique segment patterns
|
|
59
|
+
*/
|
|
60
|
+
get uniquePatternCount(): number;
|
|
61
|
+
/**
|
|
62
|
+
* Generate both lookup tables
|
|
63
|
+
*/
|
|
64
|
+
private _generateLookupTables;
|
|
65
|
+
/**
|
|
66
|
+
* Calculate segment mask from 8-bit neighbor pattern
|
|
67
|
+
*/
|
|
68
|
+
private _calculateSegmentMask;
|
|
69
|
+
/**
|
|
70
|
+
* Generate SVG path data for a segment mask
|
|
71
|
+
*/
|
|
72
|
+
private _generateSVGPattern;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=CageLookupManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CageLookupManager.d.ts","sourceRoot":"","sources":["../../src/utils/CageLookupManager.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,oBAAY,UAAU;IAEpB,QAAQ,IAAS;IACjB,UAAU,IAAS;IACnB,WAAW,IAAS;IACpB,SAAS,IAAS;IAGlB,gBAAgB,KAAS;IACzB,mBAAmB,KAAS;IAC5B,kBAAkB,KAAS;IAC3B,eAAe,MAAS;IAGxB,YAAY,MAAS;IACrB,cAAc,MAAS;IACvB,eAAe,OAAU;IACzB,aAAa,OAAU;CACxB;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAgC;IAGxD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA4B;IAGrE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgB;IAGjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkD;IAEnF,OAAO;IAIP;;OAEG;WACW,WAAW,IAAI,iBAAiB;IAO9C;;OAEG;IACI,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM;IAc/D;;OAEG;IACI,aAAa,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IASrD;;OAEG;IACI,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IAI3D;;OAEG;IACH,IAAW,kBAAkB,IAAI,MAAM,CAEtC;IAED;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAsD7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAiH5B"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2023 Erik Fortune
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Segment bit positions for the 12-bit mask
|
|
26
|
+
*/
|
|
27
|
+
export var SegmentBit;
|
|
28
|
+
(function (SegmentBit) {
|
|
29
|
+
// Edges (bits 0-3)
|
|
30
|
+
SegmentBit[SegmentBit["EDGE_TOP"] = 1] = "EDGE_TOP";
|
|
31
|
+
SegmentBit[SegmentBit["EDGE_RIGHT"] = 2] = "EDGE_RIGHT";
|
|
32
|
+
SegmentBit[SegmentBit["EDGE_BOTTOM"] = 4] = "EDGE_BOTTOM";
|
|
33
|
+
SegmentBit[SegmentBit["EDGE_LEFT"] = 8] = "EDGE_LEFT";
|
|
34
|
+
// Corner connectors (bits 4-7)
|
|
35
|
+
SegmentBit[SegmentBit["CORNER_TOP_RIGHT"] = 16] = "CORNER_TOP_RIGHT";
|
|
36
|
+
SegmentBit[SegmentBit["CORNER_BOTTOM_RIGHT"] = 32] = "CORNER_BOTTOM_RIGHT";
|
|
37
|
+
SegmentBit[SegmentBit["CORNER_BOTTOM_LEFT"] = 64] = "CORNER_BOTTOM_LEFT";
|
|
38
|
+
SegmentBit[SegmentBit["CORNER_TOP_LEFT"] = 128] = "CORNER_TOP_LEFT";
|
|
39
|
+
// Internal segments (bits 8-11)
|
|
40
|
+
SegmentBit[SegmentBit["INTERNAL_TOP"] = 256] = "INTERNAL_TOP";
|
|
41
|
+
SegmentBit[SegmentBit["INTERNAL_RIGHT"] = 512] = "INTERNAL_RIGHT";
|
|
42
|
+
SegmentBit[SegmentBit["INTERNAL_BOTTOM"] = 1024] = "INTERNAL_BOTTOM";
|
|
43
|
+
SegmentBit[SegmentBit["INTERNAL_LEFT"] = 2048] = "INTERNAL_LEFT";
|
|
44
|
+
})(SegmentBit || (SegmentBit = {}));
|
|
45
|
+
/**
|
|
46
|
+
* Two-stage lookup manager for cage border rendering
|
|
47
|
+
* Stage 1: 8-neighbor pattern → segment pattern ID (256 → 47)
|
|
48
|
+
* Stage 2: segment pattern ID → SVG path data (47 unique patterns)
|
|
49
|
+
*/
|
|
50
|
+
export class CageLookupManager {
|
|
51
|
+
constructor() {
|
|
52
|
+
// Stage 1: Neighbor pattern (8-bit) to segment pattern ID mapping
|
|
53
|
+
this._neighborToSegmentLookup = new Array(256);
|
|
54
|
+
// Stage 2: Segment pattern ID to SVG path data mapping
|
|
55
|
+
this._segmentPatterns = [];
|
|
56
|
+
// Reverse lookup for debugging: segment mask to pattern ID
|
|
57
|
+
this._segmentMaskToId = new Map();
|
|
58
|
+
this._generateLookupTables();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get singleton instance
|
|
62
|
+
*/
|
|
63
|
+
static getInstance() {
|
|
64
|
+
if (!CageLookupManager._instance) {
|
|
65
|
+
CageLookupManager._instance = new CageLookupManager();
|
|
66
|
+
}
|
|
67
|
+
return CageLookupManager._instance;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convert neighbor analysis to 8-bit pattern
|
|
71
|
+
*/
|
|
72
|
+
neighborsToPattern(neighbors) {
|
|
73
|
+
// should be bitwise or but that makes eslint cranky
|
|
74
|
+
return ((neighbors.north ? 1 : 0) +
|
|
75
|
+
(neighbors.northEast ? 2 : 0) +
|
|
76
|
+
(neighbors.east ? 4 : 0) +
|
|
77
|
+
(neighbors.southEast ? 8 : 0) +
|
|
78
|
+
(neighbors.south ? 16 : 0) +
|
|
79
|
+
(neighbors.southWest ? 32 : 0) +
|
|
80
|
+
(neighbors.west ? 64 : 0) +
|
|
81
|
+
(neighbors.northWest ? 128 : 0));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get SVG path data for a given neighbor pattern
|
|
85
|
+
*/
|
|
86
|
+
getPatternSVG(neighborPattern) {
|
|
87
|
+
if (neighborPattern < 0 || neighborPattern >= 256) {
|
|
88
|
+
throw new Error(`Invalid neighbor pattern: ${neighborPattern}`);
|
|
89
|
+
}
|
|
90
|
+
const segmentPatternId = this._neighborToSegmentLookup[neighborPattern];
|
|
91
|
+
return this._segmentPatterns[segmentPatternId];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get segment pattern ID for debugging
|
|
95
|
+
*/
|
|
96
|
+
getSegmentPatternId(neighborPattern) {
|
|
97
|
+
return this._neighborToSegmentLookup[neighborPattern];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get total number of unique segment patterns
|
|
101
|
+
*/
|
|
102
|
+
get uniquePatternCount() {
|
|
103
|
+
return this._segmentPatterns.length;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Generate both lookup tables
|
|
107
|
+
*/
|
|
108
|
+
_generateLookupTables() {
|
|
109
|
+
// First pass: generate all unique segment masks and assign IDs
|
|
110
|
+
const uniqueSegmentMasks = new Set();
|
|
111
|
+
for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {
|
|
112
|
+
const segmentMask = this._calculateSegmentMask(neighborPattern);
|
|
113
|
+
uniqueSegmentMasks.add(segmentMask);
|
|
114
|
+
}
|
|
115
|
+
// Convert to sorted array for consistent ordering
|
|
116
|
+
const sortedSegmentMasks = Array.from(uniqueSegmentMasks).sort((a, b) => a - b);
|
|
117
|
+
// Generate SVG patterns for each unique segment mask
|
|
118
|
+
for (let i = 0; i < sortedSegmentMasks.length; i++) {
|
|
119
|
+
const segmentMask = sortedSegmentMasks[i];
|
|
120
|
+
this._segmentMaskToId.set(segmentMask, i);
|
|
121
|
+
this._segmentPatterns[i] = this._generateSVGPattern(segmentMask);
|
|
122
|
+
}
|
|
123
|
+
// Second pass: populate neighbor-to-segment lookup
|
|
124
|
+
for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {
|
|
125
|
+
const segmentMask = this._calculateSegmentMask(neighborPattern);
|
|
126
|
+
const segmentPatternId = this._segmentMaskToId.get(segmentMask);
|
|
127
|
+
this._neighborToSegmentLookup[neighborPattern] = segmentPatternId;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Calculate segment mask from 8-bit neighbor pattern
|
|
132
|
+
*/
|
|
133
|
+
_calculateSegmentMask(neighborPattern) {
|
|
134
|
+
// Extract individual neighbor bits
|
|
135
|
+
// eslint-disable-next-line no-bitwise
|
|
136
|
+
const n = Boolean(neighborPattern & 1); // North
|
|
137
|
+
// eslint-disable-next-line no-bitwise
|
|
138
|
+
const ne = Boolean(neighborPattern & 2); // NorthEast
|
|
139
|
+
// eslint-disable-next-line no-bitwise
|
|
140
|
+
const e = Boolean(neighborPattern & 4); // East
|
|
141
|
+
// eslint-disable-next-line no-bitwise
|
|
142
|
+
const se = Boolean(neighborPattern & 8); // SouthEast
|
|
143
|
+
// eslint-disable-next-line no-bitwise
|
|
144
|
+
const s = Boolean(neighborPattern & 16); // South
|
|
145
|
+
// eslint-disable-next-line no-bitwise
|
|
146
|
+
const sw = Boolean(neighborPattern & 32); // SouthWest
|
|
147
|
+
// eslint-disable-next-line no-bitwise
|
|
148
|
+
const w = Boolean(neighborPattern & 64); // West
|
|
149
|
+
// eslint-disable-next-line no-bitwise
|
|
150
|
+
const nw = Boolean(neighborPattern & 128); // NorthWest
|
|
151
|
+
let segmentMask = 0;
|
|
152
|
+
// Edges: Draw when corresponding cardinal neighbor is NOT present
|
|
153
|
+
// eslint-disable-next-line no-bitwise
|
|
154
|
+
if (!n)
|
|
155
|
+
segmentMask |= SegmentBit.EDGE_TOP;
|
|
156
|
+
// eslint-disable-next-line no-bitwise
|
|
157
|
+
if (!e)
|
|
158
|
+
segmentMask |= SegmentBit.EDGE_RIGHT;
|
|
159
|
+
// eslint-disable-next-line no-bitwise
|
|
160
|
+
if (!s)
|
|
161
|
+
segmentMask |= SegmentBit.EDGE_BOTTOM;
|
|
162
|
+
// eslint-disable-next-line no-bitwise
|
|
163
|
+
if (!w)
|
|
164
|
+
segmentMask |= SegmentBit.EDGE_LEFT;
|
|
165
|
+
// Internal segments: Draw when corresponding cardinal neighbor IS present
|
|
166
|
+
// eslint-disable-next-line no-bitwise
|
|
167
|
+
if (n)
|
|
168
|
+
segmentMask |= SegmentBit.INTERNAL_TOP;
|
|
169
|
+
// eslint-disable-next-line no-bitwise
|
|
170
|
+
if (e)
|
|
171
|
+
segmentMask |= SegmentBit.INTERNAL_RIGHT;
|
|
172
|
+
// eslint-disable-next-line no-bitwise
|
|
173
|
+
if (s)
|
|
174
|
+
segmentMask |= SegmentBit.INTERNAL_BOTTOM;
|
|
175
|
+
// eslint-disable-next-line no-bitwise
|
|
176
|
+
if (w)
|
|
177
|
+
segmentMask |= SegmentBit.INTERNAL_LEFT;
|
|
178
|
+
// Corner connectors: Draw when diagonal is missing but both adjacent cardinals are present
|
|
179
|
+
// eslint-disable-next-line no-bitwise
|
|
180
|
+
if (!ne && n && e)
|
|
181
|
+
segmentMask |= SegmentBit.CORNER_TOP_RIGHT;
|
|
182
|
+
// eslint-disable-next-line no-bitwise
|
|
183
|
+
if (!se && s && e)
|
|
184
|
+
segmentMask |= SegmentBit.CORNER_BOTTOM_RIGHT;
|
|
185
|
+
// eslint-disable-next-line no-bitwise
|
|
186
|
+
if (!sw && s && w)
|
|
187
|
+
segmentMask |= SegmentBit.CORNER_BOTTOM_LEFT;
|
|
188
|
+
// eslint-disable-next-line no-bitwise
|
|
189
|
+
if (!nw && n && w)
|
|
190
|
+
segmentMask |= SegmentBit.CORNER_TOP_LEFT;
|
|
191
|
+
return segmentMask;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate SVG path data for a segment mask
|
|
195
|
+
*/
|
|
196
|
+
_generateSVGPattern(segmentMask) {
|
|
197
|
+
const paths = [];
|
|
198
|
+
const inset = 0.1; // Standard inset for cage boundaries
|
|
199
|
+
const edgeExtension = 0.0; // Extend to cell edge for connections
|
|
200
|
+
// Determine which segments have internal connections (need to extend to edge)
|
|
201
|
+
// eslint-disable-next-line no-bitwise
|
|
202
|
+
const hasInternalTop = Boolean(segmentMask & SegmentBit.INTERNAL_TOP);
|
|
203
|
+
// eslint-disable-next-line no-bitwise
|
|
204
|
+
const hasInternalRight = Boolean(segmentMask & SegmentBit.INTERNAL_RIGHT);
|
|
205
|
+
// eslint-disable-next-line no-bitwise
|
|
206
|
+
const hasInternalBottom = Boolean(segmentMask & SegmentBit.INTERNAL_BOTTOM);
|
|
207
|
+
// eslint-disable-next-line no-bitwise
|
|
208
|
+
const hasInternalLeft = Boolean(segmentMask & SegmentBit.INTERNAL_LEFT);
|
|
209
|
+
// Edge segments - draw main segment with standard inset, then extensions to edge where needed
|
|
210
|
+
// eslint-disable-next-line no-bitwise
|
|
211
|
+
if (segmentMask & SegmentBit.EDGE_TOP) {
|
|
212
|
+
// Main horizontal segment
|
|
213
|
+
paths.push(`M${inset},${inset} L${1 - inset},${inset}`);
|
|
214
|
+
// Extensions to cell edges where there are internal connections
|
|
215
|
+
if (hasInternalLeft) {
|
|
216
|
+
paths.push(`M${edgeExtension},${inset} L${inset},${inset}`);
|
|
217
|
+
}
|
|
218
|
+
if (hasInternalRight) {
|
|
219
|
+
paths.push(`M${1 - inset},${inset} L${1 - edgeExtension},${inset}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// eslint-disable-next-line no-bitwise
|
|
223
|
+
if (segmentMask & SegmentBit.EDGE_RIGHT) {
|
|
224
|
+
// Main vertical segment
|
|
225
|
+
paths.push(`M${1 - inset},${inset} L${1 - inset},${1 - inset}`);
|
|
226
|
+
// Extensions to cell edges where there are internal connections
|
|
227
|
+
if (hasInternalTop) {
|
|
228
|
+
paths.push(`M${1 - inset},${edgeExtension} L${1 - inset},${inset}`);
|
|
229
|
+
}
|
|
230
|
+
if (hasInternalBottom) {
|
|
231
|
+
paths.push(`M${1 - inset},${1 - inset} L${1 - inset},${1 - edgeExtension}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// eslint-disable-next-line no-bitwise
|
|
235
|
+
if (segmentMask & SegmentBit.EDGE_BOTTOM) {
|
|
236
|
+
// Main horizontal segment
|
|
237
|
+
paths.push(`M${1 - inset},${1 - inset} L${inset},${1 - inset}`);
|
|
238
|
+
// Extensions to cell edges where there are internal connections
|
|
239
|
+
if (hasInternalRight) {
|
|
240
|
+
paths.push(`M${1 - edgeExtension},${1 - inset} L${1 - inset},${1 - inset}`);
|
|
241
|
+
}
|
|
242
|
+
if (hasInternalLeft) {
|
|
243
|
+
paths.push(`M${inset},${1 - inset} L${edgeExtension},${1 - inset}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// eslint-disable-next-line no-bitwise
|
|
247
|
+
if (segmentMask & SegmentBit.EDGE_LEFT) {
|
|
248
|
+
// Main vertical segment
|
|
249
|
+
paths.push(`M${inset},${1 - inset} L${inset},${inset}`);
|
|
250
|
+
// Extensions to cell edges where there are internal connections
|
|
251
|
+
if (hasInternalBottom) {
|
|
252
|
+
paths.push(`M${inset},${1 - edgeExtension} L${inset},${1 - inset}`);
|
|
253
|
+
}
|
|
254
|
+
if (hasInternalTop) {
|
|
255
|
+
paths.push(`M${inset},${inset} L${inset},${edgeExtension}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Corner connectors - extend outward from cage corners
|
|
259
|
+
const cornerRadius = 0.08;
|
|
260
|
+
// eslint-disable-next-line no-bitwise
|
|
261
|
+
if (segmentMask & SegmentBit.CORNER_TOP_RIGHT) {
|
|
262
|
+
// Extend outward from top-right corner
|
|
263
|
+
paths.push(`M${1 - inset},${inset} L${1 - inset + cornerRadius},${inset} M${1 - inset},${inset} L${1 - inset},${inset - cornerRadius}`);
|
|
264
|
+
}
|
|
265
|
+
// eslint-disable-next-line no-bitwise
|
|
266
|
+
if (segmentMask & SegmentBit.CORNER_BOTTOM_RIGHT) {
|
|
267
|
+
// Extend outward from bottom-right corner
|
|
268
|
+
paths.push(`M${1 - inset},${1 - inset} L${1 - inset + cornerRadius},${1 - inset} M${1 - inset},${1 - inset} L${1 - inset},${1 - inset + cornerRadius}`);
|
|
269
|
+
}
|
|
270
|
+
// eslint-disable-next-line no-bitwise
|
|
271
|
+
if (segmentMask & SegmentBit.CORNER_BOTTOM_LEFT) {
|
|
272
|
+
// Extend outward from bottom-left corner
|
|
273
|
+
paths.push(`M${inset},${1 - inset} L${inset - cornerRadius},${1 - inset} M${inset},${1 - inset} L${inset},${1 - inset + cornerRadius}`);
|
|
274
|
+
}
|
|
275
|
+
// eslint-disable-next-line no-bitwise
|
|
276
|
+
if (segmentMask & SegmentBit.CORNER_TOP_LEFT) {
|
|
277
|
+
// Extend outward from top-left corner
|
|
278
|
+
paths.push(`M${inset},${inset} L${inset - cornerRadius},${inset} M${inset},${inset} L${inset},${inset - cornerRadius}`);
|
|
279
|
+
}
|
|
280
|
+
return paths.join(' ');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=CageLookupManager.js.map
|