Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ jobs:
build-args: |
GIT_COMMIT=${{ env.BUILD_HASH }}
DEBUG=False
tags: ${{ env.CUSTOM_TAG }}
tags: ${{ env.CUSTOM_TAG }},ghcr.io/snowsune/size-diff:latest
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ cache
*.*~
.env
*.sqlite3
art/dist/
3 changes: 3 additions & 0 deletions Dockerfile → Legacy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ RUN pip install --upgrade pip && pip install -r requirements.txt
# Copy the app code to the container
COPY . .

# Run the art trimming script
RUN python3 scripts/trim_art.py

# Expose port 5000 for the Flask app
EXPOSE 5000

Expand Down
20 changes: 20 additions & 0 deletions Legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Anthropomorphic Size Diff tool!

Find it at https://size-diff.snowsune.net/


## Development

This project uses pipenv to manage the python environment!

To get started:

```shell
pipenv shell
flask run --debug
```


## Artists!

None so far! Be the first!
149 changes: 145 additions & 4 deletions app/__init__.py → Legacy/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
get_default_characters,
)
from app.utils.stats import StatsManager
from app.utils.generate_image import render_image
from app.utils.generate_image_legacy import render_image
from app.utils.character import Character

app = Flask(__name__)
Expand Down Expand Up @@ -83,7 +83,7 @@ def wrapped(*args, **kwargs):

@app.route("/generate-image")
@cache_with_stats(timeout=31536000, query_string=True)
def generate_image():
def generate_image_legacy():
# Get characters
characters = request.args.get("characters", "")
characters_list = extract_characters(characters)
Expand Down Expand Up @@ -152,6 +152,13 @@ def index():
characters = request.args.get("characters", "")
characters_list = extract_characters(characters)

# Process characters to populate image and ears_offset from species data
processed_characters = []
for char in characters_list:
processed_char = calculate_height_offset(char, use_species_scaling=False)
processed_characters.append(processed_char)
characters_list = processed_characters

# Extract settings from query string
measure_ears = request.args.get("measure_ears", "true") == "true"
scale_height = request.args.get("scale_height", "false") == "true"
Expand All @@ -165,12 +172,17 @@ def index():

# Insert default character values if none exist
if len(characters_list) == 0:
characters_list = get_default_characters()
default_chars = get_default_characters()
# Process default characters to populate image and ears_offset
characters_list = []
for char in default_chars:
processed_char = calculate_height_offset(char, use_species_scaling=False)
characters_list.append(processed_char)

# Load presets for the dropdown
presets = load_preset_characters()
preset_map = {
f"{p['name']} ({p['species'].replace('_', ' ').title()}, {p['gender']}, {p['height']}in)": f"{p['species']},{p['gender']},{p['height']},{p['name']}"
f"{p['name'].replace('_', ' ').title()} --- {p['species'].replace('_', ' ').title()}, {p['gender']}, {p.get('description', '')}": f"{p['species']},{p['gender']},{p['height']},{p['name']}"
for p in presets
}

Expand Down Expand Up @@ -218,12 +230,32 @@ def index():
settings_query = f"&measure_ears=false" if not measure_ears else ""
settings_query += f"&scale_height=true" if scale_height else ""

# Convert Character objects to JSON-serializable dictionaries for JavaScript
characters_json_data = []
for char in characters_list:
char_dict = {
"name": char.name,
"species": char.species,
"gender": char.gender,
"height": char.height,
"feral_height": char.feral_height,
"image": char.image,
"ears_offset": char.ears_offset,
"color": getattr(char, "color", None),
}
characters_json_data.append(char_dict)

import json

characters_json = json.dumps(characters_json_data)

return render_template(
"index.html",
stats=stats,
cache_performance=f"{cache_stats['hits']}/{cache_stats['misses']}",
species=species_list,
characters_list=characters_list,
characters_json=characters_json,
characters_query=generate_characters_query_string(characters_list),
settings_query=settings_query,
measure_ears=measure_ears,
Expand Down Expand Up @@ -300,6 +332,115 @@ def add_preset():
return redirect(f"/?characters={characters_query}{settings_query}")


@app.route("/taur")
def taur():
"""
Base route for volnar's sub-page!
"""

return render_template("taur.html")


# Interactive demo route
@app.route("/interactive-demo")
def interactive_demo():
return render_template("interactive_demo.html")


# Serve species data images for the client-side renderer
@app.route("/species_data/<filename>")
def serve_species_image(filename):
from flask import send_from_directory
import os

# Get the absolute path to the species_data directory
species_data_path = os.path.join(os.path.dirname(__file__), "species_data")
return send_from_directory(species_data_path, filename)


# New universal renderer endpoint (replaces the old Python renderer)
@app.route("/generate-image-new")
@cache_with_stats(timeout=31536000, query_string=True)
def generate_image():
# Get characters
characters = request.args.get("characters", "")
characters_list = extract_characters(characters)

# Get settings
measure_ears = request.args.get("measure_ears", True) == "True"
scale_height = request.args.get("scale_height", True) == "True"

# Get height
size = int(request.args.get("size", "400"))

# Record we've generated a new image!
stats_manager.increment_images_generated()

def generate_and_save():
if len(characters_list) == 0:
logging.warn("Asked to generate an empty image!")
# Generate an empty image
image = Image.new("RGB", (int(size * 1.4), size))
pixels = image.load()
for i in range(image.size[0]):
for j in range(image.size[1]):
pixels[i, j] = (
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255),
)
else:
# Use the JavaScript renderer with fallback to Python PIL
from app.utils.js_renderer import render_with_js_fallback

