diff --git a/.gitignore b/.gitignore index 269d87a..3ab4911 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ cache .env *.sqlite3 art/dist/ +__pycache__ diff --git a/app/__init__.py b/app/__init__.py index e2d203a..ad8b838 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -22,7 +22,11 @@ from functools import wraps from app.utils.species_lookup import load_species_data -from app.utils.calculate_heights import calculate_height_offset, convert_to_inches +from app.utils.calculate_heights import ( + calculate_height_offset, + convert_to_inches, + inches_to_feet_inches, +) from app.utils.parse_data import ( extract_characters, filter_valid_characters, @@ -34,6 +38,7 @@ from app.utils.stats import StatsManager from app.utils.generate_image import render_image from app.utils.character import Character +from app.utils.taur_calculator import calculate_taur app = Flask(__name__) app.secret_key = os.urandom(24) @@ -300,13 +305,133 @@ def add_preset(): return redirect(f"/?characters={characters_query}{settings_query}") -@app.route("/taur") +@app.route("/taur", methods=["GET", "POST"]) def taur(): """ - Base route for volnar's sub-page! + Taur calculator! + + Collaboration with Volnar <3 """ + # Filter out some ones we dont want to show/dont have data + filtered_species = [ + s + for s in species_list + if s not in ["taur_(generic)", "preset_species", "rexouium"] + ] + + # Load species data for auto-population + species_data_map = {} + for species_name in filtered_species: + try: + data = load_species_data(species_name) + # Extract species_length, species_tail_length, species_weight from male section + # and get a default species_height from the first data point + if "male" in data: + male_data = data["male"] + species_data_map[species_name] = { + "species_length": male_data.get("species_length", 0), + "species_tail_length": male_data.get("species_tail_length", 0), + "species_weight": male_data.get("species_weight", 0), + "species_height": ( + male_data.get("data", [{}])[0].get("height", 0) + if male_data.get("data") + else 0 + ), + } + except Exception as e: + logging.warning(f"Failed to load species data for {species_name}: {e}") + species_data_map[species_name] = { + "species_length": 0, + "species_tail_length": 0, + "species_weight": 0, + "species_height": 0, + } + + if request.method == "POST": + # POST when handling a form submission - redirect with URL parameters + params = {} + for key in [ + "name", + "measurement_type", + "anthro_height", + "species_height", + "species_length", + "species_tail_length", + "taur_full_height", + "species_weight", + "taur_length", + ]: + value = request.form.get(key, "") + if value: + params[key] = value + + query_string = "&".join([f"{k}={v}" for k, v in params.items()]) + return redirect(f"/taur?{query_string}") + + # Handle GET with calculation parameters + calculation_result = None + cleaned_calculation_result = {} + if request.args.get("anthro_height"): + try: + anthro_height = float(request.args.get("anthro_height", 0)) + species_height = float(request.args.get("species_height", 0)) + species_length = float(request.args.get("species_length", 0)) + species_tail_length = float(request.args.get("species_tail_length", 0)) + taur_full_height = float(request.args.get("taur_full_height", 0)) + species_weight = float(request.args.get("species_weight", 0)) + taur_length = request.args.get("taur_length") + measurement_type = request.args.get("measurement_type", "vitruvian") + + taur_length_float = float(taur_length) if taur_length else None + + calculation_result = calculate_taur( + anthro_height=anthro_height, + species_height=species_height, + species_length=species_length, + species_tail_length=species_tail_length, + taur_full_height=taur_full_height, + species_weight=species_weight, + taur_length=taur_length_float, + measurement_type=measurement_type, + ) - return render_template("taur.html") + cleaned_calculation_result["AR"] = ( + f"{inches_to_feet_inches(calculation_result['AR'])} (Anthropic Ratio)" + ) + cleaned_calculation_result["TH"] = ( + f"{inches_to_feet_inches(calculation_result['TH'])} (Taur Height)" + ) + cleaned_calculation_result["TFH"] = ( + f"{inches_to_feet_inches(calculation_result['TFH'])} (Taur Full Height)" + ) + cleaned_calculation_result["TL"] = ( + f"{inches_to_feet_inches(calculation_result['TL'])} (Taur Length)" + ) + cleaned_calculation_result["TT"] = ( + f"{inches_to_feet_inches(calculation_result['TT'])} (Taur Tail Length)" + ) + cleaned_calculation_result["TTo"] = ( + f"{inches_to_feet_inches(calculation_result['TTo'])} (Taur Torso Length)" + ) + cleaned_calculation_result["THe"] = ( + f"{inches_to_feet_inches(calculation_result['THe'])} (Taur Head Length)" + ) + cleaned_calculation_result["TW"] = ( + f"{calculation_result['TW']:.2f} lbs (Taur Weight)" + ) + + except (ValueError, TypeError) as e: + logging.warning(f"Taur calculation error: {e}") + calculation_result = None + cleaned_calculation_result = None + + return render_template( + "taur.html", + species=filtered_species, + species_data=species_data_map, + calculation_result=cleaned_calculation_result, + form_data=dict(request.args) if request.args else None, + ) # For WSGI diff --git a/app/species_data/african_buffalo.yaml b/app/species_data/african_buffalo.yaml index e9dccc2..f5d44fe 100644 --- a/app/species_data/african_buffalo.yaml +++ b/app/species_data/african_buffalo.yaml @@ -1,6 +1,9 @@ male: image: "Chrissy/missing.png" ears_offset: 0 + species_length: 96 + species_tail_length: 30 + species_weight: 1500 # Pounds data: - anthro_size: 76 # 6'4" height: 60 @@ -10,6 +13,9 @@ male: female: image: "Chrissy/missing.png" ears_offset: 0 + species_length: 96 + species_tail_length: 30 + species_weight: 1500 # Pounds data: - anthro_size: 74 # 6'2" height: 54 diff --git a/app/species_data/arctic_fox.yaml b/app/species_data/arctic_fox.yaml index b2cdbc7..cc5e348 100644 --- a/app/species_data/arctic_fox.yaml +++ b/app/species_data/arctic_fox.yaml @@ -1,6 +1,9 @@ male: image: "Chrissy/randal.png" ears_offset: 4 + species_length: 28 + species_tail_length: 16 + species_weight: 12 # Pounds data: - anthro_size: 76 # 6'4" height: 22 # 22" @@ -10,6 +13,9 @@ male: female: image: "Chrissy/vixi.png" ears_offset: 4 + species_length: 28 + species_tail_length: 16 + species_weight: 12 # Pounds data: - anthro_size: 74 # 6'2" height: 20 # 22" diff --git a/app/species_data/canine.yaml b/app/species_data/canine.yaml index 0b8e618..130756a 100644 --- a/app/species_data/canine.yaml +++ b/app/species_data/canine.yaml @@ -2,6 +2,9 @@ male: image: "Hunner/Hieght_Ref_Maxene_for_Vixi.png" ears_offset: 3 color: "870716" + species_length: 36 + species_tail_length: 18 + species_weight: 80 # Pounds data: - anthro_size: 76 # 6'4" height: 28 @@ -12,6 +15,9 @@ female: image: "Hunner/Hieght_Ref_Ky-Li_for_Vixi.png" ears_offset: 9 color: "add6ed" + species_length: 36 + species_tail_length: 18 + species_weight: 80 # Pounds data: - anthro_size: 76 # 6'4" height: 26 diff --git a/app/species_data/equine.yaml b/app/species_data/equine.yaml index 6883e3a..19604c2 100644 --- a/app/species_data/equine.yaml +++ b/app/species_data/equine.yaml @@ -1,6 +1,9 @@ male: image: "Placeholder/f_equine.png" ears_offset: 0 + species_length: 72 + species_tail_length: 24 + species_weight: 1000 # Pounds data: - anthro_size: 76 # 6'4" height: 72 @@ -10,6 +13,9 @@ male: female: image: "Placeholder/f_equine.png" ears_offset: 0 + species_length: 72 + species_tail_length: 24 + species_weight: 1000 # Pounds data: - anthro_size: 76 # 6'4" height: 70 diff --git a/app/species_data/feline.yaml b/app/species_data/feline.yaml index 238de43..7c13215 100644 --- a/app/species_data/feline.yaml +++ b/app/species_data/feline.yaml @@ -2,6 +2,9 @@ male: image: "Rhainbowmetall/Felid_Sketch_Male.png" ears_offset: 9 color: "282b1d" + species_length: 30 + species_tail_length: 12 + species_weight: 15 # Pounds data: - anthro_size: 76 # 6'4" height: 14 @@ -12,6 +15,9 @@ female: image: "Rhainbowmetall/Felid_Sketch_Female.png" ears_offset: 9 color: "1d751d" + species_length: 30 + species_tail_length: 12 + species_weight: 15 # Pounds data: - anthro_size: 74 # 6'2" height: 11 diff --git a/app/species_data/fennec_fox.yaml b/app/species_data/fennec_fox.yaml index 0acb5e0..cac335a 100644 --- a/app/species_data/fennec_fox.yaml +++ b/app/species_data/fennec_fox.yaml @@ -1,6 +1,9 @@ male: image: "Chrissy/m_fennec.png" ears_offset: 10 + species_length: 20 + species_tail_length: 15 + species_weight: 3 # Pounds data: - anthro_size: 76 # 6'4" height: 11 @@ -10,6 +13,9 @@ male: female: image: "Chrissy/missing.png" ears_offset: 0 + species_length: 20 + species_tail_length: 14 + species_weight: 3 # Pounds data: - anthro_size: 74 # 6'2" height: 10 diff --git a/app/species_data/giraffe.yaml b/app/species_data/giraffe.yaml index ff95520..d5f59be 100644 --- a/app/species_data/giraffe.yaml +++ b/app/species_data/giraffe.yaml @@ -1,6 +1,9 @@ male: image: "Placeholder/m_giraffe.png" ears_offset: 0 + species_length: 120 + species_tail_length: 36 + species_weight: 3000 # Pounds data: - anthro_size: 76 # 6'4" height: 220 @@ -10,6 +13,9 @@ male: female: image: "Placeholder/m_giraffe.png" ears_offset: 0 + species_length: 120 + species_tail_length: 36 + species_weight: 2600 # Pounds data: - anthro_size: 76 # 6'4" height: 200 diff --git a/app/species_data/mouse.yaml b/app/species_data/mouse.yaml index bc8f116..1e1cca6 100644 --- a/app/species_data/mouse.yaml +++ b/app/species_data/mouse.yaml @@ -2,6 +2,9 @@ male: image: "Hunner/Size_Refs_Mouse_for_Vixi (male).png" ears_offset: 6 color: "007516" + species_length: 4 + species_tail_length: 3 + species_weight: 0.5 # Pounds data: - anthro_size: 76 # 6'4" height: 3 @@ -12,6 +15,9 @@ female: image: "Hunner/Size_Refs_Mouse_for_Vixi (female).png" ears_offset: 7 color: "7900ad" + species_length: 4 + species_tail_length: 3 + species_weight: 0.5 # Pounds data: - anthro_size: 74 # 6'2" height: 2.5 diff --git a/app/species_data/red_fox.yaml b/app/species_data/red_fox.yaml index afe5cec..db3fe8a 100644 --- a/app/species_data/red_fox.yaml +++ b/app/species_data/red_fox.yaml @@ -1,6 +1,9 @@ male: image: "Chrissy/randal.png" ears_offset: 4 + species_length: 28 + species_tail_length: 16 + species_weight: 12 # Pounds data: - anthro_size: 76 # 6'4" height: 17 @@ -10,6 +13,9 @@ male: female: image: "Chrissy/sinopa.png" ears_offset: 1.5 + species_length: 28 + species_tail_length: 16 + species_weight: 12 # Pounds data: - anthro_size: 76 # 6'4" height: 15 diff --git a/app/species_data/saber-toothed_tiger.yaml b/app/species_data/saber-toothed_tiger.yaml index 62dc89c..821a244 100644 --- a/app/species_data/saber-toothed_tiger.yaml +++ b/app/species_data/saber-toothed_tiger.yaml @@ -2,6 +2,9 @@ male: image: "Hunner/Hieght_Ref_NO_BAK_Tirga_for_Tirga.png" ears_offset: 3 color: "00730b" + species_length: 60 + species_tail_length: 30 + species_weight: 400 # Pounds data: - anthro_size: 76 # 6'4" height: 44 @@ -12,6 +15,9 @@ female: image: "Hunner/Hieght_Ref_NO_BAK_Tirga_for_Tirga.png" ears_offset: 3 color: "b216f0" + species_length: 60 + species_tail_length: 30 + species_weight: 400 # Pounds data: - anthro_size: 74 # 6'2" height: 46 diff --git a/app/species_data/shark.yaml b/app/species_data/shark.yaml index 0d2f8b7..bafa677 100644 --- a/app/species_data/shark.yaml +++ b/app/species_data/shark.yaml @@ -3,6 +3,9 @@ male: image: "Mori/m_shark.png" ears_offset: 3 # color: "311b9e" + species_length: 72 + species_tail_length: 36 + species_weight: 500 # Pounds data: - anthro_size: 76 # 6'4" height: 90 @@ -13,6 +16,9 @@ female: image: "Mori/f_shark.png" ears_offset: 3 # color: "664ce6" + species_length: 72 + species_tail_length: 36 + species_weight: 500 # Pounds data: - anthro_size: 74 # 6'2" height: 92 diff --git a/app/species_data/wolf.yaml b/app/species_data/wolf.yaml index 3dcb731..f9de2b2 100644 --- a/app/species_data/wolf.yaml +++ b/app/species_data/wolf.yaml @@ -1,6 +1,9 @@ male: image: "Placeholder/m_wolf.png" ears_offset: 0 + species_length: 40 + species_tail_length: 20 + species_weight: 100 # Pounds data: - anthro_size: 76 # 6'4" height: 36 @@ -10,6 +13,9 @@ male: female: image: "Hunner/Hieght_Ref_Ky-Li_for_Vixi.png" ears_offset: 10 + species_length: 40 + species_tail_length: 20 + species_weight: 100 # Pounds data: - anthro_size: 76 # 6'4" height: 34 diff --git a/app/static/css/taur_app.css b/app/static/css/taur_app.css new file mode 100644 index 0000000..73ea6be --- /dev/null +++ b/app/static/css/taur_app.css @@ -0,0 +1,150 @@ +.taur-container { + max-width: 1400px; + margin: 20px auto; + padding: 20px; + display: grid; + grid-template-columns: 1fr 400px; + gap: 30px; + min-height: calc(100vh - 40px); +} + +.taur-left-column { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + max-height: 100vh; +} + +.taur-right-column { + display: flex; + flex-direction: column; + gap: 20px; +} + +.taur-header { + text-align: center; +} + +.taur-header h1 { + color: #f80; + font-size: 2.5em; + margin-bottom: 10px; +} + +.taur-header p { + color: #fff; + font-size: 1.1em; +} + +.taur-canvas-container { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +#taur-canvas { + border: 2px solid #f80; + border-radius: 8px; + background: #000; + max-width: 50vw; + max-height: 100vh; + width: auto; + height: auto; +} + +.taur-form { + display: flex; + flex-direction: column; + gap: 15px; +} + +.taur-form-group { + display: flex; + flex-direction: column; +} + +.taur-form-group label { + color: #fff; + margin-bottom: 5px; + font-size: 1em; +} + +.info-icon { + cursor: help; + font-size: 0.9em; + opacity: 0.7; + margin-left: 4px; + display: inline-block; + vertical-align: middle; +} + +.info-icon:hover { + opacity: 1; +} + +.taur-form-group input, +.taur-form-group select { + padding: 10px; + font-size: 1em; + border: 1px solid #444; + border-radius: 5px; + background: #222; + color: #fff; + width: 100%; + box-sizing: border-box; +} + +.taur-form-group input:focus, +.taur-form-group select:focus { + outline: none; + border-color: #f80; +} + +.taur-form button { + padding: 10px 20px; + font-size: 1em; + background: #f80; + color: #000; + border: none; + border-radius: 5px; + cursor: pointer; + font-weight: bold; +} + +.taur-form button:hover { + background: #ffab40; +} + +.back-link-container { + text-align: center; +} + +.back-link { + color: #78d0ff; + text-decoration: none; + font-size: 1.2em; +} + +.back-link:hover { + color: #a0e0ff; +} + +@media (max-width: 968px) { + .taur-container { + grid-template-columns: 1fr; + } + + .taur-left-column { + order: -1; + height: auto; + min-height: 300px; + } + + #taur-canvas { + max-width: 100vw; + max-height: 50vh; + } +} diff --git a/app/static/images/taur/TaurCalculatorReference.png b/app/static/images/taur/TaurCalculatorReference.png new file mode 100644 index 0000000..1ef1e1f Binary files /dev/null and b/app/static/images/taur/TaurCalculatorReference.png differ diff --git a/app/templates/taur.html b/app/templates/taur.html index e0eb3ff..c8b9efa 100644 --- a/app/templates/taur.html +++ b/app/templates/taur.html @@ -4,23 +4,222 @@ - Taur Calculator (Coming Soon!) + Taur Calculator + -
-

Taur Calculator

-

Thanks to Volnar this feature is coming soon! - You'll be able to size your taur-self up!

-
- ← Back - to Main Calculator +
+
+
+ +
+
+
+
+

Taur Calculator

+

By Vixi and Volnar!

+
+ + + + + + {% if calculation_result %} +
+

Calculation Results

+
+ + + {% for key, value in calculation_result.items() %} + + + + + {% endfor %} + +
{{ key }} + + {% if value is string %} + {{ value.replace('\\u0027', "'") }} + {% elif value is mapping %} + { } + {% else %} + {{ value }} + {% endif %} +
+
+
+ {% endif %} + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + \ No newline at end of file diff --git a/app/utils/taur_calculator.py b/app/utils/taur_calculator.py new file mode 100644 index 0000000..a1a70a0 --- /dev/null +++ b/app/utils/taur_calculator.py @@ -0,0 +1,98 @@ +# Conceived by Volnar <3 +# Implemented by Vixi! + +from typing import Dict, Any, Tuple, Optional + + +def calculate_taur( + anthro_height: float, + species_height: float, + species_length: float, + species_tail_length: float, + taur_full_height: float, + species_weight: float, + taur_length: Optional[float] = None, + measurement_type: str = "limb", # "limb" for mammals, "vitruvian" for reptiles + custom_body_parts: Optional[Dict[str, float]] = None, +) -> Dict[str, Any]: + """ + Calculate taur body dimensions based on provided inputs + using Volnar's Taur Calculator formula <3 + + Args: + anthro_height (float): Anthro height before taurification (AH). + species_height (float): Species height (SH). + species_length (float): Species length w/o tail (SL). + species_tail_length (float): Species tail length (ST). + taur_full_height (float): Taur full height (TFH). + species_weight (float): Species weight (SW). + taur_length (float, optional): Taur length w/o tail (TL). + measurement_type (str, optional): 'limb' (default) or 'vitruvian'. + custom_body_parts (dict, optional): Additional custom body part measurements (CBP). + + Returns: + Dict[str, Any]: Calculated taur results including TH, TFH, TL, etc. + """ + + # Splitting Anthro Height into parts + anthro_legs = anthro_height * 4 / 8 + anthro_torso = anthro_height * 3 / 8 + anthro_head = anthro_height * 1 / 8 + # Here approx: arm_length = anthro_legs + + # TH: Taur Height + if measurement_type == "limb": + # Limb Type: TH = AL + taur_height = anthro_legs + if taur_length is None: + taur_length_calc = species_length + else: + taur_length_calc = taur_length + else: + # Vitruvian Type: TL = (2 * AT) - AHe + taur_length_calc = (2 * anthro_torso) - anthro_head + # In this type, TH meaning is not directly defined; fallback to species scaling + taur_height = species_height * (taur_full_height / anthro_height) + + # Ratios + # TR: Taur Ratio (size change between SH and TH) + taur_ratio = taur_height / species_height if species_height > 0 else 0 + # AR: Anthro Ratio (size change between AH and TFH) + anthro_ratio = taur_full_height / anthro_height if anthro_height > 0 else 0 + + # Apply AR to custom body parts + cbp_result = {} + if custom_body_parts: + for part, value in custom_body_parts.items(): + cbp_result[part] = value * anthro_ratio + + # Taur Torso and Head + taur_torso = anthro_torso * anthro_ratio + taur_head = anthro_head * anthro_ratio + + # Taur Tail + taur_tail = ( + species_tail_length * taur_ratio if species_tail_length is not None else 0 + ) + + # Taur Weight estimate: cube AR, apply to SW + taur_weight = species_weight * (anthro_ratio**3) + taur_weight_minus = taur_weight * 0.9 + taur_weight_plus = taur_weight * 1.1 + + # Compose results + result = { + "TH": taur_height, + "TFH": taur_full_height, + "TL": taur_length_calc, + "TT": taur_tail, + "TTo": taur_torso, + "THe": taur_head, + "TW": taur_weight, + "TW-": taur_weight_minus, + "TW+": taur_weight_plus, + "CBP": cbp_result, + "AR": anthro_ratio, + "TR": taur_ratio, + } + return result