From 9ff5b7aa52be4f331f5b6cfe04f3c3a41bd3b0a5 Mon Sep 17 00:00:00 2001 From: Spine Date: Sat, 23 Mar 2024 09:56:15 +0000 Subject: [PATCH] script to generate trump precedence chart --- bin/build-trump-chart | 239 ++++++++++++++++++++++++++++++ public/static/svg/trump-chart.svg | 90 +++++++++++ 2 files changed, 329 insertions(+) create mode 100755 bin/build-trump-chart create mode 100644 public/static/svg/trump-chart.svg diff --git a/bin/build-trump-chart b/bin/build-trump-chart new file mode 100755 index 000000000..ebc64eb7d --- /dev/null +++ b/bin/build-trump-chart @@ -0,0 +1,239 @@ +#! /usr/bin/python3 +# +# This generates the trump precedence chart +# It is a bit of an awful hack but it gets the job done. +# The arrows are particularly fragile: change some of the +# chart settings and they will likely break. + +class Chart: + def __init__( + self, + chart_width: int, + chart_height: int, + box_width: int, + box_height: int, + gutter: int, + x: int, + y: int, + y_offset: int, + radius: int + ): + self.chart_width = chart_width + self.chart_height = chart_height + self.box_width = box_width + self.box_height = box_height + self.gutter = gutter + self.x = x + self.y = y + self.y_offset = y_offset + self.radius = radius + self.column_count = 0 + self.dist = 10 # spline distance for arcs + self.gradient = [ + "#3dda07", + "#46c819", + "#43b72a", + "#56a63b", + "#5f944d", + ] + self.head_css = "font-family=\"sans-serif\" font-size=\"14\" font-weight=\"bolder\" dominant-baseline=\"middle\" text-anchor=\"middle\"" + self.text_css = "font-family=\"sans-serif\" font-size=\"12\" font-weight=\"bold\" dominant-baseline=\"middle\" text-anchor=\"middle\"" + self.path_color = "#dffcd7" + self.box_color = "#ebf9e7" + + def head_box(self, x: int, y: int, height: int, text: str, bg_color: str) -> str: + para = text.split('/') + box = f"\n" + x_mid = x + int(self.box_width / 2) + y_begin = y + int(height / (len(para) + 1)) + 3 + for line in para: + box += f"{line}\n" + y_begin += 18 + return box + "\n" + + def box(self, x: int, y: int, height: int, text: str, bg_color: str) -> str: + para = text.split('/') + box = f"\n" + x_mid = x + int(self.box_width / 2) + y_begin = y + int(height / (len(para) + 1)) + 2 + for line in para: + box += f"{line}\n" + y_begin += 16 + return box + + def line(self, x1: int, y1: int, x2: int, y2: int) -> str: + return f"\n" + + def arc_ne(self, x: int, y: int) -> str: + return f"\n" + + def arc_nw(self, x: int, y: int) -> str: + return f"\n" + + def arc_se(self, x: int, y: int) -> str: + return f"\n" + + def arc_sw(self, x: int, y: int) -> str: + return f"\n" + + def side_arrow_head(self, x: int, y: int, direction: int) -> str: + return f"\n" + + def up_arrow_head(self, x: int, y: int) -> str: + return f"\n" + + def up_arrow(self, x: int, y: int) -> str: + x_mid = x + int(self.box_width/2) + return f"\n" + self.up_arrow_head(x_mid, y - 10) + "\n" + + def column(self, name: str, dependent: list[str]): + x = self.x + self.column_count * (self.box_width + self.gutter) + self.column_count += 1 + y = self.box_height + out = self.head_box(x, y - 20, self.box_height + 20, name, "#abe797") + + n = 0 + for item in dependent: + y += self.y_offset + out += self.box(x, y, self.box_height, item, self.gradient[n]) + self.up_arrow(x, y) + n += 1 + + return out + + def dependent_column(self, right: bool, dependent: dict) -> str: + x = self.x + self.column_count * (self.box_width + self.gutter) + y = self.box_height + + # curving arrow to each adjacent columns + out = "" + for direction in [-1, 1]: + if direction == 1 and not right: + continue + x_mid = int(self.box_width / 2) + x_begin = x + x_mid + x_end = x_begin + (x_mid + self.gutter - 3) * direction + y_begin = y + self.y_offset + y_end = y_begin - self.box_height - 10 + out += f"\n\n" + out += self.side_arrow_head(x_end, y_end, direction) + "\n" + + n = 0 + for item in dependent: + y += self.y_offset + out += self.box(x, y, self.box_height, item, self.gradient[n]) + if n: + out += self.up_arrow(x, y) + n += 1 + + self.column_count += 1 + return out + + def horizontal(self, top: int, color: str, dependent: list[str]) -> str: + x = self.gutter + y = top + x_offset = self.box_width + self.gutter + out = "" + n = 0 + for item in dependent: + y_mid = y + int(self.box_height / 2) + out += self.box(x, y, self.box_height, item, color) + if n: + out +=f"\n" + self.side_arrow_head(x - 2, y_mid, 1) + n += 1 + x += x_offset + return out + + def svg(self) -> str: + out = (f""" + + + """).strip() + + out += self.column("MP3 V0", [ + "V2", + "any VBR", + ]) + + self.column_count += 1 + cbr = [ + "256 CBR", + "224 CBR", + "192 CBR", + "< 192 CBR", + ] + + out += self.column("MP3 320", cbr) + + # draw the arrows from the CBR boxes to V0 + x = self.x + 2 * (self.box_width + self.gutter) + x_end = x - 59 + y = 3 * (self.box_height) + for n in range(0, len(cbr)): + out += self.line(x, y, x_end, y) + out += self.arc_se(x_end - self.radius, y - self.radius) + y += self.y_offset + + y_top = self.chart_height - self.box_height - 20 + out += self.horizontal(y_top, self.gradient[4], [ + "192 AAC", + "256 AAC", + "320 AAC", + ]) + + # line from first horizontal box + x = self.gutter + int(self.box_width / 2) + out += self.arc_ne(x, y_top) + out += self.arc_sw(x + self.box_width + self.gutter, y_top - self.radius * 2) + x += self.radius - 1 + x_end = x + int((self.box_width + self.gutter) / 2) + 20 + y = y_top - self.radius + out += self.line(x, y, x_end, y) + + # line from middle horizontal box + x_end += self.radius + y_end = self.chart_height - self.box_height - 80 + out += self.line(x_end, y_end + self.radius * 2, x_end, y_end) + + # line from third horizontal box + out += self.arc_se(x_end, y_end) + x = int(self.gutter) / 2 + self.box_width * 2 - 10 + x_end = x + self.box_width - self.gutter + 10 + out += self.arc_nw(x_end, y_top) + + y = y_top - self.radius + out += self.line(x, y, x_end - self.radius, y) + + # line to top + x = self.x + int(self.gutter + 3/2 * self.box_width) + y_begin = self.y + int(self.box_height * 2 - 20) + out += self.line(x, y_begin, x, y_end) + out += self.arc_nw(x, y_begin) + y = y_begin - self.radius + x_end = x - int(self.box_width / 2) - self.gutter + 2 + out += self.line(x - self.radius, y, x_end, y) + out += self.side_arrow_head(x_end, y, -1) + + out += self.column("FLAC 100% Log/+ Cue + Checksum", [ + "FLAC 100% Log + Cue/No Checksum", + "FLAC 100% Log/No Checksum", + ]) + + out += self.dependent_column(False, [ + "FLAC < 100% Log/or No log", + ]) + + return out + "" + +c = Chart( + 810, + 450, + 138, # box width + 40, # box height + 20, # gutter + 20, # x + 20, # y + 60, # y_offset, how far below the next box is placed + 30, # radius of arcs +) + +print(c.svg()) diff --git a/public/static/svg/trump-chart.svg b/public/static/svg/trump-chart.svg new file mode 100644 index 000000000..b12810fa1 --- /dev/null +++ b/public/static/svg/trump-chart.svg @@ -0,0 +1,90 @@ + + +MP3 V0 + + +V2 + + + + +any VBR + + + + +MP3 320 + + +256 CBR + + + + +224 CBR + + + + +192 CBR + + + + +< 192 CBR + + + + + + + + + + + + +192 AAC + +256 AAC + + + +320 AAC + + + + + + + + + + + + + + +FLAC 100% Log ++ Cue + Checksum + + +FLAC 100% Log + Cue +No Checksum + + + + +FLAC 100% Log +No Checksum + + + + + + + + +FLAC < 100% Log +or No log +