diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py
index d1054f4..a9497ac 100644
--- a/src/wireviz/DataClasses.py
+++ b/src/wireviz/DataClasses.py
@@ -5,7 +5,13 @@ from enum import Enum
from itertools import zip_longest
from typing import Dict, List, Optional, Tuple, Union
-from wireviz.wv_colors import COLOR_CODES, Color, ColorMode, Colors, ColorScheme
+from wireviz.wv_colors import (
+ COLOR_CODES,
+ ColorOutputMode,
+ MultiColor,
+ SingleColor,
+ get_color_by_colorcode_index,
+)
from wireviz.wv_helper import aspect_ratio, awg_equiv, mm2_equiv
# Each type alias have their legal values described in comments - validation might be implemented in the future
@@ -60,12 +66,12 @@ class Metadata(dict):
@dataclass
class Options:
fontname: PlainText = "arial"
- bgcolor: Color = "WH"
- bgcolor_node: Optional[Color] = "WH"
- bgcolor_connector: Optional[Color] = None
- bgcolor_cable: Optional[Color] = None
- bgcolor_bundle: Optional[Color] = None
- color_mode: ColorMode = "SHORT"
+ bgcolor: SingleColor = "WH" # will be converted to SingleColor in __post_init__
+ bgcolor_node: SingleColor = "WH"
+ bgcolor_connector: SingleColor = None
+ bgcolor_cable: SingleColor = None
+ bgcolor_bundle: SingleColor = None
+ color_mode: ColorOutputMode = ColorOutputMode.EN_UPPER
mini_bom_mode: bool = True
template_separator: str = "."
_pad: int = 0
@@ -74,6 +80,13 @@ class Options:
_image_paths: [List] = field(default_factory=list)
def __post_init__(self):
+
+ self.bgcolor = SingleColor(self.bgcolor)
+ self.bgcolor_node = SingleColor(self.bgcolor_node)
+ self.bgcolor_connector = SingleColor(self.bgcolor_connector)
+ self.bgcolor_cable = SingleColor(self.bgcolor_cable)
+ self.bgcolor_bundle = SingleColor(self.bgcolor_bundle)
+
if not self.bgcolor_node:
self.bgcolor_node = self.bgcolor
if not self.bgcolor_connector:
@@ -99,13 +112,15 @@ class Image:
width: Optional[int] = None
height: Optional[int] = None
fixedsize: Optional[bool] = None
- bgcolor: Optional[Color] = None
+ bgcolor: SingleColor = None
# Contents of the text cell
just below the image cell:
caption: Optional[MultilineHypertext] = None
# See also HTML doc at https://graphviz.org/doc/info/shapes.html#html
def __post_init__(self):
+ self.bgcolor = SingleColor(self.bgcolor)
+
if self.fixedsize is None:
# Default True if any dimension specified unless self.scale also is specified.
self.fixedsize = (self.width or self.height) and self.scale is None
@@ -141,7 +156,10 @@ class AdditionalComponent:
qty: float = 1
unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
- bgcolor: Optional[Color] = None
+ bgcolor: SingleColor = None
+
+ def __post_init__(self):
+ self.bgcolor = SingleColor(self.bgcolor)
@property
def description(self) -> str:
@@ -159,7 +177,7 @@ class PinClass:
index: int
id: str
label: str
- color: str
+ color: MultiColor
parent: str # designator of parent connector
_anonymous: bool = False # true for pins on autogenerated connectors
_simple: bool = False # true for simple connector
@@ -178,7 +196,7 @@ class WireClass:
index: int
id: str
label: str
- color: str
+ color: MultiColor
parent: str # designator of parent cable/bundle
# gauge: Gauge
# pn: str
@@ -203,8 +221,8 @@ class Connection:
@dataclass
class Connector(Component):
name: Designator
- bgcolor: Optional[Color] = None
- bgcolor_title: Optional[Color] = None
+ bgcolor: SingleColor = None
+ bgcolor_title: SingleColor = None
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
supplier: Optional[MultilineHypertext] = None
@@ -219,8 +237,8 @@ class Connector(Component):
notes: Optional[MultilineHypertext] = None
pins: List[Pin] = field(default_factory=list)
pinlabels: List[Pin] = field(default_factory=list)
- pincolors: List[Color] = field(default_factory=list)
- color: Optional[Color] = None
+ pincolors: List[str] = field(default_factory=list)
+ color: MultiColor = None
show_name: Optional[bool] = None
show_pincount: Optional[bool] = None
hide_disconnected_pins: bool = False
@@ -238,6 +256,10 @@ class Connector(Component):
def __post_init__(self) -> None:
+ self.bgcolor = SingleColor(self.bgcolor)
+ self.bgcolor_title = SingleColor(self.bgcolor_title)
+ self.color = SingleColor(self.color)
+
if isinstance(self.image, dict):
self.image = Image(**self.image)
@@ -280,7 +302,7 @@ class Connector(Component):
index=pin_index,
id=pin_id,
label=pin_label,
- color=pin_color,
+ color=MultiColor(pin_color),
parent=self.name,
_anonymous=self.is_autogenerated,
_simple=self.style == "simple",
@@ -341,8 +363,8 @@ class Connector(Component):
@dataclass
class Cable(Component):
name: Designator
- bgcolor: Optional[Color] = None
- bgcolor_title: Optional[Color] = None
+ bgcolor: SingleColor = None
+ bgcolor_title: SingleColor = None
manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None
mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
supplier: Union[MultilineHypertext, List[MultilineHypertext], None] = None
@@ -355,14 +377,14 @@ class Cable(Component):
show_equiv: bool = False
length: float = 0
length_unit: Optional[str] = None
- color: Optional[Color] = None
+ color: MultiColor = None
wirecount: Optional[int] = None
- shield: Union[bool, Color] = False
+ shield: Union[bool, MultiColor] = False
image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None
- colors: List[Colors] = field(default_factory=list)
+ colors: List[str] = field(default_factory=list)
wirelabels: List[Wire] = field(default_factory=list)
- color_code: Optional[ColorScheme] = None
+ color_code: Optional[str] = None
show_name: Optional[bool] = None
show_wirecount: bool = True
show_wirenumbers: Optional[bool] = None
@@ -393,6 +415,10 @@ class Cable(Component):
def __post_init__(self) -> None:
+ self.bgcolor = SingleColor(self.bgcolor)
+ self.bgcolor_title = SingleColor(self.bgcolor_title)
+ self.color = SingleColor(self.color)
+
if isinstance(self.image, dict):
self.image = Image(**self.image)
@@ -441,21 +467,20 @@ class Cable(Component):
if self.wirecount: # number of wires explicitly defined
if self.colors: # use custom color palette (partly or looped if needed)
- pass
+ self.colors = [
+ self.colors[i % len(self.colors)] for i in range(self.wirecount)
+ ]
elif self.color_code:
# use standard color palette (partly or looped if needed)
if self.color_code not in COLOR_CODES:
raise Exception("Unknown color code")
- self.colors = COLOR_CODES[self.color_code]
+ self.colors = [
+ get_color_by_colorcode_index(self.color_code, i)
+ for i in range(self.wirecount)
+ ]
else: # no colors defined, add dummy colors
self.colors = [""] * self.wirecount
- # make color code loop around if more wires than colors
- if self.wirecount > len(self.colors):
- m = self.wirecount // len(self.colors) + 1
- self.colors = self.colors * int(m)
- # cut off excess after looping
- self.colors = self.colors[: self.wirecount]
else: # wirecount implicit in length of color list
if not self.colors:
raise Exception(
@@ -491,7 +516,7 @@ class Cable(Component):
index=wire_index, # TODO: wire_id
id=wire_index + 1, # TODO: wire_id
label=wire_label,
- color=wire_color,
+ color=MultiColor(wire_color),
parent=self.name,
)
)
@@ -504,7 +529,9 @@ class Cable(Component):
index=index_offset,
id="s",
label="Shield",
- color=self.shield if isinstance(self.shield, str) else None,
+ color=MultiColor(self.shield)
+ if isinstance(self.shield, str)
+ else MultiColor(None),
parent=self.name,
)
)
diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py
index 8c6572f..e7a7603 100644
--- a/src/wireviz/Harness.py
+++ b/src/wireviz/Harness.py
@@ -5,6 +5,7 @@ from pathlib import Path
from graphviz import Graph
+import wireviz.wv_colors
from wireviz.DataClasses import (
Arrow,
ArrowWeight,
@@ -152,7 +153,7 @@ class Harness:
for connector in self.connectors.values():
# generate connector node
- gv_html = gv_node_component(connector, self.options)
+ gv_html = gv_node_component(connector)
bgcolor = calculate_node_bgcolor(connector, self.options)
dot.node(
connector.name,
@@ -163,25 +164,27 @@ class Harness:
)
# generate edges for connector loops
if len(connector.loops) > 0:
- dot.attr("edge", color="#000000:#ffffff:#000000")
+ dot.attr("edge", color="#000000")
loops = gv_connector_loops(connector)
for head, tail in loops:
dot.edge(head, tail)
# determine if there are double- or triple-colored wires in the harness;
# if so, pad single-color wires to make all wires of equal thickness
- pad = any(
- len(colorstr) > 2
+ multicolor_wires = [
+ len(wire.color) > 1
for cable in self.cables.values()
- for colorstr in cable.colors
- )
-
- self.options._pad = pad
+ for wire in cable.wire_objects
+ ]
+ if any(multicolor_wires):
+ wireviz.wv_colors.padding_amount = 3
+ else:
+ wireviz.wv_colors.padding_amount = 1
for cable in self.cables.values():
# generate cable node
# TODO: PN info for bundles (per wire)
- gv_html = gv_node_component(cable, self.options)
+ gv_html = gv_node_component(cable)
bgcolor = calculate_node_bgcolor(cable, self.options)
style = "filled,dashed" if cable.category == "bundle" else "filled"
dot.node(
diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py
index 3de45d8..4e075a3 100644
--- a/src/wireviz/wv_bom.py
+++ b/src/wireviz/wv_bom.py
@@ -4,8 +4,7 @@ from dataclasses import asdict
from itertools import groupby
from typing import Any, Dict, List, Optional, Tuple, Union
-from wireviz.DataClasses import AdditionalComponent, Cable, Color, Connector
-from wireviz.wv_colors import translate_color
+from wireviz.DataClasses import AdditionalComponent, Cable, Connector
from wireviz.wv_gv_html import html_line_breaks
from wireviz.wv_helper import clean_whitespace, pn_info_string
@@ -99,7 +98,7 @@ def generate_bom(harness: "Harness") -> List[BOMEntry]:
+ (f", {connector.subtype}" if connector.subtype else "")
+ (f", {connector.pincount} pins" if connector.show_pincount else "")
+ (
- f", {translate_color(connector.color, harness.options.color_mode)}"
+ f", xxx" # {translate_color(connector.color, harness.options.color_mode)}
if connector.color
else ""
)
@@ -132,7 +131,7 @@ def generate_bom(harness: "Harness") -> List[BOMEntry]:
)
+ (" shielded" if cable.shield else "")
+ (
- f", {translate_color(cable.color, harness.options.color_mode)}"
+ f", xxx" # {translate_color(cable.color, harness.options.color_mode)}
if cable.color
else ""
)
@@ -154,7 +153,7 @@ def generate_bom(harness: "Harness") -> List[BOMEntry]:
+ (f", {cable.type}" if cable.type else "")
+ (f", {cable.gauge} {cable.gauge_unit}" if cable.gauge else "")
+ (
- f", {translate_color(color, harness.options.color_mode)}"
+ f", xxx" # {translate_color(color, harness.options.color_mode)}
if color
else ""
)
@@ -236,7 +235,7 @@ def component_table_entry(
type: str,
qty: Union[int, float],
unit: Optional[str] = None,
- bgcolor: Optional[Color] = None,
+ bgcolor: Optional[str] = None,
pn: Optional[str] = None,
manufacturer: Optional[str] = None,
mpn: Optional[str] = None,
diff --git a/src/wireviz/wv_colors.py b/src/wireviz/wv_colors.py
index 34d9241..be8fefb 100644
--- a/src/wireviz/wv_colors.py
+++ b/src/wireviz/wv_colors.py
@@ -1,6 +1,206 @@
# -*- coding: utf-8 -*-
-from typing import Dict, List
+from collections import namedtuple
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import List
+
+KnownColor = namedtuple("KnownColor", "html code_de full_en full_de")
+
+ColorOutputMode = Enum(
+ "ColorOutputMode", "EN_LOWER EN_UPPER DE_LOWER DE_UPPER HTML_LOWER HTML_UPPER"
+)
+
+known_colors = { # v--------v--------- for future use
+ "BK": KnownColor("#000000", "sw", "black", "schwarz"),
+ "WH": KnownColor("#ffffff", "ws", "white", "weiß"),
+ "GY": KnownColor("#999999", "gr", "grey", "grau"),
+ "PK": KnownColor("#ff66cc", "rs", "pink", "rosa"),
+ "RD": KnownColor("#ff0000", "rt", "red", "rot"),
+ "OG": KnownColor("#ff8000", "or", "orange", "orange"),
+ "YE": KnownColor("#ffff00", "ge", "yellow", "gelb"),
+ "OL": KnownColor("#708000", "ol", "olive green", "olivgrün"),
+ "GN": KnownColor("#00ff00", "gn", "green", "grün"),
+ "TQ": KnownColor("#00ffff", "tk", "turquoise", "türkis"),
+ "LB": KnownColor("#a0dfff", "hb", "light blue", "hellblau"),
+ "BU": KnownColor("#0066ff", "bl", "blue", "blau"),
+ "VT": KnownColor("#8000ff", "vi", "violet", "violett"),
+ "BN": KnownColor("#895956", "br", "brown", "braun"),
+ "BG": KnownColor("#ceb673", "bg", "beige", "beige"),
+ "IV": KnownColor("#f5f0d0", "eb", "ivory", "elfenbein"),
+ "SL": KnownColor("#708090", "si", "slate", "schiefer"),
+ "CU": KnownColor("#d6775e", "ku", "copper", "Kupfer"),
+ "SN": KnownColor("#aaaaaa", "vz", "tin", "verzinkt"),
+ "SR": KnownColor("#84878c", "ag", "silver", "Silber"),
+ "GD": KnownColor("#ffcf80", "au", "gold", "Gold"),
+}
+
+color_output_mode = ColorOutputMode.EN_UPPER
+
+padding_amount = 1
+
+
+def convert_case(inp):
+ if "_LOWER" in color_output_mode.name:
+ return inp.lower()
+ elif "_UPPER" in color_output_mode.name:
+ return inp.upper()
+ else: # currently not used
+ return inp
+
+
+def get_color_by_colorcode_index(color_code: str, index: int) -> str:
+ num_colors_in_code = len(COLOR_CODES[color_code])
+ actual_index = index % num_colors_in_code # wrap around if index is out of bounds
+ return COLOR_CODES[color_code][actual_index]
+
+ # # make color code loop around if more wires than colors
+ # if self.wirecount > len(self.colors):
+ # m = self.wirecount // len(self.colors) + 1
+ # self.colors = self.colors * int(m)
+ # # cut off excess after looping
+ # self.colors = self.colors[: self.wirecount]
+
+
+@dataclass
+class SingleColor:
+ _code_en: str
+ _html: str
+
+ @property
+ def code_en(self):
+ return convert_case(self._code_en) if self._code_en else None
+
+ @property
+ def code_de(self):
+ return (
+ convert_case(known_colors[self._code_en.upper()].code_de)
+ if self._code_en
+ else None
+ )
+
+ @property
+ def html(self):
+ return convert_case(self._html) if self._code_en else None
+
+ @property
+ def known(self):
+ return (
+ self.code_en.upper() in known_colors.keys() if self._code_en else True
+ ) # ?
+
+ def __init__(self, inp):
+ if inp is None:
+ self._html = None
+ self._code_en = None
+ elif isinstance(inp, int):
+ hex_str = f"#{inp:06x}"
+ self._html = hex_str
+ self._code_en = hex_str # do not perform reverse lookup
+ elif inp.upper() in known_colors.keys():
+ inp_upper = inp.upper()
+ self._code_en = inp_upper
+ self._html = known_colors[inp_upper].html
+ else: # assume valid HTML color
+ self._html = inp
+ self._code_en = inp
+
+ @property
+ def html_padded(self):
+ return ":".join([self.html] * padding_amount)
+
+ def __bool__(self):
+ return self._code_en is not None
+
+ def __str__(self):
+ if self._html is None:
+ return ""
+ elif self.known and "EN_" in color_output_mode.name:
+ return self.code_en
+ elif self.known and "DE_" in color_output_mode.name:
+ return self.code_de
+ else:
+ return self.html
+
+
+@dataclass
+class MultiColor:
+ colors: List[SingleColor] = field(default_factory=list)
+
+ def __init__(self, inp):
+ self.colors = []
+ if inp is None:
+ pass
+ elif isinstance(inp, List): # input is already a list
+ for item in inp:
+ if item is None:
+ pass
+ elif isinstance(item, SingleColor):
+ self.colors.append(item)
+ else: # string
+ self.colors.append(SingleColor(item))
+ elif isinstance(inp, SingleColor): # single color
+ self.colors = [inp]
+ else: # split input into list
+ if ":" in str(inp):
+ self.colors = [SingleColor(item) for item in inp.split(":")]
+ else:
+ if isinstance(inp, int):
+ self.colors = [SingleColor(inp)]
+ elif len(inp) % 2 == 0:
+ items = [inp[i : i + 2] for i in range(0, len(inp), 2)]
+ known = [item.upper() in known_colors.keys() for item in items]
+ if all(known):
+ self.colors = [SingleColor(item) for item in items]
+ else: # assume it's a HTML color name
+ self.colors = [SingleColor(inp)]
+ else: # assume it's a HTML color name
+ self.colors = [SingleColor(inp)]
+
+ def __len__(self):
+ return len(self.colors)
+
+ def __bool__(self):
+ return len(self.colors) >= 1
+
+ def __str__(self):
+ if "EN_" in color_output_mode.name or "DE_" in color_output_mode.name:
+ joiner = "" if self.all_known else ":"
+ elif "HTML_" in color_output_mode.name:
+ joiner = ":"
+ else:
+ joiner = "???"
+ return joiner.join([str(color) for color in self.colors])
+
+ @property
+ def all_known(self):
+ return all([color.known for color in self.colors])
+
+ @property
+ def html(self):
+ return ":".join([color.html for color in self.colors])
+
+ @property
+ def html_padded_list(self):
+ # padding only properly works for padding_amount 1 or 3
+ if padding_amount == 1:
+ out = [color.html for color in self.colors]
+ elif len(self) == 0:
+ out = []
+ elif len(self) == 1:
+ out = [self.colors[0].html for i in range(3)]
+ elif len(self) == 2:
+ out = [self.colors[0].html, self.colors[1].html, self.colors[0].html]
+ elif len(self) == 3:
+ out = [color.html for color in self.colors]
+ else:
+ raise Exception(f"Padding not supported for len {len(selfq)}")
+ return [str(color) for color in out]
+
+ @property
+ def html_padded(self):
+ return ":".join(self.html_padded_list)
+
COLOR_CODES = {
# fmt: off
@@ -38,166 +238,3 @@ COLOR_CODES = {
"T568A": ["WHGN", "GN", "WHOG", "BU", "WHBU", "OG", "WHBN", "BN"],
"T568B": ["WHOG", "OG", "WHGN", "BU", "WHBU", "GN", "WHBN", "BN"],
}
-
-# Convention: Color names should be 2 letters long, to allow for multicolored wires
-
-_color_hex = {
- "BK": "#000000",
- "WH": "#ffffff",
- "GY": "#999999",
- "PK": "#ff66cc",
- "RD": "#ff0000",
- "OG": "#ff8000",
- "YE": "#ffff00",
- "OL": "#708000", # olive green
- "GN": "#00ff00",
- "TQ": "#00ffff",
- "LB": "#a0dfff", # light blue
- "BU": "#0066ff",
- "VT": "#8000ff",
- "BN": "#895956",
- "BG": "#ceb673", # beige
- "IV": "#f5f0d0", # ivory
- "SL": "#708090",
- "CU": "#d6775e", # Faux-copper look, for bare CU wire
- "SN": "#aaaaaa", # Silvery look for tinned bare wire
- "SR": "#84878c", # Darker silver for silvered wire
- "GD": "#ffcf80", # Golden color for gold
-}
-
-_color_full = {
- "BK": "black",
- "WH": "white",
- "GY": "grey",
- "PK": "pink",
- "RD": "red",
- "OG": "orange",
- "YE": "yellow",
- "OL": "olive green",
- "GN": "green",
- "TQ": "turquoise",
- "LB": "light blue",
- "BU": "blue",
- "VT": "violet",
- "BN": "brown",
- "BG": "beige",
- "IV": "ivory",
- "SL": "slate",
- "CU": "copper",
- "SN": "tin",
- "SR": "silver",
- "GD": "gold",
-}
-
-_color_ger = {
- "BK": "sw",
- "WH": "ws",
- "GY": "gr",
- "PK": "rs",
- "RD": "rt",
- "OG": "or",
- "YE": "ge",
- "OL": "ol", # olivgrün
- "GN": "gn",
- "TQ": "tk",
- "LB": "hb", # hellblau
- "BU": "bl",
- "VT": "vi",
- "BN": "br",
- "BG": "bg", # beige
- "IV": "eb", # elfenbeinfarben
- "SL": "si", # Schiefer
- "CU": "ku", # Kupfer
- "SN": "vz", # verzinkt
- "SR": "ag", # Silber
- "GD": "au", # Gold
-}
-
-
-color_default = "#ffffff"
-
-_hex_digits = set("0123456789abcdefABCDEF")
-
-
-# Literal type aliases below are commented to avoid requiring python 3.8
-Color = str # Two-letter color name = Literal[_color_hex.keys()]
-Colors = str # One or more two-letter color names (Color) concatenated into one string
-ColorMode = (
- str # = Literal['full', 'FULL', 'hex', 'HEX', 'short', 'SHORT', 'ger', 'GER']
-)
-ColorScheme = str # Color scheme name = Literal[COLOR_CODES.keys()]
-
-
-def get_color_hex(input: Colors, pad: bool = False) -> List[str]:
- """Return list of hex colors from either a string of color names or :-separated hex colors."""
- if input is None or input == "":
- return [color_default]
- elif input[0] == "#": # Hex color(s)
- output = input.split(":")
- for i, c in enumerate(output):
- if c[0] != "#" or not all(d in _hex_digits for d in c[1:]):
- if c != input:
- c += f" in input: {input}"
- print(f"Invalid hex color: {c}")
- output[i] = color_default
- else: # Color name(s)
-
- def lookup(c: str) -> str:
- try:
- return _color_hex[c]
- except KeyError:
- if c != input:
- c += f" in input: {input}"
- print(f"Unknown color name: {c}")
- return color_default
-
- output = [lookup(input[i : i + 2]) for i in range(0, len(input), 2)]
-
- if len(output) == 2: # Give wires with EXACTLY 2 colors that striped look.
- output += output[:1]
- elif pad and len(output) == 1: # Hacky style fix: Give single color wires
- output *= 3 # a triple-up so that wires are the same size.
-
- return output
-
-
-def get_color_translation(translate: Dict[Color, str], input: Colors) -> List[str]:
- """Return list of colors translations from either a string of color names or :-separated hex colors."""
-
- def from_hex(hex_input: str) -> str:
- for color, hex in _color_hex.items():
- if hex == hex_input:
- return translate[color]
- return f'({",".join(str(int(hex_input[i:i+2], 16)) for i in range(1, 6, 2))})'
-
- return (
- [from_hex(h) for h in input.lower().split(":")]
- if input[0] == "#"
- else [translate.get(input[i : i + 2], "??") for i in range(0, len(input), 2)]
- )
-
-
-def translate_color(input: Colors, color_mode: ColorMode) -> str:
- if input == "":
- return ""
- if input is None:
- return None
- upper = color_mode.isupper()
- if not (color_mode.isupper() or color_mode.islower()):
- raise Exception("Unknown color mode capitalization")
-
- color_mode = color_mode.lower()
- if color_mode == "full":
- output = "/".join(get_color_translation(_color_full, input))
- elif color_mode == "hex":
- output = ":".join(get_color_hex(input, pad=False))
- elif color_mode == "ger":
- output = "".join(get_color_translation(_color_ger, input))
- elif color_mode == "short":
- output = input
- else:
- raise Exception("Unknown color mode")
- if upper:
- return output.upper()
- else:
- return output.lower()
diff --git a/src/wireviz/wv_colors_new.py b/src/wireviz/wv_colors_new.py
deleted file mode 100644
index 24fdee9..0000000
--- a/src/wireviz/wv_colors_new.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from collections import namedtuple
-from dataclasses import dataclass, field
-from enum import Enum
-from typing import List
-
-KnownColor = namedtuple("KnownColor", "html code_de full_en full_de")
-ColorOutputMode = Enum("ColorOutputMode", "EN_LOWER EN_UPPER DE_LOWER DE_UPPER HTML_LOWER HTML_UPPER")
-
-known_colors = {
- "BK": KnownColor("#000000", "sw", "black", "schwarz"),
- "WH": KnownColor("#ffffff", "ws", "white", "weiß"),
- "GY": KnownColor("#999999", "gr", "grey", "grau"),
- "PK": KnownColor("#ff66cc", "rs", "pink", "rosa"),
- "RD": KnownColor("#ff0000", "rt", "red", "rot"),
- "OG": KnownColor("#ff8000", "or", "orange", "orange"),
- "YE": KnownColor("#ffff00", "ge", "yellow", "gelb"),
- "OL": KnownColor("#708000", "ol", "olive green", "olivgrün"),
- "GN": KnownColor("#00ff00", "gn", "green", "grün"),
- "TQ": KnownColor("#00ffff", "tk", "turquoise", "türkis"),
- "LB": KnownColor("#a0dfff", "hb", "light blue", "hellblau"),
- "BU": KnownColor("#0066ff", "bl", "blue", "blau"),
- "VT": KnownColor("#8000ff", "vi", "violet", "violett"),
- "BN": KnownColor("#895956", "br", "brown", "braun"),
- "BG": KnownColor("#ceb673", "bg", "beige", "beige"),
- "IV": KnownColor("#f5f0d0", "eb", "ivory", "elfenbein"),
- "SL": KnownColor("#708090", "si", "slate", "schiefer"),
- "CU": KnownColor("#d6775e", "ku", "copper", "Kupfer"),
- "SN": KnownColor("#aaaaaa", "vz", "tin", "verzinkt"),
- "SR": KnownColor("#84878c", "ag", "silver", "Silber"),
- "GD": KnownColor("#ffcf80", "au", "gold", "Gold"),
-}
-
-color_output_mode = ColorOutputMode.EN_UPPER
-padding_amount = 1
-
-def convert_case(inp):
- if "_LOWER" in color_output_mode.name:
- return inp.lower()
- elif "_UPPER" in color_output_mode.name:
- return inp.upper()
- else: # currently not used
- return inp
-
-@dataclass
-class SingleColor():
- _code_en: str
- _code_de: str
- _html: str
-
- @property
- def code_en(self):
- return convert_case(self._code_en)
- # repeat for _code_de, _html
-
- @property
- def code_de(self):
- return convert_case(self._code_de)
- # repeat for _code_de, _html
-
- @property
- def html(self):
- return convert_case(self._html)
- # repeat for _code_de, _html
-
- @property
- def known(self):
- return self.code_en.upper() in known_colors.keys()
-
- def __init__(self, inp):
- if isinstance(inp, int):
- hex_str = f"#{inp:06x}"
- self._html = hex_str
- self._code_en = hex_str # do not perform reverse lookup
- self._code_de = hex_str
- elif inp.upper() in known_colors.keys():
- inp_upper = inp.upper()
- self._code_en = inp_upper
- self._code_de = known_colors[inp_upper].code_de
- self._html = known_colors[inp_upper].html
- else: # assume valid HTML color
- self._html = inp
- self._code_en = inp
- self._code_de = inp
-
- @property
- def html_padded(self):
- return ":".join([self.html] * padding_amount)
-
- def __str__(self):
- if self.known and "EN_" in color_output_mode.name:
- return self.code_en
- elif self.known and "DE_" in color_output_mode.name:
- return self.code_de
- else:
- return self.html
-
-@dataclass
-class MultiColor():
- colors: List[SingleColor] = field(default_factory=list)
-
- def __init__(self, inp):
- self.colors = []
- if isinstance(inp, List): # input is already a list
- for item in inp:
- if isinstance(item, SingleColor):
- self.colors.append(item)
- else: # string
- self.colors.append(SingleColor(item))
- elif isinstance(inp, SingleColor): # single color
- self.colors = [inp]
- else: # split input into list
- if ":" in str(inp):
- self.colors = [SingleColor(item) for item in inp.split(":")]
- else:
- if isinstance(inp, int):
- self.colors = [SingleColor(inp)]
- elif len(inp) % 2 == 0:
- items = [inp[i:i+2] for i in range(0, len(inp), 2)]
- known = [item.upper() in known_colors.keys() for item in items]
- if all(known):
- self.colors = [SingleColor(item) for item in items]
- else: # assume it's a HTML color name
- self.colors = [SingleColor(inp)]
- else: # assume it's a HTML color name
- self.colors = [SingleColor(inp)]
-
-
- def __len__(self):
- return len(self.colors)
-
- def __str__(self):
- if "EN_" in color_output_mode.name or "DE_" in color_output_mode.name:
- joiner = "" if self.all_known else ":"
- elif "HTML_" in color_output_mode.name:
- joiner = ":"
- else:
- joiner = "???"
- return joiner.join([str(color) for color in self.colors])
-
- @property
- def all_known(self):
- return all([color.known for color in self.colors])
-
- @property
- def html(self):
- return ":".join([color.html for color in self.colors])
-
- @property
- def html_padded(self):
- if len(self) == 0:
- out = []
- elif len(self) == 1:
- out = [self.colors[0] for i in range(3)]
- elif len(self) == 2:
- out = [self.colors[0], self.colors[1], self.colors[0]]
- elif len(self) == 3:
- out = self.colors
- else:
- raise Exception(f"Padding not supported for len {len(selfq)}")
- return ":".join([str(color) for color in out])
-
-COLOR_CODES = {
- # fmt: off
- "DIN": [
- "WH", "BN", "GN", "YE", "GY", "PK", "BU", "RD", "BK", "VT", "GYPK", "RDBU",
- "WHGN", "BNGN", "WHYE", "YEBN", "WHGY", "GYBN", "WHPK", "PKBN", "WHBU", "BNBU",
- "WHRD", "BNRD", "WHBK", "BNBK", "GYGN", "YEGY", "PKGN", "YEPK", "GNBU", "YEBU",
- "GNRD", "YERD", "GNBK", "YEBK", "GYBU", "PKBU", "GYRD", "PKRD", "GYBK", "PKBK",
- "BUBK", "RDBK", "WHBNBK", "YEGNBK", "GYPKBK", "RDBUBK", "WHGNBK", "BNGNBK",
- "WHYEBK", "YEBNBK", "WHGYBK", "GYBNBK", "WHPKBK", "PKBNBK", "WHBUBK",
- "BNBUBK", "WHRDBK", "BNRDBK",
- ],
- # fmt: on
- "IEC": ["BN", "RD", "OG", "YE", "GN", "BU", "VT", "GY", "WH", "BK"],
- "BW": ["BK", "WH"],
- # 25-pair color code - see also https://en.wikipedia.org/wiki/25-pair_color_code
- # 5 major colors (WH,RD,BK,YE,VT) combined with 5 minor colors (BU,OG,GN,BN,SL).
- # Each POTS pair tip (+) had major/minor color, and ring (-) had minor/major color.
- # fmt: off
- "TEL": [ # 25x2: Ring and then tip of each pair
- "BUWH", "WHBU", "OGWH", "WHOG", "GNWH", "WHGN", "BNWH", "WHBN", "SLWH", "WHSL",
- "BURD", "RDBU", "OGRD", "RDOG", "GNRD", "RDGN", "BNRD", "RDBN", "SLRD", "RDSL",
- "BUBK", "BKBU", "OGBK", "BKOG", "GNBK", "BKGN", "BNBK", "BKBN", "SLBK", "BKSL",
- "BUYE", "YEBU", "OGYE", "YEOG", "GNYE", "YEGN", "BNYE", "YEBN", "SLYE", "YESL",
- "BUVT", "VTBU", "OGVT", "VTOG", "GNVT", "VTGN", "BNVT", "VTBN", "SLVT", "VTSL",
- ],
- "TELALT": [ # 25x2: Tip and then ring of each pair
- "WHBU", "BU", "WHOG", "OG", "WHGN", "GN", "WHBN", "BN", "WHSL", "SL",
- "RDBU", "BURD", "RDOG", "OGRD", "RDGN", "GNRD", "RDBN", "BNRD", "RDSL", "SLRD",
- "BKBU", "BUBK", "BKOG", "OGBK", "BKGN", "GNBK", "BKBN", "BNBK", "BKSL", "SLBK",
- "YEBU", "BUYE", "YEOG", "OGYE", "YEGN", "GNYE", "YEBN", "BNYE", "YESL", "SLYE",
- "VTBU", "BUVT", "VTOG", "OGVT", "VTGN", "GNVT", "VTBN", "BNVT", "VTSL", "SLVT",
- ],
- # fmt: on
- "T568A": ["WHGN", "GN", "WHOG", "BU", "WHBU", "OG", "WHBN", "BN"],
- "T568B": ["WHOG", "OG", "WHGN", "BU", "WHBU", "GN", "WHBN", "BN"],
-}
diff --git a/src/wireviz/wv_gv_html.py b/src/wireviz/wv_gv_html.py
index 0daaf1d..16dad23 100644
--- a/src/wireviz/wv_gv_html.py
+++ b/src/wireviz/wv_gv_html.py
@@ -17,7 +17,6 @@ from wireviz.DataClasses import (
ShieldClass,
WireClass,
)
-from wireviz.wv_colors import get_color_hex, translate_color
from wireviz.wv_helper import pn_info_string, remove_links
from wireviz.wv_table_util import * # TODO: explicitly import each needed tag later
@@ -26,9 +25,7 @@ HEADER_MPN = "MPN"
HEADER_SPN = "SPN"
-def gv_node_component(
- component: Component, harness_options: Options, pad=None
-) -> Table:
+def gv_node_component(component: Component) -> Table:
# If no wires connected (except maybe loop wires)?
if isinstance(component, Connector):
if not (component.ports_left or component.ports_right):
@@ -52,8 +49,8 @@ def gv_node_component(
html_line_breaks(component.type),
html_line_breaks(component.subtype),
f"{component.pincount}-pin" if component.show_pincount else None,
- translate_color(component.color, harness_options.color_mode),
- colorbar_cell(component.color),
+ str(component.color) if component.color else None,
+ colorbar_cell(component.color) if component.color else None,
]
elif isinstance(component, Cable):
line_info = [
@@ -64,10 +61,12 @@ def gv_node_component(
f"{component.length} {component.length_unit}"
if component.length > 0
else None,
- translate_color(component.color, harness_options.color_mode),
- colorbar_cell(component.color),
+ str(component.color) if component.color else None,
+ colorbar_cell(component.color) if component.color else None,
]
+ x = colorbar_cell(component.color) if component.color else None
+
line_image, line_image_caption = image_and_caption_cells(component)
# line_additional_component_table = get_additional_component_table(self, connector)
line_additional_component_table = None
@@ -79,7 +78,7 @@ def gv_node_component(
else:
line_ports = None
elif isinstance(component, Cable):
- line_ports = gv_conductor_table(component, harness_options)
+ line_ports = gv_conductor_table(component)
lines = [
line_name,
@@ -109,17 +108,17 @@ def calculate_node_bgcolor(component, harness_options):
# assign component node bgcolor at the GraphViz node level
# instead of at the HTML table level for better rendering of node outline
if component.bgcolor:
- return translate_color(component.bgcolor, "HEX")
+ return component.bgcolor.html
elif isinstance(component, Connector) and harness_options.bgcolor_connector:
- return translate_color(harness_options.bgcolor_connector, "HEX")
+ return harness_options.bgcolor_connector.html
elif (
isinstance(component, Cable)
and component.category == "bundle"
and harness_options.bgcolor_bundle
):
- return translate_color(harness_options.bgcolor_bundle, "HEX")
+ return harness_options.bgcolor_bundle.html
elif isinstance(component, Cable) and harness_options.bgcolor_cable:
- return translate_color(harness_options.bgcolor_cable, "HEX")
+ return harness_options.bgcolor_cable.html
def make_list_of_cells(inp) -> List[Td]:
@@ -170,31 +169,25 @@ def nested_table(lines: List[Td]) -> Table:
def gv_pin_table(component) -> Table:
- pin_tuples = zip_longest(
- component.pins,
- component.pinlabels,
- component.pincolors,
- )
-
pin_rows = []
- for pinindex, (pinname, pinlabel, pincolor) in enumerate(pin_tuples):
- if component.should_show_pin(pinname):
- pin_rows.append(
- gv_pin_row(pinindex, pinname, pinlabel, pincolor, component)
- )
+ for pin in component.pin_objects:
+ if component.should_show_pin(pin.id): # TODO remove True
+ pin_rows.append(gv_pin_row(pin, component))
tbl = Table(pin_rows, border=0, cellborder=1, cellpadding=3, cellspacing=0)
return tbl
-def gv_pin_row(pin_index, pin_name, pin_label, pin_color, connector) -> Tr:
+def gv_pin_row(pin, connector) -> Tr:
# ports in GraphViz are 1-indexed for more natural maping to pin/wire numbers
- cell_pin_left = Td(pin_name, port=f"p{pin_index+1}l")
- cell_pin_label = Td(pin_label, delete_if_empty=True)
- cell_pin_right = Td(pin_name, port=f"p{pin_index+1}r")
+ cell_pin_left = Td(pin.id, port=f"p{pin.index+1}l")
+ cell_pin_label = Td(pin.label, delete_if_empty=True)
+ cell_pin_color = Td(str(pin.color), delete_if_empty=True)
+ cell_pin_right = Td(pin.id, port=f"p{pin.index+1}r")
cells = [
cell_pin_left if connector.ports_left else None,
cell_pin_label,
+ cell_pin_color, # TODO: generate proper color mini-table too
cell_pin_right if connector.ports_right else None,
]
return Tr(cells)
@@ -217,7 +210,7 @@ def gv_connector_loops(connector: Connector) -> List:
return loop_edges
-def gv_conductor_table(cable, harness_options) -> Table:
+def gv_conductor_table(cable) -> Table:
rows = []
rows.append(Tr(Td(" "))) # spacer row on top
@@ -233,7 +226,7 @@ def gv_conductor_table(cable, harness_options) -> Table:
wireinfo = []
if cable.show_wirenumbers and not isinstance(wire, ShieldClass):
wireinfo.append(str(wire.id))
- wireinfo.append(translate_color(wire.color, harness_options.color_mode))
+ wireinfo.append(str(wire.color))
wireinfo.append(wire.label)
ins, outs = [], []
@@ -252,7 +245,7 @@ def gv_conductor_table(cable, harness_options) -> Table:
rows.append(Tr(cells_above))
# the wire itself
- rows.append(Tr(gv_wire_cell(wire, padding=harness_options._pad)))
+ rows.append(Tr(gv_wire_cell(wire)))
# row below the wire
# TODO: PN stuff for bundles
@@ -263,16 +256,17 @@ def gv_conductor_table(cable, harness_options) -> Table:
return tbl
-def gv_wire_cell(wire: Union[WireClass, ShieldClass], padding) -> Td:
+def gv_wire_cell(wire: Union[WireClass, ShieldClass]) -> Td:
+ # import pudb; pudb.set_trace()
if wire.color:
- color_list = ["#000000"] + get_color_hex(wire.color, pad=padding) + ["#000000"]
+ color_list = ["#000000"] + wire.color.html_padded_list + ["#000000"]
else:
color_list = ["#000000"]
wire_inner_rows = []
for j, bgcolor in enumerate(color_list[::-1]):
wire_inner_cell_attribs = {
- "bgcolor": bgcolor if bgcolor != "" else "BK",
+ "bgcolor": bgcolor if bgcolor != "" else "#000000",
"border": 0,
"cellpadding": 0,
"colspan": 3,
@@ -339,16 +333,9 @@ def wire_pn_stuff():
def gv_edge_wire(harness, cable, connection) -> (str, str, str):
if connection.via.color:
# check if it's an actual wire and not a shield
- wire_color = get_color_hex(connection.via.color, pad=harness.options._pad)
- color = ":".join(["#000000"] + wire_color + ["#000000"])
+ color = f"#000000:{connection.via.color.html_padded}:#000000"
else: # it's a shield connection
- # shield is shown with specified color and black borders, or as a thin black wire otherwise
- if connection.via.color:
- shield_color_hex = get_color_hex(connection.via.color)[0]
- shield_color_str = ":".join(["#000000", shield_color_hex, "#000000"])
- else:
- shield_color_str = "#000000"
- color = shield_color_str
+ color = "#000000"
if connection.from_ is not None: # connect to left
from_port_str = (
@@ -417,7 +404,7 @@ def gv_edge_mate(mate) -> (str, str, str, str):
def colored_cell(contents, bgcolor) -> Td:
- return Td(contents, bgcolor=translate_color(bgcolor, "HEX"))
+ return Td(contents, bgcolor=bgcolor.html)
def part_number_str_list(component: Component) -> List[str]:
@@ -433,10 +420,7 @@ def part_number_str_list(component: Component) -> List[str]:
def colorbar_cell(color) -> Td:
- if color:
- return Td("", bgcolor=translate_color(color, "HEX"), width=4)
- else:
- return None
+ return Td("", bgcolor=color.html, width=4)
def image_and_caption_cells(component: Component) -> (Td, Td):
@@ -456,7 +440,7 @@ def image_and_caption_cells(component: Component) -> (Td, Td):
image_cell.update_attribs(
balign="left",
- bgcolor=translate_color(component.image.bgcolor, "HEX"),
+ bgcolor=component.image.bgcolor.html,
sides="TLR" if component.image.caption else None,
)
@@ -495,7 +479,7 @@ def set_dot_basics(dot, options):
"graph",
rankdir="LR",
ranksep="2",
- bgcolor=translate_color(options.bgcolor, "HEX"),
+ bgcolor=options.bgcolor.html,
nodesep="0.33",
fontname=options.fontname,
)
@@ -506,7 +490,7 @@ def set_dot_basics(dot, options):
height="0",
margin="0", # Actual size of the node is entirely determined by the label.
style="filled",
- fillcolor=translate_color(options.bgcolor_node, "HEX"),
+ fillcolor=options.bgcolor_node.html,
fontname=options.fontname,
)
dot.attr("edge", style="bold", fontname=options.fontname)
diff --git a/src/wireviz/wv_html.py b/src/wireviz/wv_html.py
index 1534266..8af39cb 100644
--- a/src/wireviz/wv_html.py
+++ b/src/wireviz/wv_html.py
@@ -79,7 +79,7 @@ def generate_html_output(
replacements = {
"": f"{APP_NAME} {__version__} - {APP_URL}",
"": options.fontname,
- "": wv_colors.translate_color(options.bgcolor, "hex"),
+ "": options.bgcolor.html,
"": svgdata,
"": bom_html,
"": bom_html_reversed,
|