* Initializes the traversion graph based on the supported formats and handlers. This should be called after all handlers have been registered and their supported formats have been cached in window.supportedFormatCache. The graph is built by creating nodes for each unique file format and edges for ea
(supportedFormatCache: Map<string, FileFormat[]>, handlers: FormatHandler[], strictCategories: boolean = false)
| 134 | * @param strictCategories If true, the algorithm will apply category change costs more strictly, even when formats share categories. This can lead to more accurate pathfinding at the cost of potentially longer paths and increased search time. If false, category change costs will only be applied when formats do not share any categories, allowing for more flexible pathfinding that may yield shorter paths but with less nuanced cost calculations. |
| 135 | */ |
| 136 | public init(supportedFormatCache: Map<string, FileFormat[]>, handlers: FormatHandler[], strictCategories: boolean = false) { |
| 137 | this.handlers = handlers; |
| 138 | this.nodes.length = 0; |
| 139 | this.edges.length = 0; |
| 140 | |
| 141 | console.log("Initializing traversion graph..."); |
| 142 | const startTime = performance.now(); |
| 143 | |
| 144 | let handlerIndex = 0; |
| 145 | supportedFormatCache.forEach((formats, handler) => { |
| 146 | let fromIndices: Array<{format: FileFormat, index: number}> = []; |
| 147 | let toIndices: Array<{format: FileFormat, index: number}> = []; |
| 148 | formats.forEach(format => { |
| 149 | const formatIdentifier = format.mime + `(${format.format})`; |
| 150 | let index = this.nodes.findIndex(node => node.identifier === formatIdentifier); |
| 151 | if (index === -1) { |
| 152 | index = this.nodes.length; |
| 153 | this.nodes.push({ |
| 154 | identifier: formatIdentifier, |
| 155 | format: format, |
| 156 | edges: [] |
| 157 | }); |
| 158 | } |
| 159 | if (format.from) fromIndices.push({format, index}); |
| 160 | if (format.to) toIndices.push({format, index}); |
| 161 | }); |
| 162 | fromIndices.forEach(from => { |
| 163 | toIndices.forEach(to => { |
| 164 | if (from.index === to.index) return; // No self-loops |
| 165 | this.edges.push({ |
| 166 | from: from, |
| 167 | to: to, |
| 168 | handler: handler, |
| 169 | cost: this.costFunction( |
| 170 | from, |
| 171 | to, |
| 172 | strictCategories, |
| 173 | handler, |
| 174 | handlerIndex |
| 175 | ) |
| 176 | }); |
| 177 | this.nodes[from.index].edges.push(this.edges.length - 1); |
| 178 | }); |
| 179 | }); |
| 180 | handlerIndex++; |
| 181 | }); |
| 182 | |
| 183 | // Add edges for handlers with supportAnyInput. |
| 184 | // These handlers can accept any format as input, so we create edges |
| 185 | // from every known format node to their output formats. |
| 186 | handlers.forEach((handler, hIndex) => { |
| 187 | if (!handler.supportAnyInput) return; |
| 188 | const formats = supportedFormatCache.get(handler.name); |
| 189 | if (!formats) return; |
| 190 | |
| 191 | // Collect output format node indices for this handler |
| 192 | const toEntries: Array<{format: FileFormat, index: number}> = []; |
| 193 | for (const f of formats) { |
nothing calls this directly
no test coverage detected