ziem-io commited on
Commit
cd07d29
·
1 Parent(s): be26fa3

New: SVG as python

Browse files
Files changed (1) hide show
  1. app.py +95 -128
app.py CHANGED
@@ -22,6 +22,9 @@ from safetensors.torch import load_file # Sicheres & schnelles Laden von We
22
  # UI / Serving
23
  import gradio as gr # Web-UI für Demo/Spaces
24
 
 
 
 
25
  # Projektspezifische Module
26
  from lib.bert_regressor import BertMultiHeadRegressor
27
  from lib.bert_regressor_utils import (
@@ -111,6 +114,90 @@ def is_eng(review: str):
111
 
112
  return lang_label[1] == "__label__en", lang_prob
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  ### Do actual prediction #########################################################
115
  @spaces.GPU(duration=10) # Sekunden GPU-Zeit pro Call
116
  def predict(review: str):
@@ -133,90 +220,12 @@ def predict(review: str):
133
 
134
  #html_out = f"<b>{html.escape(review)}</b>"
135
 
136
- html_out = '''
137
- <svg id="wheel" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
138
-
139
- <defs>
140
- <path id="textPathU-68cfc5fd8ae1e" 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;" />
141
- <path id="textPathL-68cfc5fd8ae1e" 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;" />
142
- </defs>
143
-
144
- <circle class="circle" r="40" cx="250" cy="250" style="stroke: black; fill: none;" />
145
- <circle class="circle" r="80" cx="250" cy="250" style="stroke: black; fill: none;" />
146
- <circle class="circle" r="120" cx="250" cy="250" style="stroke: black; fill: none;" />
147
- <circle class="circle" r="160" cx="250" cy="250" style="stroke: black; fill: none;" />
148
- <circle class="circle" r="200" cx="250" cy="250" style="stroke: black; fill: none;" />
149
-
150
- <line class="line" x1="250" y1="210" x2="250" y2="50" style="stroke: black;" />
151
- <line class="line" x1="278.28427124746" y1="221.71572875254" x2="391.42135623731" y2="108.57864376269" style="stroke: black;" />
152
- <line class="line" x1="290" y1="250" x2="450" y2="250" style="stroke: black;" />
153
- <line class="line" x1="278.28427124746" y1="278.28427124746" x2="391.42135623731" y2="391.42135623731" style="stroke: black;" />
154
- <line class="line" x1="250" y1="290" x2="250" y2="450" style="stroke: black;" />
155
- <line class="line" x1="221.71572875254" y1="278.28427124746" x2="108.57864376269" y2="391.42135623731" style="stroke: black;" />
156
- <line class="line" x1="210" y1="250" x2="50" y2="250" style="stroke: black;" />
157
- <line class="line" x1="221.71572875254" y1="221.71572875254" x2="108.57864376269" y2="108.57864376269" style="stroke: black;" />
158
-
159
- <text text-anchor="middle" dy="0">
160
- <textPath xlink:href="#textPathU-68cfc5fd8ae1e" startOffset="50%">
161
- Grainy
162
- </textPath>
163
- </text>
164
-
165
- <text text-anchor="middle" dy="0">
166
- <textPath xlink:href="#textPathU-68cfc5fd8ae1e" startOffset="62.5%">
167
- Grassy
168
- </textPath>
169
- </text>
170
-
171
- <text text-anchor="middle" dy="0">
172
- <textPath xlink:href="#textPathU-68cfc5fd8ae1e" startOffset="75%">
173
- Fragrant
174
- </textPath>
175
- </text>
176
-
177
- <text text-anchor="middle" dy="9px">
178
- <textPath xlink:href="#textPathL-68cfc5fd8ae1e" startOffset="62.5%">
179
- Fruity
180
- </textPath>
181
- </text>
182
-
183
- <text text-anchor="middle" dy="9px">
184
- <textPath xlink:href="#textPathL-68cfc5fd8ae1e" startOffset="50%">
185
- Peaty
186
- </textPath>
187
- </text>
188
-
189
- <text text-anchor="middle" dy="9px">
190
- <textPath xlink:href="#textPathL-68cfc5fd8ae1e" startOffset="37.5%">
191
- Woody
192
- </textPath>
193
- </text>
194
-
195
- <text text-anchor="middle" dy="0">
196
- <textPath xlink:href="#textPathU-68cfc5fd8ae1e" startOffset="25%">
197
- Winey
198
- </textPath>
199
- </text>
200
-
201
- <text text-anchor="middle" dy="0">
202
- <textPath xlink:href="#textPathU-68cfc5fd8ae1e" startOffset="37.5%">
203
- Off-Notes
204
- </textPath>
205
- </text>
206
-
207
- <polyline class="graph" points></polyline>
208
-
209
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
210
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
211
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
212
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
213
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
214
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
215
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
216
- <circle class="point" style="stroke: black; fill: white;" cx="250" cy="250" r="3.5" />
217
-
218
- </svg>
219
 
 
 
 
 
220
  <style>
221
  svg#wheel > .circle,
222
  svg#wheel > .line {
@@ -231,56 +240,14 @@ def predict(review: str):
231
  fill-opacity: 0.9;
232
  }
233
  svg#wheel > text {
234
- font-family: 'Roboto Condensed', sans-serif;
235
  font-style: normal;
236
  font-weight: 400;
237
  font-size: 18px;
238
  // text-transform: uppercase;
239
- &.scale {
240
- fill: rgb(180, 180, 180);
241
- }
242
  }
243
  </style>
244
-
245
- <script>
246
- const setPoints = function (vals) {
247
-
248
- const elemsPoint = document.querySelectorAll('.point');
249
- const elemGraph = document.querySelector('.graph');
250
-
251
- const points = [];
252
-
253
- elemsPoint.forEach(function(elemPoint, i) {
254
- // steps offset convert to bogenmass
255
- const angle = (360 / config.segments * i + config.offsetAngle - 90) * (Math.PI * 2 / 360);
256
-
257
- const val = vals[i];
258
-
259
- const c = {
260
- x: Math.cos(angle) * (val * config.inputRatio + config.innerRadius) + config.offsetCenter,
261
- y: Math.sin(angle) * (val * config.inputRatio + config.innerRadius) + config.offsetCenter
262
- }
263
-
264
- points.push(`${c.x},${c.y}`);
265
-
266
- elemGraph.setAttribute('points', points.join(' '));
267
-
268
- elemPoint.setAttribute('cx', `${c.x}`);
269
- elemPoint.setAttribute('cy', `${c.y}`);
270
- });
271
- }
272
-
273
- const config = {
274
- segments: 8,
275
- offsetAngle: 0,
276
- innerRadius: 40,
277
- offsetCenter: 250,
278
- inputRatio: 16
279
- };
280
-
281
- setPoints([0, 1, 1.5, 0, 2, 0, 3.4, 2])
282
- </script>
283
- '''
284
 
285
  json_out = {
286
  "review": review,
@@ -302,7 +269,7 @@ def predict(review: str):
302
  }
303
 
304
  return html_out, json_out
305
-
306
  ### Create Form interface with Gradio Framework ##################################
307
  iface = gr.Interface(
308
  fn=predict,
 
22
  # UI / Serving
23
  import gradio as gr # Web-UI für Demo/Spaces
24
 
25
+ import math
26
+ from typing import List, Tuple
27
+
28
  # Projektspezifische Module
29
  from lib.bert_regressor import BertMultiHeadRegressor
30
  from lib.bert_regressor_utils import (
 
114
 
115
  return lang_label[1] == "__label__en", lang_prob
116
 
117
+ ### Wheel ##################################
118
+
119
+ # ---------- Geometrie: JS -> Python ----------
120
+ def compute_wheel_points(
121
+ vals: List[float],
122
+ segments: int = 8,
123
+ offset_angle_deg: float = 0.0,
124
+ inner_radius: float = 40.0,
125
+ center: float = 250.0,
126
+ input_ratio: float = 16.0,
127
+ ) -> List[Tuple[float, float]]:
128
+ """
129
+ Repliziert die JS-Logik:
130
+ angle = (360/segments*i + offsetAngle - 90) * (π*2/360)
131
+ r = vals[i] * inputRatio + innerRadius
132
+ x = cos(angle) * r + center
133
+ y = sin(angle) * r + center
134
+ """
135
+ pts = []
136
+ for i, v in enumerate(vals):
137
+ angle_deg = (360.0 / segments) * i + offset_angle_deg - 90.0
138
+ angle = angle_deg * (math.pi * 2.0 / 360.0)
139
+ r = v * input_ratio + inner_radius
140
+ x = math.cos(angle) * r + center
141
+ y = math.sin(angle) * r + center
142
+ pts.append((x, y))
143
+ return pts
144
+
145
+ # ---------- SVG-Bau ----------
146
+ SVG_BASE = """<svg id="wheel" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
147
+ <!-- Ringe -->
148
+ <circle class="circle" r="40" cx="250" cy="250" style="stroke:black; fill:none" />
149
+ <circle class="circle" r="80" cx="250" cy="250" style="stroke:black; fill:none" />
150
+ <circle class="circle" r="120" cx="250" cy="250" style="stroke:black; fill:none" />
151
+ <circle class="circle" r="160" cx="250" cy="250" style="stroke:black; fill:none" />
152
+ <circle class="circle" r="200" cx="250" cy="250" style="stroke:black; fill:none" />
153
+
154
+ <!-- Achsen -->
155
+ <line class="line" x1="250" y1="210" x2="250" y2="50" style="stroke:black" />
156
+ <line class="line" x1="278.28427124746" y1="221.71572875254" x2="391.42135623731" y2="108.57864376269" style="stroke:black" />
157
+ <line class="line" x1="290" y1="250" x2="450" y2="250" style="stroke:black" />
158
+ <line class="line" x1="278.28427124746" y1="278.28427124746" x2="391.42135623731" y2="391.42135623731" style="stroke:black" />
159
+ <line class="line" x1="250" y1="290" x2="250" y2="450" style="stroke:black" />
160
+ <line class="line" x1="221.71572875254" y1="278.28427124746" x2="108.57864376269" y2="391.42135623731" style="stroke:black" />
161
+ <line class="line" x1="210" y1="250" x2="50" y2="250" style="stroke:black" />
162
+ <line class="line" x1="221.71572875254" y1="221.71572875254" x2="108.57864376269" y2="108.57864376269" style="stroke:black" />
163
+
164
+ <!-- Textpfade & Labels (dein Original kannst du hier einfügen) -->
165
+ {LABELS}
166
+
167
+ <!-- Fläche -->
168
+ <polyline class="graph" points="{POINTS}" style="fill:rgb(250,208,72); fill-opacity:0.9" />
169
+
170
+ <!-- Punkte -->
171
+ {POINT_CIRCLES}
172
+ </svg>"""
173
+
174
+ def build_point_circles(points: List[Tuple[float, float]]) -> str:
175
+ return "\n ".join(
176
+ f'<circle class="point" style="stroke:black; fill:white" cx="{x:.2f}" cy="{y:.2f}" r="3.5" />'
177
+ for x, y in points
178
+ )
179
+
180
+ def build_svg_with_values(
181
+ vals: List[float],
182
+ labels_svg: str = "",
183
+ segments: int = 8,
184
+ offset_angle_deg: float = 0.0,
185
+ inner_radius: float = 40.0,
186
+ center: float = 250.0,
187
+ input_ratio: float = 16.0,
188
+ ) -> str:
189
+ pts = compute_wheel_points(
190
+ vals,
191
+ segments=segments,
192
+ offset_angle_deg=offset_angle_deg,
193
+ inner_radius=inner_radius,
194
+ center=center,
195
+ input_ratio=input_ratio,
196
+ )
197
+ points_attr = " ".join(f"{x:.2f},{y:.2f}" for x, y in pts)
198
+ circles = build_point_circles(pts)
199
+ return SVG_BASE.format(LABELS=labels_svg, POINTS=points_attr, POINT_CIRCLES=circles)
200
+
201
  ### Do actual prediction #########################################################
202
  @spaces.GPU(duration=10) # Sekunden GPU-Zeit pro Call
203
  def predict(review: str):
 
220
 
221
  #html_out = f"<b>{html.escape(review)}</b>"
222
 
223
+ values = [0, 1, 1.5, 0, 2, 0, 3.4, 2] # deine 8 Werte
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
+ svg = build_svg_with_values(values, labels_svg="")
226
+
227
+ html_out = """
228
+ {svg}
229
  <style>
230
  svg#wheel > .circle,
231
  svg#wheel > .line {
 
240
  fill-opacity: 0.9;
241
  }
242
  svg#wheel > text {
243
+ font-family: "Source Sans Pro", ui-sans-serif, system-ui, sans-serif;
244
  font-style: normal;
245
  font-weight: 400;
246
  font-size: 18px;
247
  // text-transform: uppercase;
 
 
 
248
  }
249
  </style>
250
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
  json_out = {
253
  "review": review,
 
269
  }
270
 
271
  return html_out, json_out
272
+
273
  ### Create Form interface with Gradio Framework ##################################
274
  iface = gr.Interface(
275
  fn=predict,