Compare commits
3 Commits
1b79cd6f45
...
1faf5ccad5
| Author | SHA1 | Date | |
|---|---|---|---|
| 1faf5ccad5 | |||
| 3ba2ffd33f | |||
| 40e55a9067 |
@ -308,7 +308,7 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
|
|
||||||
if straight_length < 0:
|
if straight_length < 0:
|
||||||
raise BuildError(
|
raise BuildError(
|
||||||
f'Asked to draw path with total length {length:,g}, shorter than required bends ({bend_dxy[0]:,})'
|
f'Asked to draw L-path with total length {length:,g}, shorter than required bends ({bend_dxy[0]:,})'
|
||||||
)
|
)
|
||||||
|
|
||||||
data = self.LData(straight_length, kwargs, ccw)
|
data = self.LData(straight_length, kwargs, ccw)
|
||||||
@ -320,7 +320,6 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
data: LData,
|
data: LData,
|
||||||
tree: ILibrary,
|
tree: ILibrary,
|
||||||
port_names: tuple[str, str],
|
port_names: tuple[str, str],
|
||||||
append: bool,
|
|
||||||
straight_kwargs: dict[str, Any],
|
straight_kwargs: dict[str, Any],
|
||||||
) -> ILibrary:
|
) -> ILibrary:
|
||||||
"""
|
"""
|
||||||
@ -333,20 +332,12 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
pmap = {port_names[1]: sport_in}
|
pmap = {port_names[1]: sport_in}
|
||||||
if isinstance(straight_pat_or_tree, Pattern):
|
if isinstance(straight_pat_or_tree, Pattern):
|
||||||
straight_pat = straight_pat_or_tree
|
straight_pat = straight_pat_or_tree
|
||||||
if append:
|
|
||||||
pat.plug(straight_pat, pmap, append=True)
|
pat.plug(straight_pat, pmap, append=True)
|
||||||
else:
|
|
||||||
straight_name = tree <= {SINGLE_USE_PREFIX + 'straight': straight_pat}
|
|
||||||
pat.plug(straight_name, pmap)
|
|
||||||
else:
|
else:
|
||||||
straight_tree = straight_pat_or_tree
|
straight_tree = straight_pat_or_tree
|
||||||
if append:
|
|
||||||
top = straight_tree.top()
|
top = straight_tree.top()
|
||||||
straight_tree.flatten(top)
|
straight_tree.flatten(top)
|
||||||
pat.plug(straight_tree[top], pmap, append=True)
|
pat.plug(straight_tree[top], pmap, append=True)
|
||||||
else:
|
|
||||||
straight = tree <= straight_pat_or_tree
|
|
||||||
pat.plug(straight, pmap)
|
|
||||||
if data.ccw is not None:
|
if data.ccw is not None:
|
||||||
bend, bport_in, bport_out = self.bend
|
bend, bport_in, bport_out = self.bend
|
||||||
pat.plug(bend, {port_names[1]: bport_in}, mirrored=bool(data.ccw))
|
pat.plug(bend, {port_names[1]: bport_in}, mirrored=bool(data.ccw))
|
||||||
@ -371,7 +362,7 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
|
|
||||||
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.add_port_pair(names=port_names, ptype='unk' if in_ptype is None else in_ptype)
|
pat.add_port_pair(names=port_names, ptype='unk' if in_ptype is None else in_ptype)
|
||||||
self._renderL(data=data, tree=tree, port_names=port_names, append=False, straight_kwargs=kwargs)
|
self._renderL(data=data, tree=tree, port_names=port_names, straight_kwargs=kwargs)
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
def render(
|
def render(
|
||||||
@ -379,7 +370,6 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
batch: Sequence[RenderStep],
|
batch: Sequence[RenderStep],
|
||||||
*,
|
*,
|
||||||
port_names: tuple[str, str] = ('A', 'B'),
|
port_names: tuple[str, str] = ('A', 'B'),
|
||||||
append: bool = True,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> ILibrary:
|
) -> ILibrary:
|
||||||
|
|
||||||
@ -389,7 +379,7 @@ class SimpleTool(Tool, metaclass=ABCMeta):
|
|||||||
for step in batch:
|
for step in batch:
|
||||||
assert step.tool == self
|
assert step.tool == self
|
||||||
if step.opcode == 'L':
|
if step.opcode == 'L':
|
||||||
self._renderL(data=step.data, tree=tree, port_names=port_names, append=append, straight_kwargs=kwargs)
|
self._renderL(data=step.data, tree=tree, port_names=port_names, straight_kwargs=kwargs)
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -409,6 +399,20 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
out_port_name: str
|
out_port_name: str
|
||||||
length_range: tuple[float, float] = (0, numpy.inf)
|
length_range: tuple[float, float] = (0, numpy.inf)
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class SBend:
|
||||||
|
""" Description of an s-bend generator """
|
||||||
|
ptype: str
|
||||||
|
|
||||||
|
fn: Callable[[float], Pattern] | Callable[[float], Library]
|
||||||
|
"""
|
||||||
|
Generator function. `jog` (only argument) is assumed to be left (ccw) relative to travel
|
||||||
|
and may be negative for a jog i the opposite direction. Won't be called if jog=0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
in_port_name: str
|
||||||
|
out_port_name: str
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class Bend:
|
class Bend:
|
||||||
""" Description of a pre-rendered bend """
|
""" Description of a pre-rendered bend """
|
||||||
@ -455,11 +459,26 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
b_transition: 'AutoTool.Transition | None'
|
b_transition: 'AutoTool.Transition | None'
|
||||||
out_transition: 'AutoTool.Transition | None'
|
out_transition: 'AutoTool.Transition | None'
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class SData:
|
||||||
|
""" Data for planS """
|
||||||
|
straight_length: float
|
||||||
|
straight: 'AutoTool.Straight'
|
||||||
|
gen_kwargs: dict[str, Any]
|
||||||
|
jog_remaining: float
|
||||||
|
sbend: 'AutoTool.SBend'
|
||||||
|
in_transition: 'AutoTool.Transition | None'
|
||||||
|
b_transition: 'AutoTool.Transition | None'
|
||||||
|
out_transition: 'AutoTool.Transition | None'
|
||||||
|
|
||||||
straights: list[Straight]
|
straights: list[Straight]
|
||||||
""" List of straight-generators to choose from, in order of priority """
|
""" List of straight-generators to choose from, in order of priority """
|
||||||
|
|
||||||
bends: list[Bend]
|
bends: list[Bend]
|
||||||
""" List of bends to choose from, in order of priority. """
|
""" List of bends to choose from, in order of priority """
|
||||||
|
|
||||||
|
sbends: list[SBend]
|
||||||
|
""" List of S-bend generators to choose from, in order of priority """
|
||||||
|
|
||||||
transitions: dict[tuple[str, str], Transition]
|
transitions: dict[tuple[str, str], Transition]
|
||||||
""" `{(external_ptype, internal_ptype): Transition, ...}` """
|
""" `{(external_ptype, internal_ptype): Transition, ...}` """
|
||||||
@ -491,6 +510,20 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
bend_angle *= -1
|
bend_angle *= -1
|
||||||
return bend_dxy, bend_angle
|
return bend_dxy, bend_angle
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sbend2dxy(sbend: SBend, jog: float) -> NDArray[numpy.float64]:
|
||||||
|
if numpy.isclose(jog, 0):
|
||||||
|
return numpy.zeros(2)
|
||||||
|
|
||||||
|
sbend_pat_or_tree = sbend.fn(jog)
|
||||||
|
sbpat = sbend_pat_or_tree if isinstance(sbend_pat_or_tree, Pattern) else sbend_pat_or_tree.top_pattern()
|
||||||
|
|
||||||
|
angle_in = sbpat[sbend.in_port_name].rotation
|
||||||
|
assert angle_in is not None
|
||||||
|
|
||||||
|
dxy = rotation_matrix_2d(-angle_in) @ (sbpat[sbend.out_port_name].offset - sbpat[sbend.in_port_name].offset)
|
||||||
|
return dxy
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _itransition2dxy(in_transition: Transition | None) -> NDArray[numpy.float64]:
|
def _itransition2dxy(in_transition: Transition | None) -> NDArray[numpy.float64]:
|
||||||
if in_transition is None:
|
if in_transition is None:
|
||||||
@ -550,7 +583,7 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
else:
|
else:
|
||||||
# Failed to break
|
# Failed to break
|
||||||
raise BuildError(
|
raise BuildError(
|
||||||
f'Asked to draw path with total length {length:,g}, shorter than required bends and transitions:\n'
|
f'Asked to draw L-path with total length {length:,g}, shorter than required bends and transitions:\n'
|
||||||
f'bend: {bend_dxy[0]:,g} in_trans: {itrans_dxy[0]:,g}\n'
|
f'bend: {bend_dxy[0]:,g} in_trans: {itrans_dxy[0]:,g}\n'
|
||||||
f'out_trans: {otrans_dxy[0]:,g} bend_trans: {btrans_dxy[0]:,g}'
|
f'out_trans: {otrans_dxy[0]:,g} bend_trans: {btrans_dxy[0]:,g}'
|
||||||
)
|
)
|
||||||
@ -571,7 +604,6 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
data: LData,
|
data: LData,
|
||||||
tree: ILibrary,
|
tree: ILibrary,
|
||||||
port_names: tuple[str, str],
|
port_names: tuple[str, str],
|
||||||
append: bool,
|
|
||||||
straight_kwargs: dict[str, Any],
|
straight_kwargs: dict[str, Any],
|
||||||
) -> ILibrary:
|
) -> ILibrary:
|
||||||
"""
|
"""
|
||||||
@ -584,21 +616,12 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
straight_pat_or_tree = data.straight.fn(data.straight_length, **(straight_kwargs | data.straight_kwargs))
|
straight_pat_or_tree = data.straight.fn(data.straight_length, **(straight_kwargs | data.straight_kwargs))
|
||||||
pmap = {port_names[1]: data.straight.in_port_name}
|
pmap = {port_names[1]: data.straight.in_port_name}
|
||||||
if isinstance(straight_pat_or_tree, Pattern):
|
if isinstance(straight_pat_or_tree, Pattern):
|
||||||
straight_pat = straight_pat_or_tree
|
pat.plug(straight_pat_or_tree, pmap, append=True)
|
||||||
if append:
|
|
||||||
pat.plug(straight_pat, pmap, append=True)
|
|
||||||
else:
|
|
||||||
straight_name = tree <= {SINGLE_USE_PREFIX + 'straight': straight_pat}
|
|
||||||
pat.plug(straight_name, pmap)
|
|
||||||
else:
|
else:
|
||||||
straight_tree = straight_pat_or_tree
|
straight_tree = straight_pat_or_tree
|
||||||
if append:
|
|
||||||
top = straight_tree.top()
|
top = straight_tree.top()
|
||||||
straight_tree.flatten(top)
|
straight_tree.flatten(top)
|
||||||
pat.plug(straight_tree[top], pmap, append=True)
|
pat.plug(straight_tree[top], pmap, append=True)
|
||||||
else:
|
|
||||||
straight = tree <= straight_pat_or_tree
|
|
||||||
pat.plug(straight, pmap)
|
|
||||||
if data.b_transition:
|
if data.b_transition:
|
||||||
pat.plug(data.b_transition.abstract, {port_names[1]: data.b_transition.our_port_name})
|
pat.plug(data.b_transition.abstract, {port_names[1]: data.b_transition.our_port_name})
|
||||||
if data.ccw is not None:
|
if data.ccw is not None:
|
||||||
@ -627,7 +650,123 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
|
|
||||||
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.add_port_pair(names=port_names, ptype='unk' if in_ptype is None else in_ptype)
|
pat.add_port_pair(names=port_names, ptype='unk' if in_ptype is None else in_ptype)
|
||||||
self._renderL(data=data, tree=tree, port_names=port_names, append=False, straight_kwargs=kwargs)
|
self._renderL(data=data, tree=tree, port_names=port_names, straight_kwargs=kwargs)
|
||||||
|
return tree
|
||||||
|
|
||||||
|
def planS(
|
||||||
|
self,
|
||||||
|
length: float,
|
||||||
|
jog: float,
|
||||||
|
*,
|
||||||
|
in_ptype: str | None = None,
|
||||||
|
out_ptype: str | None = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> tuple[Port, Any]:
|
||||||
|
|
||||||
|
success = False
|
||||||
|
for straight in self.straights:
|
||||||
|
for sbend in self.sbends:
|
||||||
|
out_ptype_pair = (
|
||||||
|
'unk' if out_ptype is None else out_ptype,
|
||||||
|
straight.ptype if numpy.isclose(jog, 0) else sbend.ptype
|
||||||
|
)
|
||||||
|
out_transition = self.transitions.get(out_ptype_pair, None)
|
||||||
|
otrans_dxy = self._otransition2dxy(out_transition, pi)
|
||||||
|
|
||||||
|
# Assume we'll need a straight segment with transitions, then discard them if they don't fit
|
||||||
|
# We do this before generating the s-bend because the transitions might have some dy component
|
||||||
|
in_ptype_pair = ('unk' if in_ptype is None else in_ptype, straight.ptype)
|
||||||
|
in_transition = self.transitions.get(in_ptype_pair, None)
|
||||||
|
itrans_dxy = self._itransition2dxy(in_transition)
|
||||||
|
|
||||||
|
b_transition = None
|
||||||
|
if not numpy.isclose(jog, 0) and sbend.ptype != straight.ptype:
|
||||||
|
b_transition = self.transitions.get((sbend.ptype, straight.ptype), None)
|
||||||
|
btrans_dxy = self._itransition2dxy(b_transition)
|
||||||
|
|
||||||
|
if length > itrans_dxy[0] + btrans_dxy[0] + otrans_dxy[0]:
|
||||||
|
# `if` guard to avoid unnecessary calls to `_sbend2dxy()`, which calls `sbend.fn()`
|
||||||
|
# note some S-bends may have 0 length, so we can't be more restrictive
|
||||||
|
jog_remaining = jog - itrans_dxy[1] - btrans_dxy[1] - otrans_dxy[1]
|
||||||
|
sbend_dxy = self._sbend2dxy(sbend, jog_remaining)
|
||||||
|
straight_length = length - sbend_dxy[0] - itrans_dxy[0] - btrans_dxy[0] - otrans_dxy[0]
|
||||||
|
success = straight.length_range[0] <= straight_length < straight.length_range[1]
|
||||||
|
if success:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Straight didn't work, see if just the s-bend is enough
|
||||||
|
if sbend.ptype != straight.ptype:
|
||||||
|
# Need to use a different in-transition for sbend (vs straight)
|
||||||
|
in_ptype_pair = ('unk' if in_ptype is None else in_ptype, sbend.ptype)
|
||||||
|
in_transition = self.transitions.get(in_ptype_pair, None)
|
||||||
|
itrans_dxy = self._itransition2dxy(in_transition)
|
||||||
|
|
||||||
|
jog_remaining = jog - itrans_dxy[1] - otrans_dxy[1]
|
||||||
|
sbend_dxy = self._sbend2dxy(sbend, jog_remaining)
|
||||||
|
success = numpy.isclose(length, sbend_dxy[0] + itrans_dxy[1] + otrans_dxy[1])
|
||||||
|
if success:
|
||||||
|
b_transition = None
|
||||||
|
straight_length = 0
|
||||||
|
break
|
||||||
|
if success:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Failed to break
|
||||||
|
raise BuildError(
|
||||||
|
f'Asked to draw S-path with total length {length:,g}, shorter than required bends and transitions:\n'
|
||||||
|
f'sbend: {sbend_dxy[0]:,g} in_trans: {itrans_dxy[0]:,g}\n'
|
||||||
|
f'out_trans: {otrans_dxy[0]:,g} bend_trans: {btrans_dxy[0]:,g}'
|
||||||
|
)
|
||||||
|
|
||||||
|
if out_transition is not None:
|
||||||
|
out_ptype_actual = out_transition.their_port.ptype
|
||||||
|
elif not numpy.isclose(jog_remaining, 0):
|
||||||
|
out_ptype_actual = sbend.ptype
|
||||||
|
else:
|
||||||
|
out_ptype_actual = self.default_out_ptype
|
||||||
|
|
||||||
|
data = self.SData(straight_length, straight, kwargs, jog_remaining, sbend, in_transition, b_transition, out_transition)
|
||||||
|
out_port = Port((length, jog), rotation=pi, ptype=out_ptype_actual)
|
||||||
|
return out_port, data
|
||||||
|
|
||||||
|
def _renderS(
|
||||||
|
self,
|
||||||
|
data: SData,
|
||||||
|
tree: ILibrary,
|
||||||
|
port_names: tuple[str, str],
|
||||||
|
gen_kwargs: dict[str, Any],
|
||||||
|
) -> ILibrary:
|
||||||
|
"""
|
||||||
|
Render an L step into a preexisting tree
|
||||||
|
"""
|
||||||
|
pat = tree.top_pattern()
|
||||||
|
if data.in_transition:
|
||||||
|
pat.plug(data.in_transition.abstract, {port_names[1]: data.in_transition.their_port_name})
|
||||||
|
if not numpy.isclose(data.straight_length, 0):
|
||||||
|
straight_pat_or_tree = data.straight.fn(data.straight_length, **(gen_kwargs | data.gen_kwargs))
|
||||||
|
pmap = {port_names[1]: data.straight.in_port_name}
|
||||||
|
if isinstance(straight_pat_or_tree, Pattern):
|
||||||
|
straight_pat = straight_pat_or_tree
|
||||||
|
pat.plug(straight_pat, pmap, append=True)
|
||||||
|
else:
|
||||||
|
straight_tree = straight_pat_or_tree
|
||||||
|
top = straight_tree.top()
|
||||||
|
straight_tree.flatten(top)
|
||||||
|
pat.plug(straight_tree[top], pmap, append=True)
|
||||||
|
if data.b_transition:
|
||||||
|
pat.plug(data.b_transition.abstract, {port_names[1]: data.b_transition.our_port_name})
|
||||||
|
if not numpy.isclose(data.jog_remaining, 0):
|
||||||
|
sbend_pat_or_tree = data.sbend.fn(data.jog_remaining, **(gen_kwargs | data.gen_kwargs))
|
||||||
|
pmap = {port_names[1]: data.sbend.in_port_name}
|
||||||
|
if isinstance(sbend_pat_or_tree, Pattern):
|
||||||
|
pat.plug(sbend_pat_or_tree, pmap, append=True)
|
||||||
|
else:
|
||||||
|
sbend_tree = sbend_pat_or_tree
|
||||||
|
top = sbend_tree.top()
|
||||||
|
sbend_tree.flatten(top)
|
||||||
|
pat.plug(sbend_tree[top], pmap, append=True)
|
||||||
|
if data.out_transition:
|
||||||
|
pat.plug(data.out_transition.abstract, {port_names[1]: data.out_transition.our_port_name})
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
def render(
|
def render(
|
||||||
@ -635,7 +774,6 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
batch: Sequence[RenderStep],
|
batch: Sequence[RenderStep],
|
||||||
*,
|
*,
|
||||||
port_names: tuple[str, str] = ('A', 'B'),
|
port_names: tuple[str, str] = ('A', 'B'),
|
||||||
append: bool = True,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> ILibrary:
|
) -> ILibrary:
|
||||||
|
|
||||||
@ -645,7 +783,9 @@ class AutoTool(Tool, metaclass=ABCMeta):
|
|||||||
for step in batch:
|
for step in batch:
|
||||||
assert step.tool == self
|
assert step.tool == self
|
||||||
if step.opcode == 'L':
|
if step.opcode == 'L':
|
||||||
self._renderL(data=step.data, tree=tree, port_names=port_names, append=append, straight_kwargs=kwargs)
|
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)
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
@ -740,7 +880,7 @@ class PathTool(Tool, metaclass=ABCMeta):
|
|||||||
|
|
||||||
if straight_length < 0:
|
if straight_length < 0:
|
||||||
raise BuildError(
|
raise BuildError(
|
||||||
f'Asked to draw path with total length {length:,g}, shorter than required bend: {bend_dxy[0]:,g}'
|
f'Asked to draw L-path with total length {length:,g}, shorter than required bend: {bend_dxy[0]:,g}'
|
||||||
)
|
)
|
||||||
data = numpy.array((length, bend_run))
|
data = numpy.array((length, bend_run))
|
||||||
out_port = Port(data, rotation=bend_angle, ptype=self.ptype)
|
out_port = Port(data, rotation=bend_angle, ptype=self.ptype)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user