snarled/snarl/tracker.py

98 lines
3.0 KiB
Python

from typing import List, Set, ClassVar, Optional
from collections import defaultdict
from dataclasses import dataclass
from .types import layer_t, contour_t
class NetName:
name: Optional[str]
subname: int
count: ClassVar[defaultdict[Optional[str], int]] = defaultdict(int)
def __init__(self, name: Optional[str] = None) -> None:
self.name = name
self.subname = self.count[name]
NetName.count[name] += 1
def __lt__(self, other: 'NetName') -> bool:
if self.name == other.name:
return self.subname < other.subname
elif self.name is None:
return False
elif other.name is None:
return True
else:
return self.name < other.name
def __repr__(self) -> str:
if self.name is not None:
name = self.name
else:
name = '(None)'
if NetName.count[self.name] == 1:
return name
else:
return f'{name}__{self.subname}'
class NetsInfo:
nets: defaultdict[NetName, defaultdict[layer_t, List]]
net_aliases: defaultdict[NetName, NetName]
def __init__(self) -> None:
self.nets = defaultdict(lambda: defaultdict(list))
self.net_aliases = defaultdict(list)
def resolve_name(self, net_name: NetName) -> NetName:
while net_name in self.net_aliases:
net_name = self.net_aliases[net_name]
return net_name
def merge(self, net_a: NetName, net_b: NetName) -> None:
net_a = self.resolve_name(net_a)
net_b = self.resolve_name(net_b)
# Always keep named nets if the other is anonymous
keep_net, old_net = sorted((net_a, net_b))
#logger.info(f'merging {old_net} into {keep_net}')
self.net_aliases[old_net] = keep_net
if old_net in self.nets:
for layer in self.nets[old_net]:
self.nets[keep_net][layer] += self.nets[old_net][layer]
del self.nets[old_net]
def get(self, net: NetName, layer: layer_t) -> List[contour_t]:
return self.nets[self.resolve_name(net)][layer]
def get_shorted_nets(self) -> List[Set[NetName]]:
shorts = defaultdict(list)
for kk in self.net_aliases:
if kk.name is None:
continue
base_name = self.resolve_name(kk)
assert(base_name.name is not None)
shorts[base_name].append(kk)
shorted_sets = [set([kk] + others)
for kk, others in shorts.items()]
return shorted_sets
def get_open_nets(self) -> defaultdict[str, List[NetName]]:
opens = defaultdict(list)
seen_names = {}
for kk in self.nets:
if kk.name is None:
continue
if kk.name in seen_names:
if kk.name not in opens:
opens[kk.name].append(seen_names[kk.name])
opens[kk.name].append(kk)
else:
seen_names[kk.name] = kk
return opens