# Convert characters to dictionaries for JS renderer
char_dicts = []
for char in characters_list:
char_dict = {
"name": char.name,
"species": char.species,
"height": char.height,
"gender": char.gender,
"feral_height": char.feral_height,
"image": char.image,
"ears_offset": char.ears_offset,
}
if hasattr(char, "color") and char.color:
char_dict["color"] = char.color
char_dicts.append(char_dict)

options = {
"size": size,
"measureToEars": measure_ears,
"useSpeciesScaling": scale_height,
}

# This will try JS renderer first, fall back to Python PIL automatically
image = render_with_js_fallback(char_dicts, options)

# Save image to a BytesIO object
img_io = io.BytesIO()
image.save(img_io, "PNG")
img_io.seek(0)
return img_io

# Submit the task to the executor
future = executor.submit(generate_and_save)

try:
img_io = future.result(timeout=30) # Wait for up to 30 seconds
except TimeoutError:
return "Image generation timed out", 504

# Create a response with the image and set Content-Type to image/png
response = make_response(img_io.read())
response.headers.set("Content-Type", "image/png")
response.headers.set("Content-Disposition", "inline", filename="preview.png")
response.headers.set("Cache-Control", "public, max-age=31536000")

return response


# For WSGI
def create_app():
return app
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "missing.png"
image: "Chrissy/missing.png"
ears_offset: 0
data:
- anthro_size: 76 # 6'4"
Expand All @@ -8,7 +8,7 @@ male:
height: 50

female:
image: "missing.png"
image: "Chrissy/missing.png"
ears_offset: 0
data:
- anthro_size: 74 # 6'2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "randal.png"
image: "Chrissy/randal.png"
ears_offset: 4
data:
- anthro_size: 76 # 6'4"
Expand All @@ -8,7 +8,7 @@ male:
height: 15 # 15"

female:
image: "vixi.png"
image: "Chrissy/vixi.png"
ears_offset: 4
data:
- anthro_size: 74 # 6'2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "Hieght_Ref_Maxene_for_Vixi.png"
image: "Hunner/Hieght_Ref_Maxene_for_Vixi.png"
ears_offset: 3
color: "870716"
data:
Expand All @@ -9,7 +9,7 @@ male:
height: 24

female:
image: "Hieght_Ref_Ky-Li_for_Vixi.png"
image: "Hunner/Hieght_Ref_Ky-Li_for_Vixi.png"
ears_offset: 9
color: "add6ed"
data:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "f_equine.png"
image: "Placeholder/f_equine.png"
ears_offset: 0
data:
- anthro_size: 76 # 6'4"
Expand All @@ -8,7 +8,7 @@ male:
height: 56

female:
image: "f_equine.png"
image: "Placeholder/f_equine.png"
ears_offset: 0
data:
- anthro_size: 76 # 6'4"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "Felid_Sketch_Male.png"
image: "Rhainbowmetall/Felid_Sketch_Male.png"
ears_offset: 9
color: "282b1d"
data:
Expand All @@ -9,7 +9,7 @@ male:
height: 10

female:
image: "Felid_Sketch_Female.png"
image: "Rhainbowmetall/Felid_Sketch_Female.png"
ears_offset: 9
color: "1d751d"
data:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "m_fennec.png"
image: "Chrissy/m_fennec.png"
ears_offset: 10
data:
- anthro_size: 76 # 6'4"
Expand All @@ -8,7 +8,7 @@ male:
height: 7

female:
image: "missing.png"
image: "Chrissy/missing.png"
ears_offset: 0
data:
- anthro_size: 74 # 6'2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
male:
image: "m_giraffe.png"
image: "Placeholder/m_giraffe.png"
ears_offset: 0
data:
- anthro_size: 76 # 6'4"
Expand All @@ -8,7 +8,7 @@ male:
height: 190

female:
image: "m_giraffe.png"
image: "Placeholder/m_giraffe.png"
ears_offset: 0
data:
- anthro_size: 76 # 6'4"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
male:
image: "m_mouse_hunner.png"
image: "Hunner/Size_Refs_Mouse_for_Vixi (male).png"
ears_offset: 6
color: "8B8000"
color: "007516"
data:
- anthro_size: 76 # 6'4"
height: 3
- anthro_size: 60 # 5'
height: 4.5

female:
image: "f_mouse_hunner.png"
image: "Hunner/Size_Refs_Mouse_for_Vixi (female).png"
ears_offset: 7
color: "049904"
color: "7900ad"
data:
- anthro_size: 74 # 6'2"
height: 2.5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@ presets:
height: 66
description: All things foxy vixen! With 100% bonus wings!
- name: Tirga
species: feline
species: saber-toothed_tiger
gender: androgynous
height: 72
height: 84
description: She's got more limbs than she knows what to do with.
- name: Alice
species: canine
gender: female
height: 141
description: Big ol' dog~
- name: Maxene
species: canine
gender: male
height: 120
description: Cute demon doggo!
Loading
Loading