Added route to dynamically generate logo.svg.

This commit is contained in:
Fabio Manganiello 2023-05-20 01:18:48 +02:00
parent c5aee0a65d
commit 075efde58c
Signed by: blacklight
GPG key ID: D90FBA7F76362774
3 changed files with 229 additions and 0 deletions

View file

@ -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"""
<circle
cx="{self.center[0]}" cy="{self.center[1]}"
r="{self.outer_radius - (self.inner_radius / math.pi)}"
stroke-width="{self.inner_radius}"
stroke="{self.color}"
fill="none" />
"""
# 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"""
<polygon points="{svg_points}" stroke="{self.color}" fill="{self.color}" />"""
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 = """
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.1"
width="{width}" height="{height}"
viewBox="0 0 100 100"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="triangleGradient">
<stop offset="0%" stop-color="#8acb45" />
<stop offset="50%" stop-color="#6bbb4c" />
<stop offset="100%" stop-color="#5cb450" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="{bg_color}" />
"""
template_end = "\n</svg>"
@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<polygon points="67,47 67,3 99,25.3" fill="url(#triangleGradient)" />"""
svg += template_end
rs = make_response(svg)
rs.headers.update({"Content-Type": "image/svg+xml"})
return rs
# vim:sw=4:ts=4:et:

View file

@ -0,0 +1,41 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.1"
width="256" height="256"
viewBox="0 0 100 100"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="triangleGradient">
<stop offset="0%" stop-color="#8acb45" />
<stop offset="50%" stop-color="#6bbb4c" />
<stop offset="100%" stop-color="#5cb450" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="none" />
<circle cx="32.9" cy="34.5" r="18.652957411320997" stroke-width="12.4" stroke="#000000" fill="none" />
<polygon points="55.047460351019936,39.0 55.047460351019936,30.0 65.13584176553678,32.35 65.13584176553678,36.65" stroke="#000000" fill="#000000" />
<polygon points="49.830263293291885,49.47084449253994 54.330263293291885,41.67661585847999 61.89205788133026,48.755966264631844 59.742057881330254,52.479875500904924" stroke="#000000" fill="#000000" />
<polygon points="40.07661585848,55.93026329329189 47.870844492539945,51.43026329329188 50.87987550090493,61.342057881330255 47.15596626463184,63.492057881330254" stroke="#000000" fill="#000000" />
<polygon points="28.4,56.64746035101994 37.4,56.64746035101993 35.050000000000004,66.73584176553678 30.75,66.73584176553678" stroke="#000000" fill="#000000" />
<polygon points="17.92915550746006,51.43026329329189 25.72338414152001,55.93026329329189 18.644033735368154,63.492057881330254 14.920124499095078,61.34205788133026" stroke="#000000" fill="#000000" />
<polygon points="11.469736706708115,41.67661585847999 15.969736706708115,49.47084449253994 6.0579421186697395,52.479875500904924 3.9079421186697445,48.75596626463185" stroke="#000000" fill="#000000" />
<polygon points="10.752539648980061,30.000000000000004 10.752539648980065,39.0 0.6641582344632226,36.65 0.6641582344632226,32.35000000000001" stroke="#000000" fill="#000000" />
<polygon points="15.969736706708108,19.52915550746006 11.469736706708112,27.323384141520012 3.907942118669741,20.24403373536816 6.057942118669736,16.520124499095083" stroke="#000000" fill="#000000" />
<polygon points="25.723384141519993,13.06973670670812 17.92915550746005,17.569736706708124 14.92012449909506,7.657942118669752 18.644033735368136,5.507942118669753" stroke="#000000" fill="#000000" />
<polygon points="37.39999999999999,12.352539648980063 28.399999999999995,12.352539648980066 30.749999999999996,2.264158234463224 35.04999999999999,2.264158234463224" stroke="#000000" fill="#000000" />
<polygon points="47.870844492539945,17.569736706708117 40.07661585848,13.069736706708117 47.15596626463185,5.507942118669746 50.87987550090493,7.6579421186697445" stroke="#000000" fill="#000000" />
<polygon points="54.33026329329188,27.323384141519995 49.83026329329188,19.52915550746005 59.74205788133025,16.52012449909506 61.892057881330246,20.244033735368134" stroke="#000000" fill="#000000" />
<circle cx="65.5" cy="70.5" r="11.69436596743778" stroke-width="8.5" stroke="#000000" fill="none" />
<polygon points="75.59619697596976,80.76775567601894 79.72023567151646,72.76823663812462 85.21241914871936,78.24356296406287 83.2420451052915,82.06555539327904" stroke="#000000" fill="#000000" />
<polygon points="63.76722121027272,84.79536560098661 72.59279310784501,83.03205034817951 71.73633099741353,90.73982242421863 67.51966886857343,91.58229526722646" stroke="#000000" fill="#000000" />
<polygon points="53.24306321548609,78.05827365609566 60.1243326673541,83.85897453881562 53.56415840708632,87.99508278180117 50.276440780082716,85.22363680450164" stroke="#000000" fill="#000000" />
<polygon points="51.94862861538407,65.62964748747973 51.70385937197527,74.62631842830451 44.379917983020505,72.07618897003609 44.49686328820471,67.7777795205309" stroke="#000000" fill="#000000" />
<polygon points="60.85865306549732,56.86849609787744 53.67216135948117,62.28646037972363 51.09953008890019,54.970392715438265 54.53307612621901,52.381809780778426" stroke="#000000" fill="#000000" />
<polygon points="73.2637064235145,58.37214517857838 64.54706708723641,56.131565190885546 68.66298975389094,49.55870749237323 72.82760632566824,50.62920681982647" stroke="#000000" fill="#000000" />
<polygon points="79.82253049387555,69.00831630296325 76.13955073459155,60.796394475966565 83.84465362096915,59.916242652069755 85.60429950596037,63.83971641385705" stroke="#000000" fill="#000000" />
<polygon points="67,47 67,3 99,25.3" fill="url(#triangleGradient)" />
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB