diff --git a/docs/syntax.md b/docs/syntax.md index 8da04aeea..01ae27735 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -85,6 +85,8 @@ tweak: # optional tweaking of .gv output # loops loops: # every list item is itself a list of exactly two pins # on the connector that are to be shorted + # Accepts pin numbers or names + loop_color: # Color for loop wiring # auto-generation autogenerate: # optional; defaults to false; see below diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py index 6b7462abf..4204abc4e 100644 --- a/src/wireviz/DataClasses.py +++ b/src/wireviz/DataClasses.py @@ -135,6 +135,8 @@ class Connector: notes: Optional[MultilineHypertext] = None pins: List[Pin] = field(default_factory=list) pinlabels: List[Pin] = field(default_factory=list) + reverse_pins: bool = False + shell: bool = False pincolors: List[Color] = field(default_factory=list) color: Optional[Color] = None show_name: Optional[bool] = None @@ -142,6 +144,7 @@ class Connector: hide_disconnected_pins: bool = False autogenerate: bool = False loops: List[List[Pin]] = field(default_factory=list) + loop_side: Optional[str] = None ignore_in_bom: bool = False additional_components: List[AdditionalComponent] = field(default_factory=list) @@ -178,11 +181,13 @@ def __post_init__(self) -> None: self.show_pincount = self.style != 'simple' # hide pincount for simple (1 pin) connectors by default for loop in self.loops: - # TODO: check that pins to connect actually exist - # TODO: allow using pin labels in addition to pin numbers, just like when defining regular connections - # TODO: include properties of wire used to create the loop - if len(loop) != 2: - raise Exception('Loops must be between exactly two pins!') + # check that pins to connect actually exist + if type(loop) == dict: + loop = list(loop.values())[0] + for pin in loop: + if not ((self.pins.count(pin) == 1) or (self.pinlabels.count(pin) == 1)): + raise Exception(f"Didn't find exactly one {self.name}:{pin} to loop into") + self.activate_pin(pin) for i, item in enumerate(self.additional_components): if isinstance(item, dict): diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py index 2f9eb641c..03a29f020 100644 --- a/src/wireviz/Harness.py +++ b/src/wireviz/Harness.py @@ -93,8 +93,8 @@ def connect(self, from_name: str, from_pin: (int, str), via_name: str, via_wire: def create_graph(self) -> Graph: dot = Graph() - dot.body.append(f'// Graph generated by {APP_NAME} {__version__}') - dot.body.append(f'// {APP_URL}') + dot.body.append(f'// Graph generated by {APP_NAME} {__version__}\n') + dot.body.append(f'// {APP_URL}\n') dot.attr('graph', rankdir='LR', ranksep='2', bgcolor=wv_colors.translate_color(self.options.bgcolor, "HEX"), @@ -122,6 +122,21 @@ def create_graph(self) -> Graph: # If no wires connected (except maybe loop wires)? if not (connector.ports_left or connector.ports_right): connector.ports_left = True # Use left side pins. + loop_side = 'l' + + # Make sure loop direction has connectors :) + if connector.loop_side: + if connector.loop_side.lower() in ["l", "left"]: + loop_side = 'l' + connector.ports_left = True + elif connector.loop_side.lower() in ["r", "right"]: + loop_side = 'r' + connector.ports_right = True + else: + raise Exception("Invalid loop direction!") + # if not specified, default to direction of other connections + else: + loop_side = "l" if connector.ports_left else "r" html = [] @@ -146,7 +161,12 @@ def create_graph(self) -> Graph: pinhtml = [] pinhtml.append('') - for pinindex, (pinname, pinlabel, pincolor) in enumerate(zip_longest(connector.pins, connector.pinlabels, connector.pincolors)): + pin_index_list = enumerate(zip_longest(connector.pins, connector.pinlabels, connector.pincolors)) + + if connector.reverse_pins: + pin_index_list = reversed(list(pin_index_list)) + + for pinindex, (pinname, pinlabel, pincolor) in pin_index_list: if connector.hide_disconnected_pins and not connector.visible_pins.get(pinname, False): continue pinhtml.append(' ') @@ -178,18 +198,37 @@ def create_graph(self) -> Graph: fillcolor=translate_color(self.options.bgcolor_connector, "HEX")) if len(connector.loops) > 0: - dot.attr('edge', color='#000000:#ffffff:#000000') - if connector.ports_left: - loop_side = 'l' + if loop_side == 'l': loop_dir = 'w' - elif connector.ports_right: - loop_side = 'r' + elif loop_side == 'r': loop_dir = 'e' - else: - raise Exception('No side for loops') + + color_iter = iter(wv_colors.COLOR_CODES["DIN"]) for loop in connector.loops: - dot.edge(f'{connector.name}:p{loop[0]}{loop_side}:{loop_dir}', - f'{connector.name}:p{loop[1]}{loop_side}:{loop_dir}') + if type(loop) == dict: + color = list(loop.keys())[0] + loop = loop[color] + else: + color = next(color_iter) + loop_color_hex = translate_color(color, "hex") + dot.attr('edge', color=f'#000000:{loop_color_hex}:#000000') + loop_pins = [] + for pin in loop: + if pin in connector.pins: + loop_pins.append(connector.pins.index(pin)+1) + elif pin in connector.pinlabels: + loop_pins.append(connector.pinlabels.index(pin)+1) + else: + raise Exception(f"Didn't find exactly one {connector.name}:{pin} to loop into") + first = True + for found in loop_pins: + if first: + first = False + prev = found + else: + dot.edge(f'{connector.name}:p{prev}{loop_side}:{loop_dir}', + f'{connector.name}:p{found}{loop_side}:{loop_dir}') + prev = found # determine if there are double- or triple-colored wires in the harness;