Download app.py from by3axap/Patternv2: direct link, hf CLI and curl.
- Browser
- Download file 5.82 kB
-
https://huggingface.co/spaces/by3axap/Patternv2/resolve/main/app.py
- Command line
-
hf download hf://spaces/by3axap/Patternv2/app.py
-
curl -L -o app.py https://huggingface.co/spaces/by3axap/Patternv2/resolve/main/app.py
5.82 kB
| import math | |
| import re | |
| from flask import Flask, render_template, request | |
| from PIL import Image, ImageOps | |
| app = Flask(__name__) | |
| def prepare_svg_symbol(file_storage): | |
| try: | |
| raw_data = file_storage.read().decode('utf-8') | |
| vb_match = re.search(r'viewBox=["\']([^"\']+)["\']', raw_data, re.I) | |
| vbox = vb_match.group(1) if vb_match else "0 0 100 100" | |
| inner = re.sub(r'<svg[^>]*>', '', raw_data, flags=re.I) | |
| inner = re.sub(r'</svg>', '', inner, flags=re.I) | |
| inner = re.sub(r'<\?xml[^>]*\?>|<!DOCTYPE[^>]*>', '', inner, flags=re.I) | |
| if 'fill=' not in inner: | |
| inner = f'<g fill="black">{inner}</g>' | |
| return inner, vbox | |
| except: | |
| return '<rect width="100" height="100" fill="black"/>', "0 0 100 100" | |
| def index(): | |
| return render_template('index.html') | |
| def generate(): | |
| try: | |
| # --- Считываем параметры --- | |
| density = int(request.form.get('density', 40)) | |
| gap_x = float(request.form.get('gap_x', 0)) | |
| gap_y = float(request.form.get('gap_y', 0)) | |
| shift_stagger = float(request.form.get('shift_stagger', 0)) | |
| base_scale = float(request.form.get('base_scale', 1.0)) | |
| growth = float(request.form.get('growth', 0)) | |
| growth_type = request.form.get('growth_type', 'sine') | |
| invert_mode = request.form.get('invert') == 'true' | |
| # --- Параметры холста --- | |
| canvas_w = 1400 | |
| canvas_h = 900 | |
| step = canvas_w / density | |
| rows = math.ceil(canvas_h / step) | |
| # --- Подготовка шейпа --- | |
| svg_file = request.files.get('shape_svg') | |
| if svg_file and svg_file.filename != '': | |
| symbol_content, v_box = prepare_svg_symbol(svg_file) | |
| else: | |
| symbol_content, v_box = ('<rect width="100" height="100" fill="black"/>', "0 0 100 100") | |
| if request.form.get('current_svg_symbol'): | |
| symbol_content = request.form.get('current_svg_symbol') | |
| v_box = request.form.get('current_vbox', "0 0 100 100") | |
| vb_parts = [float(x) for x in v_box.split()] | |
| vb_w, vb_h = vb_parts[2], vb_parts[3] | |
| # --- Обработка Растрового Изображения (МАСКА) --- | |
| raster_file = request.files.get('raster_image') | |
| pixel_map = None | |
| if raster_file and raster_file.filename != '': | |
| try: | |
| img = Image.open(raster_file) | |
| img = ImageOps.fit(img, (canvas_w, canvas_h), method=Image.Resampling.LANCZOS) | |
| img = ImageOps.grayscale(img) | |
| pixel_map = img.load() | |
| except: | |
| pass | |
| # --- Генерация SVG --- | |
| body = [] | |
| cell_w_max = step * (1.0 - gap_x) | |
| cell_h_max = step * (1.0 - gap_y) | |
| for r in range(rows): | |
| row_shift = (r * shift_stagger * step) | |
| for c in range(-density, density * 2): | |
| grid_x = c * step + row_shift | |
| grid_y = r * step | |
| if grid_x < -step or grid_x > canvas_w: | |
| continue | |
| # 1. ПРОВЕРКА ПО КАРТИНКЕ (ВИДИМОСТЬ) | |
| if pixel_map: | |
| sample_x = max(0, min(int(grid_x + step/2), canvas_w - 1)) | |
| sample_y = max(0, min(int(grid_y + step/2), canvas_h - 1)) | |
| brightness = pixel_map[sample_x, sample_y] / 255.0 | |
| # Порог видимости: если яркость выше 0.5 — считаем это светом | |
| is_visible = brightness > 0.5 if invert_mode else brightness < 0.5 | |
| if not is_visible: | |
| continue | |
| # 2. ВЫЧИСЛЕНИЕ ШИРИНЫ ПО МАТЕМАТИЧЕСКОМУ АЛГОРИТМУ | |
| wave_norm = 0 | |
| if growth_type == 'sine': | |
| # Диагональная волна | |
| wave_norm = (math.sin(c * 0.3 + r * 0.3) + 1) / 2 | |
| elif growth_type == 'linear': | |
| # Слева направо | |
| wave_norm = (c % density) / density | |
| elif growth_type == 'steps': | |
| # Ступеньки | |
| wave_norm = (r % 5) / 5.0 | |
| # Модификатор ширины: чем больше growth, тем сильнее сужается объект | |
| # При growth = 0 ширина всегда 1.0 (максимум) | |
| mod_w = 1.0 - (wave_norm * growth) | |
| mod_w = max(0.01, min(1.0, mod_w)) | |
| # Итоговые размеры | |
| content_w = cell_w_max * base_scale * mod_w | |
| content_h = cell_h_max * base_scale | |
| cx = grid_x + step / 2 | |
| cy = grid_y + step / 2 | |
| scale_x = content_w / vb_w | |
| scale_y = content_h / vb_h | |
| transform = f'translate({cx:.2f} {cy:.2f}) scale({scale_x:.4f} {scale_y:.4f}) translate({-vb_w/2:.2f} {-vb_h/2:.2f})' | |
| body.append(f'<g transform="{transform}">{symbol_content}</g>') | |
| return f'<svg xmlns="http://www.w3.org/2000/svg" width="{canvas_w}" height="{canvas_h}" viewBox="0 0 {canvas_w} {canvas_h}" style="background:white;">{"".join(body)}</svg>' | |
| except Exception as e: | |
| return f'<svg width="800" height="200"><text y="50" fill="red" font-size="20">Error: {str(e)}</text></svg>', 200 | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860) |