3DModelGen / parser.py
tomiconic's picture
Upload 11 files
77e37fc verified
from __future__ import annotations
import re
from dataclasses import dataclass, asdict
@dataclass
class PromptSpec:
object_type: str = "cargo_hauler"
scale: str = "small"
hull_style: str = "boxy"
engine_count: int = 2
wing_span: float = 0.2
cargo_ratio: float = 0.38
cockpit_ratio: float = 0.18
fin_height: float = 0.0
landing_gear: bool = True
asymmetry: float = 0.0
notes: str = ""
def to_dict(self) -> dict:
return asdict(self)
TYPE_KEYWORDS = {
"fighter": "fighter",
"combat": "fighter",
"interceptor": "fighter",
"shuttle": "shuttle",
"freighter": "freighter",
"hauler": "cargo_hauler",
"cargo": "cargo_hauler",
"transport": "cargo_hauler",
"dropship": "dropship",
"drone": "drone",
}
STYLE_KEYWORDS = {
"boxy": "boxy",
"industrial": "boxy",
"hard-surface": "boxy",
"rounded": "rounded",
"sleek": "sleek",
"streamlined": "sleek",
"brutalist": "boxy",
}
SCALE_KEYWORDS = {
"tiny": "small",
"small": "small",
"compact": "small",
"medium": "medium",
"mid-size": "medium",
"large": "large",
"heavy": "large",
"huge": "large",
}
VALID_OBJECT_TYPES = {"cargo_hauler", "fighter", "shuttle", "freighter", "dropship", "drone"}
VALID_SCALES = {"small", "medium", "large"}
VALID_HULL_STYLES = {"boxy", "rounded", "sleek"}
def _clamp(value: float, low: float, high: float) -> float:
return max(low, min(high, value))
def merge_prompt_specs(primary: PromptSpec, secondary: PromptSpec) -> PromptSpec:
merged = PromptSpec(**primary.to_dict())
if secondary.object_type in VALID_OBJECT_TYPES:
merged.object_type = secondary.object_type
if secondary.scale in VALID_SCALES:
merged.scale = secondary.scale
if secondary.hull_style in VALID_HULL_STYLES:
merged.hull_style = secondary.hull_style
merged.engine_count = int(_clamp(secondary.engine_count, 1, 6))
merged.wing_span = float(_clamp(secondary.wing_span, 0.0, 0.6))
merged.cargo_ratio = float(_clamp(secondary.cargo_ratio, 0.0, 0.65))
merged.cockpit_ratio = float(_clamp(secondary.cockpit_ratio, 0.10, 0.30))
merged.fin_height = float(_clamp(secondary.fin_height, 0.0, 0.3))
merged.landing_gear = bool(secondary.landing_gear)
merged.asymmetry = float(_clamp(secondary.asymmetry, 0.0, 0.2))
merged.notes = secondary.notes or primary.notes
if merged.object_type in {"fighter", "drone"}:
merged.cargo_ratio = min(merged.cargo_ratio, 0.20)
if merged.hull_style == "boxy":
merged.hull_style = "sleek"
return merged
def parse_prompt(prompt: str) -> PromptSpec:
text = prompt.lower().strip()
spec = PromptSpec(notes=prompt.strip())
for key, value in TYPE_KEYWORDS.items():
if key in text:
spec.object_type = value
break
for key, value in STYLE_KEYWORDS.items():
if key in text:
spec.hull_style = value
break
for key, value in SCALE_KEYWORDS.items():
if key in text:
spec.scale = value
break
if any(word in text for word in ["wing", "wings"]):
spec.wing_span = 0.42 if spec.object_type == "fighter" else 0.28
if any(word in text for word in ["no wings", "wingless"]):
spec.wing_span = 0.0
if any(word in text for word in ["cargo bay", "cargo hold", "container", "freight"]):
spec.cargo_ratio = 0.48
if any(word in text for word in ["big cockpit", "large cockpit", "glass nose"]):
spec.cockpit_ratio = 0.24
if any(word in text for word in ["small cockpit", "tiny cockpit"]):
spec.cockpit_ratio = 0.13
if any(word in text for word in ["fin", "tail", "vertical stabilizer"]):
spec.fin_height = 0.18 if spec.object_type != "fighter" else 0.12
if any(word in text for word in ["hover", "hovercraft", "antigrav"]):
spec.landing_gear = False
if spec.object_type in {"fighter", "drone"}:
spec.engine_count = 1 if "single engine" in text else 2
spec.cargo_ratio = min(spec.cargo_ratio, 0.18)
spec.hull_style = "sleek"
elif spec.object_type in {"cargo_hauler", "freighter", "dropship"}:
spec.engine_count = 4 if any(x in text for x in ["4 engine", "four engine", "quad engine"]) else 2
spec.hull_style = "boxy" if spec.hull_style == "sleek" else spec.hull_style
numeric_engine = re.search(r"(\d+)\s*(?:engine|engines)", text)
if numeric_engine:
spec.engine_count = max(1, min(6, int(numeric_engine.group(1))))
if any(word in text for word in ["asymmetric", "uneven", "offset"]):
spec.asymmetry = 0.12
return spec