Hi all, I’ve been working on a layout engine for drawing circuits in typst.
The library works for many things so far, but I found an issue that has less to do with the library and (I fear) has something to do with typst itself.
So as I lay out the circuit elements, I define nodes. Based on a fixed initial position of a sentinel node (GND) I calculate the positions of every connected* node with a breadth-first search.
Right now though, I’m looking to compare two nodes that are mathematically equivalent but aren’t comparable. The existing node existing: (x: 0pt, y: 0pt) conflicts with the new node calculated: (x: -0pt, y: 0pt). When I try to fix the comparison, i.e. if calculated.x == 0pt { calculated.x = 0pt } the conflict still happens, and the value of calculated.x is still -0pt. If I instead change this to a larger value it converts properly, so I know the path is being taken.
You can see the source here: Typst
I have a panic at line 298 where the conflict occurs.
There is a lot of code here, but almost all of it is helper functions to allow me to map dictionaries back into rotated or translated dictionaries, or work with coordinates as (x:...,y:...) instead of as lists. Anything that changes a node position is prefixed with nodemap, and anything that works with coordinates is prefixed with XY. I would really appreciate any help that can be provided, and I will happily answer any questions you all have.
The bit that demonstrates my problem looks like this:
#let place-nodes(start, start-pos, components) = {
let positions = pairs-dict(((start, start-pos),))
let queue = (start,)
let lines = ()
// determine node positions based on a Breadth-first traversal
while queue.len() > 0 {
let start-node = queue.pop()
for component in components {
// ...code omitted here that isn't relevant.
// only cover connected nodes
if start-node not in component.nodemap { continue }
let points = nodemap-place(component.nodemap, start-node, positions.at(start-node), component.dir)
for (name, xy) in points.pairs() {
// easy case
if (name not in positions) {
queue.push(name)
positions.insert(name, xy)
continue
}
let x1 = positions.at(name).x
let y1 = positions.at(name).y
let x2 = xy.x
let y2 = xy.y
if (x1 == x2 and y1 == y2) {
continue
}
// the panic that should be unreachable
panic("Conflict: " + name + " has x=" + repr(x1) + ",y=" + repr(y1) + ", calculated x=" + repr(x2) + ",y=" + repr(y2))
}
}
}
//... non-nodal computations omitted
*The version I’ve uploaded as a MVE only checks connected nodes, but the full version I have locally computes from any node with a fixed position, and additionally has support for wire drawing and about a dozen standard components.
