Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| # Utilities for constructing the whisky flavor wheel SVG. | |
| from typing import List, Tuple | |
| import math | |
| ################################################################################### | |
| # Base SVG template with placeholder for the dynamic graph coordinates. | |
| SVG_BASE = """ | |
| <svg id="wheel" | |
| viewBox="0 0 500 500" | |
| xmlns="http://www.w3.org/2000/svg" | |
| xmlns:xlink="http://www.w3.org/1999/xlink"> | |
| <defs> | |
| <path id="textPathU-68cfdea07e622" d="M 250, 250 m 0, 220 a -220,-220 0 1,1 0,-440 a -220,-220 0 1,1 0,440" style="stroke: red; fill: none;" /> | |
| <path id="textPathL-68cfdea07e622" d="M 250, 250 m 0, -220 a 220,220 0 1,0 0,440 a 220,220 0 1,0 0,-440" style="stroke: red; fill: none;" /> | |
| </defs> | |
| <!-- Ringe --> | |
| <circle class="circle" r="40" cx="250" cy="250" style="stroke:rgb(180, 180, 180); fill:none" /> | |
| <circle class="circle" r="80" cx="250" cy="250" style="stroke:rgb(180, 180, 180); fill:none" /> | |
| <circle class="circle" r="120" cx="250" cy="250" style="stroke:rgb(180, 180, 180); fill:none" /> | |
| <circle class="circle" r="160" cx="250" cy="250" style="stroke:rgb(180, 180, 180); fill:none" /> | |
| <circle class="circle" r="200" cx="250" cy="250" style="stroke:rgb(180, 180, 180); fill:none" /> | |
| <!-- Achsen --> | |
| <line class="line" x1="250" y1="210" x2="250" y2="50" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="278.28427124746" y1="221.71572875254" x2="391.42135623731" y2="108.57864376269" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="290" y1="250" x2="450" y2="250" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="278.28427124746" y1="278.28427124746" x2="391.42135623731" y2="391.42135623731" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="250" y1="290" x2="250" y2="450" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="221.71572875254" y1="278.28427124746" x2="108.57864376269" y2="391.42135623731" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="210" y1="250" x2="50" y2="250" style="stroke:rgb(180, 180, 180)" /> | |
| <line class="line" x1="221.71572875254" y1="221.71572875254" x2="108.57864376269" y2="108.57864376269" style="stroke:rgb(180, 180, 180)" /> | |
| <!-- Beschriftung --> | |
| <text text-anchor="middle" dy="0"> | |
| <textPath xlink:href="#textPathU-68cfdea07e622" startOffset="50%"> | |
| Grainy | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="0"> | |
| <textPath xlink:href="#textPathU-68cfdea07e622" startOffset="62.5%"> | |
| Grassy | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="0"> | |
| <textPath xlink:href="#textPathU-68cfdea07e622" startOffset="75%"> | |
| Fragrant | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="9px"> | |
| <textPath xlink:href="#textPathL-68cfdea07e622" startOffset="62.5%"> | |
| Fruity | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="9px"> | |
| <textPath xlink:href="#textPathL-68cfdea07e622" startOffset="50%"> | |
| Peaty | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="9px"> | |
| <textPath xlink:href="#textPathL-68cfdea07e622" startOffset="37.5%"> | |
| Woody | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="0"> | |
| <textPath xlink:href="#textPathU-68cfdea07e622" startOffset="25%"> | |
| Winey | |
| </textPath> | |
| </text> | |
| <text text-anchor="middle" dy="0"> | |
| <textPath xlink:href="#textPathU-68cfdea07e622" startOffset="37.5%"> | |
| Off-Notes | |
| </textPath> | |
| </text> | |
| <!-- Fläche --> | |
| <polyline class="graph" points="{POINTS}" style="fill:rgb(250,208,72); fill-opacity:0.9" /> | |
| </svg> | |
| """ | |
| ################################################################################### | |
| def compute_wheel_points( | |
| vals: List[float], | |
| segments: int = 8, | |
| offset_angle_deg: float = 0.0, | |
| inner_radius: float = 40.0, | |
| center: float = 250.0, | |
| input_ratio: float = 40.0, | |
| ) -> List[Tuple[float, float]]: | |
| # Repliziert die JS-Logik: | |
| # angle = (360/segments*i + offsetAngle - 90) * (π*2/360) | |
| # r = vals[i] * inputRatio + innerRadius | |
| # x = cos(angle) * r + center | |
| # y = sin(angle) * r + center | |
| pts = [] | |
| for i, v in enumerate(vals): | |
| # Align each segment by converting the polar angle to radians and shifting upwards. | |
| angle_deg = (360.0 / segments) * i + offset_angle_deg - 90.0 | |
| angle = angle_deg * (math.pi * 2.0 / 360.0) | |
| # Map the normalized input value onto the wheel radius and translate back to SVG center. | |
| r = v * input_ratio + inner_radius | |
| x = math.cos(angle) * r + center | |
| y = math.sin(angle) * r + center | |
| pts.append((x, y)) | |
| return pts | |
| ################################################################################### | |
| def build_svg_with_values( | |
| vals: List[float], | |
| segments: int = 8, | |
| offset_angle_deg: float = 0.0, | |
| inner_radius: float = 40.0, | |
| center: float = 250.0, | |
| input_ratio: float = 40.0, | |
| ) -> str: | |
| # Populate the SVG template with cartesian points generated from the input values. | |
| pts = compute_wheel_points( | |
| vals, | |
| segments=segments, | |
| offset_angle_deg=offset_angle_deg, | |
| inner_radius=inner_radius, | |
| center=center, | |
| input_ratio=input_ratio, | |
| ) | |
| # Build the `points` attribute string in the format expected by the SVG polyline element. | |
| points_attr = " ".join(f"{x:.2f},{y:.2f}" for x, y in pts) | |
| return SVG_BASE.format(POINTS=points_attr) | |