BusWireSeparate Module
Functions and values
| Function or value | Description |
Full Usage:
adjustSegmentsInModel ori model lines
Parameters:
Orientation
model : Model
lines : Line list
Returns: bool * Model
|
|
Full Usage:
alignSameNetDepartures wiresToRoute model
Parameters:
ConnectionId list
model : Model
Returns: Model
|
Where two wires of a net run along a shared trunk and turn off it at different points, move one
turn onto the other so the net leaves the trunk once, as a T junction. The reader sees one
branch instead of two near-parallel ones, and the drawing is strictly shorter - the move is
only made when it is.
The candidate moved is a wire's interior perpendicular segment (the "riser"): its base slides
along the trunk to the other riser's position, its two neighbours stretching and shrinking to
pay for it, exactly as a segment drag would. Guards, in order:
- the riser is not a segment the user dragged, and its neighbours are interior and keep their
directions (a neighbour driven past zero would fold the wire back over itself). A wire with
hand-routed segments is an ordinary member of its net here: what it may not do is move the
segments that were pinned;
- the riser at its new position stays at least minWireSeparation clear of every
same-orientation segment of any OTHER net it would run beside - the one thing separation
cannot repair afterwards, since no later pass runs;
- it also stays clear of every symbol: the merged riser can reach further along its axis than
the riser it joins, into space nothing has checked.
Of the two ways round (move A to B, move B to A) the one leaving less visible wire wins, and a
move is only made when the net's drawn length strictly falls - which is also what guarantees
the repeated scan terminates.
|
|
|
|
Function which given a cluster (loc) works out how to spread out the contained segments optimally, spacing them from other segments and symbols. Return value is a list of segments, represented as Lines, paired with where they move. lines is the source list of lines (vertical or horizontal according to which is being processed). model is the Buswire model needed to access wires. |
Full Usage:
checkExtensionNoCrossings overlap ext excludedWire info
Parameters:
float
ext : Extension
excludedWire : ConnectionId
info : LineInfo
Returns: bool
|
Return true if the extension crosses nothing it should not: no symbol boundary, and no segment of another net. The other-net half is what stops corner removal undoing separation. A corner is removed by extending a segment to where the removed pair used to reach, which can be a long way - on 3cpu/datapath it was 47 units - and the segment lands wherever the corner happened to be rather than where separation put it. Separation has already ordered that cluster to minimise crossings; a removal which crosses another net is undoing that work for the sake of two fewer bends, and the bends are the cheaper thing. Same-net crossings are not refused: one signal crossing itself misleads nobody, and a net which fans out will do it. `lines` are of the opposite orientation to the extension, so a crossing is one of them spanning the extension's P while the extension spans its own.
|
Full Usage:
checkExtensionNoOverlap overlap ext excludedWire info
Parameters:
float
ext : Extension
excludedWire : ConnectionId
info : LineInfo
Returns: bool
|
|
|
Symbol clearance missing from a sheet: for every segment separation is able to move, and every symbol that segment runs alongside, how far short of clearanceFromSymbol it stands. This is the half of separation's objective that length cannot express. Spreading segments and standing them clear of symbols both make the drawing LONGER, so a cost function counting only wire would refuse every separation which had no overlap to pay for it - and did, leaving wires hugging the symbols they arrive at on any sheet whose nets do not cross. A segment is charged for the WORST symbol it runs beside, not the sum over all of them, and not scaled by how much of it runs alongside. What a reader notices is that a wire is touching a symbol; neither how long the touch is nor how many symbols it repeats against changes the answer to "should this segment move". Summing over symbols instead makes the term grow with symbol density, so on a dense sheet clearance outranks everything else the cost measures - and it duly did: summing cost reg16x8 nine crossings and 600 units of wire after a drag, and pushed wrappedArrays onto the worse of its two phases. Taking the worst symbol leaves every corpus sheet exactly where it was. The two symbols a wire connects to are NOT exempt, unlike in adjustSegmentsInModel. A wire must be allowed to touch its own symbol, but that is what the nub is for, and the nub - with the zero-length segment beside it and everything else separation cannot move - is excluded here anyway. The riser that arrives at a port hugging that symbol's edge is exactly what this is meant to see. Where a wire legitimately runs INSIDE its own symbol's box (a mux SEL port sits in from the trapezoid edge) the full clearance is charged and no round can remove it, which costs nothing: the acceptance rule compares two layouts of one sheet, and a charge present in both cancels.
|
Full Usage:
expandCluster index searchDir lines
Parameters:
int
searchDir : LocSearchDir
lines : Line array
Returns: Cluster
|
|
|
Return the index of the Line with the smallest value of P > p Use binary earch for speed. The search narrows towards `lines[below].P < p <= lines[above].P`, so the bottom end has to be checked before it starts: with below = 0 taken on trust, an array whose first line is already at or above p returns 1 and the caller never looks at line 0. If every line is below p the last index is returned, which callers detect for themselves.
|
|
|
Full Usage:
isSegmentExtensionOk info wire segNum ori startShift newLength
Parameters:
LineInfo
wire : Wire
segNum : int
ori : Orientation
startShift : float
newLength : float
Returns: bool
|
Return true if the given segment length change is allowed. If the new segment creates a part line segment that did not previouly exist this is checked for overlap with symbols and other wires. startShift is how far the start of the segment itself moves along its own axis: zero when the segment keeps its start point, negative when it is extended backwards (as the second segment of a removed corner is, since the segment before it has gone).
|
|
|
|
|
|
Where two segments in lines are on the same Net and on top of each other we must NEVER separate them. This function links such segments, and marks all except the head one as a LINKEDSEG so that the clustering algorithm will ignore them. sameNetCapture specified how close segments muts be to be linked.
|
|
Scan through segments in P order creating a list of local Clusters. Within one cluster segments are adjacent and overlapping. Note that different clusters may occupy the same P values if their segments do not overlap. Segments within each cluster will be repositioned and reordered after clusters are identified. Every segment must be part of a unique cluster.
|
Full Usage:
makeLineInfo wiresToRoute model
Parameters:
ConnectionId list
model : Model
Returns: LineInfo
|
Process the symbols and wires in Model generating arrays of Horizontal and Vertical lines. In addition the inverse map is generated which can map each segmnet to the corresponding Line if that exists. Note that Lines reference segments, which contain wire Id and segment Index and can therefore be used to reference the corresponding wire via the model.Wires map.
|
Full Usage:
makeLines wiresToRoute ori model
Parameters:
ConnectionId list
ori : Orientation
model : Model
Returns: Line array
|
|
|
|
|
|
|
|
|
|
|
Take the routing off the wires `toRedraw` selects, route them all again from nothing, and then separate the whole sheet as usual. Neither pass is changed: this is the ordinary pair of them, applied to many wires at once instead of to the few a drag reaches. That is what makes it worth having - a sheet laid out before a routing change keeps most of its old routing, and so says almost nothing about whether the change helped. The routing comes off every one of them BEFORE any of them is routed. Routing looks only at symbols today, so that makes no difference yet; it will as soon as a wire is routed with any regard for the wires already there, since a route which is about to be thrown away is not something the next wire should be following. Wires are reset rather than deleted and recreated: a wire is identified by the two ports it joins, so deleting one would only lose its ConnectionId, which the saved file, undo and the current selection all refer to. They are routed shortest first, by the straight-line distance between the two ports. Order is immaterial while routing considers only symbols, and this is the order to have when it stops being: a short wire has the least freedom in where it can go, so it is the one that should already be there when a longer wire of the same net is routed and looking for something to join. The re-route this replaces went in Map order - by ConnectionId, which is a GUID, so in no order at all and not the same one twice.
|
|
|
Full Usage:
removeModelCorners wires model
Parameters:
ConnectionId list
model : Model
Returns: Model
|
|
|
|
|
|
Full Usage:
routeAndSeparateSymbolWires model compId
Parameters:
Model
compId : ComponentId
Returns: Model
|
Route and then separate after one symbol has changed - moved a step, rotated, scaled, flipped or had its type edited. Uses partial routing to keep the symbol's hand-routed wires attached - a changed port position must reach them, and the redraw below deliberately leaves manual wires alone. A wire with both ends on the symbol is re-routed from each end in turn rather than translated, since the symbol may have been resized or rotated, not just moved. It ends as a drag does: every floating wire re-routed from scratch and the whole sheet separated. A wire that had been ROUTED AROUND this symbol is not connected to it, so no re-route of the symbol's own wires reaches it, and its detour is routing's - separation cannot undo it. The layout left behind is exactly what "redraw floating wires" would produce, so a symbol change leaves nothing for a redraw to improve.
|
Full Usage:
segmentToLine lType ori wire seg
Parameters:
LType
ori : Orientation
wire : Wire
seg : ASegment
Returns: Line
|
|
Full Usage:
separateAndOrderModelSegments wiresToRoute model
Parameters:
ConnectionId list
model : Model
Returns: Model
|
Perform complete wire segment separation and ordering for all orientations. `wiresToRoute` is the SCOPE: only clusters holding one of these wires are touched, so a small list is a local adjustment and every wire id is a whole-sheet pass. Which to use follows from what changed. Adding one wire, or dragging a segment of one, disturbs only the clusters that wire runs through - the rest of the sheet was settled a moment ago and owes nothing to this change. Moving a SYMBOL is different: its wires may leave clusters anywhere on the sheet, and the space they vacate is space other wires should take up, so a drag's mouse-up passes every wire (see the MovingSymbols cases in SheetUpdateHelpers, and routeAndSeparateSymbolWires). An empty list means nothing changed, and the whole pass is skipped.
|
Full Usage:
separateFixedSegments wiresToRoute ori model
Parameters:
ConnectionId list
ori : Orientation
model : Model
Returns: Model
|
Segments which could be moved, but would make an extra segment if moved, are marked Fixed and not moved by the normal cluster-based separation functions. This function looks at these segments and moves them a little in the special case that they overlap. It is called after the main segment separation is complete.
|
|
Separate the sheet with the hand-routed segments freed, then pin them again where they end up. **Nothing here is re-routed.** Separation moves a segment along its own axis and no further, so a wire that was dragged into a particular shape keeps that shape; what changes is which of the parallel tracks each of its segments sits on. That is the thing hand routing cannot do for itself - the user can put a wire where they want it, but not line it up with the ten other wires that run beside it, because those move afterwards and it does not. It is a menu item rather than part of the ordinary pass because it is a judgement the user has to make. A pinned segment is pinned precisely because separation would otherwise have put it somewhere else, so doing this on every edit would quietly undo hand routing a little at a time. Asked for once, it does what the user means: "these are in the right place, now tidy them in with the rest". The pins are restored by segment index, and only where the wire still has the same number of segments - corner and spike removal inside the separation pass can shorten a wire's list, and a pin put back at an index that now means a different segment would be worse than no pin at all.
|
Full Usage:
separateModelSegmentsOneOrientation wiresToRoute ori model
Parameters:
ConnectionId list
ori : Orientation
model : Model
Returns: bool * Model
|
Perform complete segment ordering and separation for segments of given orientation. wiresToRoute: the clusters worked on are those holding at least one of these wires. Lines are generated for ALL wires either way - a cluster is defined by everything near it - and passing every wire id makes this a whole-sheet pass.
|
Full Usage:
turnDirs line wires
Parameters:
Line
wires : Map<ConnectionId, Wire>
Returns: int * int
|
|
Full Usage:
turnLengths line wires
Parameters:
Line
wires : Map<ConnectionId, Wire>
Returns: float * float
|
|
Full Usage:
updateWireSegmentJumpsAndSeparations wires model
Parameters:
ConnectionId list
model : Model
Returns: Model
|
|
|
Report the rare case of a segment getting "orphaned" - left out of every cluster. This should probably never happen, and should be fixed if it is ever seen. Once per key, not once per occurrence: this is reached from the separation scan, which runs on every symbol move, so an unconditional complaint here would arrive at drag rate. |
|
How bad a wiring is, in three terms: the length of wire actually drawn, a heavy penalty for two different nets drawn on top of each other, and what clearance the wires are short of. Wire drawn is the length of the UNION of the segments on each line of the drawing, so two segments of one net lying on top of each other are one wire and are counted once. That makes "keep wires short" and "let a net share a trunk" the same objective rather than two which have to be traded off by hand. The clearance term is what makes this a score for SEPARATION rather than for routing. The other two are both minimised by the drawing separation starts from: spreading segments apart and standing them clear of symbols costs wire and removes no overlap, so a length-only cost refuses every round of it that has nothing else to show, and a sheet whose nets never cross was left exactly as routing drew it - wires hugging the symbols they arrive at. A cost function used to accept or reject a pass has to measure what that pass is for. This is called once per settling round: one sort and one sweep for the first two terms, one pass over segments against symbol boxes for the third. Deliberately not built on makeLines, which links same-net lines pairwise and costs as much as a separation pass.
|