File size: 4,714 Bytes
77e37fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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