(from: ConvertPathNode, to: ConvertPathNode, simpleMode: boolean)
| 332 | } |
| 333 | |
| 334 | public async* searchPath(from: ConvertPathNode, to: ConvertPathNode, simpleMode: boolean) : AsyncGenerator<ConvertPathNode[]> { |
| 335 | // Dijkstra's algorithm |
| 336 | // Priority queue of {index, cost, path} |
| 337 | let queue: PriorityQueue<QueueNode> = new PriorityQueue<QueueNode>( |
| 338 | 1000, |
| 339 | (a: QueueNode, b: QueueNode) => a.cost - b.cost |
| 340 | ); |
| 341 | let visited = new Array<number>(); |
| 342 | const fromIdentifier = from.format.mime + `(${from.format.format})`; |
| 343 | const toIdentifier = to.format.mime + `(${to.format.format})`; |
| 344 | let fromIndex = this.nodes.findIndex(node => node.identifier === fromIdentifier); |
| 345 | let toIndex = this.nodes.findIndex(node => node.identifier === toIdentifier); |
| 346 | if (fromIndex === -1 || toIndex === -1) return []; // If either format is not in the graph, return empty array |
| 347 | queue.add({index: fromIndex, cost: 0, path: [from], visitedBorder: visited.length }); |
| 348 | console.log(`Starting path search from ${from.format.mime}(${from.handler?.name}) to ${to.format.mime}(${to.handler?.name}) (simple mode: ${simpleMode})`); |
| 349 | let iterations = 0; |
| 350 | let pathsFound = 0; |
| 351 | while (queue.size() > 0) { |
| 352 | iterations++; |
| 353 | // Get the node with the lowest cost |
| 354 | let current = queue.poll()!; |
| 355 | const indexInVisited = visited.indexOf(current.index); |
| 356 | if (indexInVisited >= 0 && indexInVisited < current.visitedBorder) { |
| 357 | this.dispatchEvent("skipped", current.path); |
| 358 | continue; |
| 359 | } |
| 360 | if (current.index === toIndex) { |
| 361 | // Return the path of handlers and formats to get from the input format to the output format |
| 362 | const logString = `${iterations} with cost ${current.cost.toFixed(3)}: ${current.path.map(p => p.handler.name + "(" + p.format.mime + ")").join(" → ")}`; |
| 363 | const foundPathLast = current.path.at(-1); |
| 364 | if (simpleMode || !to.handler || to.handler.name === foundPathLast?.handler.name) { |
| 365 | console.log(`Found path at iteration ${logString}`); |
| 366 | this.dispatchEvent("found", current.path); |
| 367 | yield current.path; |
| 368 | pathsFound++; |
| 369 | } |
| 370 | else { |
| 371 | console.log(`Unvalid path at iteration ${logString}`); |
| 372 | this.dispatchEvent("skipped", current.path); |
| 373 | } |
| 374 | continue; |
| 375 | } |
| 376 | visited.push(current.index); |
| 377 | this.dispatchEvent("searching", current.path); |
| 378 | this.nodes[current.index].edges.forEach(edgeIndex => { |
| 379 | let edge = this.edges[edgeIndex]; |
| 380 | const indexInVisited = visited.indexOf(edge.to.index); |
| 381 | if (indexInVisited >= 0 && indexInVisited < current.visitedBorder) return; |
| 382 | const handler = this.handlers.find(h => h.name === edge.handler); |
| 383 | if (!handler) return; // If the handler for this edge is not found, skip it |
| 384 | |
| 385 | let path = current.path.concat({handler: handler, format: edge.to.format}); |
| 386 | queue.add({ |
| 387 | index: edge.to.index, |
| 388 | cost: current.cost + edge.cost + this.calculateAdaptiveCost(path), |
| 389 | path: path, |
| 390 | visitedBorder: visited.length |
| 391 | }); |
no test coverage detected