From 075efde58cf685201c2f2b89331850e9edb0e079 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sat, 20 May 2023 01:18:48 +0200 Subject: [PATCH] Added route to dynamically generate logo.svg. --- platypush/backend/http/app/routes/logo.py | 188 ++++++++++++++++++ .../http/webapp/dist/img/icons/favicon.svg | 41 ++++ .../backend/http/webapp/dist/img/logo.png | Bin 20925 -> 0 bytes 3 files changed, 229 insertions(+) create mode 100644 platypush/backend/http/app/routes/logo.py create mode 100644 platypush/backend/http/webapp/dist/img/icons/favicon.svg delete mode 100644 platypush/backend/http/webapp/dist/img/logo.png diff --git a/platypush/backend/http/app/routes/logo.py b/platypush/backend/http/app/routes/logo.py new file mode 100644 index 000000000..6e4949294 --- /dev/null +++ b/platypush/backend/http/app/routes/logo.py @@ -0,0 +1,188 @@ +from dataclasses import dataclass +import math +from typing import Tuple + +from flask import Blueprint, make_response, request + + +logo = Blueprint('logo', __name__) + +# Declare routes list +__routes__ = [ + logo, +] + + +@dataclass +class Gear: + """ + A utility class used to model the gears in the application's logo. + """ + + center: Tuple[float, float] + outer_radius: float + inner_radius: float + color: str = "currentColor" + background: str = "" + num_spikes: int = 0 + spike_max_base: float = 0 + spike_min_base: float = 0 + spike_height: float = 0 + alpha_offset: float = 0 + + def to_svg(self) -> str: + """ + Returns the SVG representation of a gear. + """ + + # Generate the basic circle + svg = f""" + + """ + + # Generate the spikes + for i in range(self.num_spikes): + # Iterate for alpha -> [0, 2*pi] + alpha = (2 * math.pi * i) / self.num_spikes + # Calculate the base angle for the major base of the gear polygon + maj_delta_alpha = math.asin(self.spike_max_base / (2 * self.outer_radius)) + # Calculate the points of the gear polygon's major base + maj_base = ( + ( + self.center[0] + + self.outer_radius + * math.cos(alpha + maj_delta_alpha + self.alpha_offset), + self.center[1] + + self.outer_radius + * math.sin(alpha + maj_delta_alpha + self.alpha_offset), + ), + ( + self.center[0] + + self.outer_radius + * math.cos(alpha - maj_delta_alpha + self.alpha_offset), + self.center[1] + + self.outer_radius + * math.sin(alpha - maj_delta_alpha + self.alpha_offset), + ), + ) + + # Height of the gear relative to the circle's center + h = self.outer_radius * math.cos(maj_delta_alpha) + self.spike_height + # Calculate the base angle for the minor base of the gear polygon + min_delta_alpha = math.asin(self.spike_min_base / (2 * h)) + # Calculate the points of the gear polygon's minor base + min_base = ( + ( + self.center[0] + + h * math.cos(alpha - min_delta_alpha + self.alpha_offset), + self.center[1] + + h * math.sin(alpha - min_delta_alpha + self.alpha_offset), + ), + ( + self.center[0] + + h * math.cos(alpha + min_delta_alpha + self.alpha_offset), + self.center[1] + + h * math.sin(alpha + min_delta_alpha + self.alpha_offset), + ), + ) + + # Flatten the polygon's points + svg_points = " ".join( + [f"{point[0]},{point[1]}" for point in [*maj_base, *min_base]] + ) + + # Serialize the gear polygon to SVG + svg += f""" + """ + + return svg + + +# Properties of the two gears on the logo +gears = [ + Gear( + center=(32.9, 34.5), + outer_radius=22.6, + inner_radius=12.4, + num_spikes=12, + spike_max_base=9, + spike_min_base=4.3, + spike_height=10.16, + ), + Gear( + center=(65.5, 70.5), + outer_radius=14.4, + inner_radius=8.5, + num_spikes=7, + spike_max_base=9, + spike_min_base=4.3, + spike_height=7.5, + alpha_offset=math.pi / 6.6, + ), +] + + +template_start = """ + + + + + + + + + + + + +""" + +template_end = "\n" + + +@logo.route('/logo.svg', methods=['GET']) +def logo_path(): + """ + This path dynamically generates the logo image as a parametrizable vector SVG. + + Parameters: + + - ``size``: Size of the image in pixels (default: 256) + - ``bg``: Background color (default: "none") + - ``fg``: Foreground color (default: "currentColor") + + """ + size = request.args.get("size", 256) + bg = request.args.get("bg", "none") + fg = request.args.get("fg", "currentColor") + svg = template_start.format( + width=size, + height=size, + bg_color=bg, + ) + + for gear in gears: + gear.color = fg + gear.background = bg + svg += gear.to_svg() + + # "Play" triangle on the logo + svg += """\n\t\t""" + svg += template_end + + rs = make_response(svg) + rs.headers.update({"Content-Type": "image/svg+xml"}) + return rs + + +# vim:sw=4:ts=4:et: diff --git a/platypush/backend/http/webapp/dist/img/icons/favicon.svg b/platypush/backend/http/webapp/dist/img/icons/favicon.svg new file mode 100644 index 000000000..8546ef422 --- /dev/null +++ b/platypush/backend/http/webapp/dist/img/icons/favicon.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platypush/backend/http/webapp/dist/img/logo.png b/platypush/backend/http/webapp/dist/img/logo.png deleted file mode 100644 index e4da43088d30bca7f2f286aab48af92013692f8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20925 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