[AutoTool] add U-bend
This commit is contained in:
parent
8a45c6d8d6
commit
1070815730
2 changed files with 167 additions and 1 deletions
|
|
@ -264,6 +264,7 @@ class Tool:
|
|||
self,
|
||||
jog: float,
|
||||
*,
|
||||
length: float = 0,
|
||||
in_ptype: str | None = None,
|
||||
out_ptype: str | None = None,
|
||||
port_names: tuple[str, str] = ('A', 'B'),
|
||||
|
|
@ -597,6 +598,14 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||
b_transition: 'AutoTool.Transition | None'
|
||||
out_transition: 'AutoTool.Transition | None'
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class UData:
|
||||
""" Data for planU """
|
||||
ldata0: 'AutoTool.LData'
|
||||
ldata1: 'AutoTool.LData'
|
||||
straight2: 'AutoTool.Straight'
|
||||
l2_length: float
|
||||
|
||||
straights: list[Straight]
|
||||
""" List of straight-generators to choose from, in order of priority """
|
||||
|
||||
|
|
@ -942,6 +951,113 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||
self._renderS(data=data, tree=tree, port_names=port_names, gen_kwargs=kwargs)
|
||||
return tree
|
||||
|
||||
def planU(
|
||||
self,
|
||||
jog: float,
|
||||
*,
|
||||
length: float = 0,
|
||||
in_ptype: str | None = None,
|
||||
out_ptype: str | None = None,
|
||||
**kwargs,
|
||||
) -> tuple[Port, UData]:
|
||||
ccw = jog > 0
|
||||
kwargs_no_out = kwargs | {'out_ptype': None}
|
||||
|
||||
# Use loops to find a combination of straights and bends that fits
|
||||
success = False
|
||||
for _straight1 in self.straights:
|
||||
for _bend1 in self.bends:
|
||||
for straight2 in self.straights:
|
||||
for _bend2 in self.bends:
|
||||
try:
|
||||
# We need to know R1 and R2 to calculate the lengths.
|
||||
# Use large dummy lengths to probe the bends.
|
||||
p_probe1, _ = self.planL(ccw, 1e9, in_ptype=in_ptype, **kwargs_no_out)
|
||||
R1 = abs(Port((0, 0), 0).measure_travel(p_probe1)[0][1])
|
||||
p_probe2, _ = self.planL(ccw, 1e9, in_ptype=p_probe1.ptype, out_ptype=out_ptype, **kwargs)
|
||||
R2 = abs(Port((0, 0), 0).measure_travel(p_probe2)[0][1])
|
||||
|
||||
# Final x will be: x = l1_straight + R1 - R2
|
||||
# We want final x = length. So: l1_straight = length - R1 + R2
|
||||
# Total length for planL(0) is l1 = l1_straight + R1 = length + R2
|
||||
l1 = length + R2
|
||||
|
||||
# Final y will be: y = R1 + l2_straight + R2 = abs(jog)
|
||||
# So: l2_straight = abs(jog) - R1 - R2
|
||||
l2_length = abs(jog) - R1 - R2
|
||||
|
||||
if l2_length >= straight2.length_range[0] and l2_length < straight2.length_range[1]:
|
||||
p0, ldata0 = self.planL(ccw, l1, in_ptype=in_ptype, **kwargs_no_out)
|
||||
# For the second bend, we want straight length = 0.
|
||||
# Total length for planL(1) is l2 = 0 + R2 = R2.
|
||||
p1, ldata1 = self.planL(ccw, R2, in_ptype=p0.ptype, out_ptype=out_ptype, **kwargs)
|
||||
|
||||
success = True
|
||||
break
|
||||
except BuildError:
|
||||
continue
|
||||
if success:
|
||||
break
|
||||
if success:
|
||||
break
|
||||
if success:
|
||||
break
|
||||
|
||||
if not success:
|
||||
raise BuildError(f"AutoTool failed to plan U-turn with {jog=}, {length=}")
|
||||
|
||||
data = self.UData(ldata0, ldata1, straight2, l2_length)
|
||||
# Final port is at (length, jog) rot pi relative to input
|
||||
out_port = Port((length, jog), rotation=pi, ptype=p1.ptype)
|
||||
return out_port, data
|
||||
|
||||
def _renderU(
|
||||
self,
|
||||
data: UData,
|
||||
tree: ILibrary,
|
||||
port_names: tuple[str, str],
|
||||
gen_kwargs: dict[str, Any],
|
||||
) -> ILibrary:
|
||||
pat = tree.top_pattern()
|
||||
# 1. First L-bend
|
||||
self._renderL(data.ldata0, tree, port_names, gen_kwargs)
|
||||
# 2. Connecting straight
|
||||
if not numpy.isclose(data.l2_length, 0):
|
||||
s2_pat_or_tree = data.straight2.fn(data.l2_length, **(gen_kwargs | data.ldata0.straight_kwargs))
|
||||
pmap = {port_names[1]: data.straight2.in_port_name}
|
||||
if isinstance(s2_pat_or_tree, Pattern):
|
||||
pat.plug(s2_pat_or_tree, pmap, append=True)
|
||||
else:
|
||||
s2_tree = s2_pat_or_tree
|
||||
top = s2_tree.top()
|
||||
s2_tree.flatten(top, dangling_ok=True)
|
||||
pat.plug(s2_tree[top], pmap, append=True)
|
||||
# 3. Second L-bend
|
||||
self._renderL(data.ldata1, tree, port_names, gen_kwargs)
|
||||
return tree
|
||||
|
||||
def pathU(
|
||||
self,
|
||||
jog: float,
|
||||
*,
|
||||
length: float = 0,
|
||||
in_ptype: str | None = None,
|
||||
out_ptype: str | None = None,
|
||||
port_names: tuple[str, str] = ('A', 'B'),
|
||||
**kwargs,
|
||||
) -> Library:
|
||||
_out_port, data = self.planU(
|
||||
jog,
|
||||
length = length,
|
||||
in_ptype = in_ptype,
|
||||
out_ptype = out_ptype,
|
||||
**kwargs,
|
||||
)
|
||||
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'pathU')
|
||||
pat.add_port_pair(names=port_names, ptype='unk' if in_ptype is None else in_ptype)
|
||||
self._renderU(data=data, tree=tree, port_names=port_names, gen_kwargs=kwargs)
|
||||
return tree
|
||||
|
||||
def render(
|
||||
self,
|
||||
batch: Sequence[RenderStep],
|
||||
|
|
@ -959,6 +1075,8 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||
self._renderL(data=step.data, tree=tree, port_names=port_names, straight_kwargs=kwargs)
|
||||
elif step.opcode == 'S':
|
||||
self._renderS(data=step.data, tree=tree, port_names=port_names, gen_kwargs=kwargs)
|
||||
elif step.opcode == 'U':
|
||||
self._renderU(data=step.data, tree=tree, port_names=port_names, gen_kwargs=kwargs)
|
||||
return tree
|
||||
|
||||
|
||||
|
|
@ -1086,6 +1204,7 @@ class PathTool(Tool, metaclass=ABCMeta):
|
|||
port_rot = step.start_port.rotation
|
||||
# Masque convention: Port rotation points INTO the device.
|
||||
# So the direction of travel for the path is AWAY from the port, i.e., port_rot + pi.
|
||||
assert port_rot is not None
|
||||
|
||||
if step.opcode == 'L':
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue