|
import math |
|
import gradio as gr |
|
from transformers import MarianMTModel, MarianTokenizer |
|
import torch |
|
|
|
|
|
|
|
|
|
model_name = "Helsinki-NLP/opus-mt-th-en" |
|
tokenizer = MarianTokenizer.from_pretrained(model_name) |
|
model = MarianMTModel.from_pretrained(model_name) |
|
|
|
def translate_th_to_en(text_th: str) -> str: |
|
text_th = text_th.strip() |
|
if not text_th: |
|
return "" |
|
inputs = tokenizer(text_th, return_tensors="pt", max_length=512, truncation=True) |
|
translation_tokens = model.generate(**inputs, max_length=512) |
|
en_text = tokenizer.decode(translation_tokens[0], skip_special_tokens=True) |
|
return en_text |
|
|
|
|
|
|
|
|
|
|
|
def approximate_temp_with_star(star_type, distance_au, albedo=0.3): |
|
""" |
|
คำนวณอุณหภูมิพื้นผิว (°C) อย่างง่าย โดยอิง Black Body + star luminosity |
|
- star_type: |
|
"Red Dwarf" -> luminosity ~0.02 เท่าดวงอาทิตย์ |
|
"Sun-like" -> luminosity = 1 เท่าดวงอาทิตย์ |
|
"Blue Giant"-> luminosity ~10 เท่าดวงอาทิตย์ (สมมุติ) |
|
- distance_au: ระยะห่างหน่วย AU |
|
- albedo: สัดส่วนสะท้อนแสง (สมมุติ 0.3) |
|
- สุดท้ายบวก greenhouse effect ~ +15 °C (สมมุติ) |
|
""" |
|
if star_type == "Red Dwarf": |
|
lum_ratio = 0.02 |
|
elif star_type == "Blue Giant": |
|
lum_ratio = 10.0 |
|
else: |
|
lum_ratio = 1.0 |
|
|
|
|
|
luminosity = 3.828e26 * lum_ratio |
|
|
|
|
|
dist_m = distance_au * 1.496e11 |
|
|
|
sigma = 5.67e-8 |
|
|
|
T_k = ((1 - albedo) * luminosity / (16 * math.pi * sigma * dist_m**2))**0.25 |
|
T_c = T_k - 273.15 |
|
|
|
|
|
T_c += 15 |
|
|
|
return round(T_c) |
|
|
|
def approximate_gravity(diameter_factor): |
|
""" |
|
สมมุติว่า แรงโน้มถ่วง ~ diameter_factor เท่าของโลก |
|
(จริงๆ เกี่ยวกับความหนาแน่นด้วย แต่เอาแบบง่าย) |
|
""" |
|
return round(diameter_factor, 2) |
|
|
|
|
|
|
|
|
|
|
|
|
|
def describe_distance(distance_au): |
|
""" |
|
ตีความระยะห่าง (AU) เป็นคำบรรยาย |
|
""" |
|
if distance_au < 0.5: |
|
return "โคจรอยู่ใกล้มากจนดาวเคราะห์ร้อนระอุ" |
|
elif distance_au < 1.2: |
|
return "โคจรค่อนข้างใกล้เหมือนโลก หรืออาจอุ่นกว่านิดหน่อย" |
|
elif distance_au < 2.5: |
|
return "โคจรห่างพอประมาณ ค่อนข้างเย็น" |
|
else: |
|
return "โคจรไกลมาก มีสภาพหนาวเย็นสุดขั้ว" |
|
|
|
def describe_temp(temp_c): |
|
""" |
|
ตีความอุณหภูมิ (°C) เป็นคำบรรยาย |
|
""" |
|
if temp_c < -30: |
|
return "อากาศหนาวแข็ง" |
|
elif temp_c < 10: |
|
return "อากาศค่อนข้างเย็น" |
|
elif temp_c < 35: |
|
return "อากาศกำลังสบาย" |
|
else: |
|
return "ร้อนระอุ" |
|
|
|
def describe_gravity(g): |
|
""" |
|
ตีความแรงโน้มถ่วง (g) เป็นคำบรรยาย |
|
""" |
|
if g < 0.5: |
|
return "โน้มถ่วงเบามาก (ตัวลอยได้ง่าย)" |
|
elif g < 1.2: |
|
return "คล้ายโลก" |
|
elif g < 2.0: |
|
return "ค่อนข้างหนัก" |
|
else: |
|
return "หนักมากจนอาจกดทับทุกสิ่ง" |
|
|
|
def describe_tilt(tilt_deg): |
|
""" |
|
ตีความแกนเอียง |
|
""" |
|
tilt = float(tilt_deg) |
|
if tilt < 5: |
|
return "แทบไม่เอียง ฤดูกาลเรียบง่าย" |
|
elif tilt < 25: |
|
return "เอียงเล็กน้อย มีฤดูกาลพอประมาณ" |
|
elif tilt < 45: |
|
return "เอียงปานกลาง ฤดูกาลค่อนข้างเปลี่ยนแปลง" |
|
else: |
|
return "เอียงมาก ฤดูกาลสุดขั้ว" |
|
|
|
def describe_moons(n): |
|
""" |
|
ตีความจำนวนดวงจันทร์ |
|
""" |
|
n = int(n) |
|
if n == 0: |
|
return "ไม่มีดวงจันทร์" |
|
elif n == 1: |
|
return "มีดวงจันทร์หนึ่งดวง" |
|
else: |
|
return f"มีดวงจันทร์ {n} ดวง" |
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_prompts_en(planet_name_en, star_type_en, distance_desc_en, |
|
temp_desc_en, gravity_desc_en, tilt_desc_en, |
|
moon_desc_en, oxygen_desc_en, life_en): |
|
""" |
|
สร้าง 3 prompts ภาษาอังกฤษ โดยไม่ใส่ตัวเลขตรงๆ |
|
แต่ใช้คำบรรยายเชิงเปรียบเทียบ |
|
""" |
|
|
|
|
|
prompt1 = ( |
|
f"A stunning space illustration of planet '{planet_name_en}' orbiting a {star_type_en} star. " |
|
f"It is {distance_desc_en}, with {temp_desc_en} climate and {gravity_desc_en} surface gravity. " |
|
f"{tilt_desc_en}, and {moon_desc_en}. Atmosphere containing {oxygen_desc_en}, truly fascinating." |
|
) |
|
|
|
|
|
prompt2 = ( |
|
f"Alien life on planet '{planet_name_en}': {life_en}, " |
|
f"thriving under {temp_desc_en} conditions, {gravity_desc_en} gravity, and {oxygen_desc_en} in the air. " |
|
f"Highly imaginative, concept art style, fantasy world." |
|
) |
|
|
|
|
|
prompt3 = ( |
|
f"The planet '{planet_name_en}' has a surface with {temp_desc_en} weather, {gravity_desc_en}, " |
|
f"{tilt_desc_en}, and {moon_desc_en}. An epic terrain, atmospheric glow, and {oxygen_desc_en} presence. " |
|
f"Realistic space painting, cinematic lighting." |
|
) |
|
|
|
prompt_all = ( |
|
"--- Prompt #1 ---\n" + prompt1 + "\n\n" |
|
"--- Prompt #2 ---\n" + prompt2 + "\n\n" |
|
"--- Prompt #3 ---\n" + prompt3 + "\n" |
|
) |
|
return prompt_all |
|
|
|
|
|
|
|
|
|
|
|
def generate_planet_info( |
|
planet_name_th, |
|
star_type_en_select, |
|
distance_str, |
|
diameter_str, |
|
tilt_str, |
|
moon_str, |
|
oxygen_percent, |
|
planet_type_th, |
|
life_th |
|
): |
|
|
|
try: |
|
distance_au = float(distance_str) |
|
except: |
|
distance_au = 1.0 |
|
|
|
try: |
|
diameter_factor = float(diameter_str) |
|
except: |
|
diameter_factor = 1.0 |
|
|
|
try: |
|
tilt_deg = float(tilt_str) |
|
except: |
|
tilt_deg = 23.5 |
|
|
|
try: |
|
num_moon = int(moon_str) |
|
except: |
|
num_moon = 1 |
|
|
|
|
|
temp_c = approximate_temp_with_star(star_type_en_select, distance_au) |
|
|
|
g_approx = approximate_gravity(diameter_factor) |
|
|
|
|
|
|
|
|
|
|
|
|
|
child_summary = ( |
|
f"ดาว {planet_name_th} เป็น{planet_type_th} " |
|
f"โคจรรอบดาวฤกษ์ประเภท {star_type_en_select} (อิงวิทยาศาสตร์สมมุติ)\n" |
|
f"อุณหภูมิประมาณ {temp_c}°C, " |
|
f"แรงโน้มถ่วง ~{g_approx}g, ออกซิเจน ~{oxygen_percent}%, " |
|
f"แกนเอียง {tilt_deg}°, {num_moon} ดวงจันทร์,\n" |
|
f"มีสิ่งมีชีวิต: {life_th}\n" |
|
f"น่าอัศจรรย์จริง ๆ!" |
|
) |
|
|
|
|
|
detail_th = ( |
|
f"ชื่อดาวเคราะห์ (ไทย): {planet_name_th}\n" |
|
f"ประเภทดาวฤกษ์: {star_type_en_select}\n" |
|
f"ระยะห่าง: ~{distance_au} AU\n" |
|
f"ขนาดเส้นผ่านศูนย์กลาง: ~{diameter_factor} เท่าโลก\n" |
|
f"แกนเอียง: ~{tilt_deg}°\n" |
|
f"จำนวนดวงจันทร์: {num_moon}\n" |
|
f"อุณหภูมิคำนวณ: ~{temp_c} °C\n" |
|
f"แรงโน้มถ่วง: ~{g_approx} g\n" |
|
f"ออกซิเจน: {oxygen_percent}%\n" |
|
f"ชนิดดาวเคราะห์ (ไทย): {planet_type_th}\n" |
|
f"สิ่งมีชีวิต (ไทย): {life_th}\n" |
|
) |
|
|
|
|
|
planet_name_en = translate_th_to_en(planet_name_th) |
|
|
|
life_en = translate_th_to_en(life_th) |
|
|
|
|
|
|
|
type_map = { |
|
"ดาวหิน": "rocky planet", |
|
"ดาวก๊าซ": "gas giant", |
|
"ดาวน้ำแข็ง": "icy planet" |
|
} |
|
|
|
planet_type_key = planet_type_th.replace("(", "").replace(")", "").replace(" ", "").split("ดาว")[-1] |
|
planet_type_key = planet_type_key.strip() |
|
planet_type_en = type_map.get(planet_type_key, "unknown planet") |
|
|
|
|
|
|
|
distance_desc_th = describe_distance(distance_au) |
|
distance_desc_en = translate_th_to_en(distance_desc_th) |
|
|
|
temp_desc_th = describe_temp(temp_c) |
|
temp_desc_en = translate_th_to_en(temp_desc_th) |
|
|
|
gravity_desc_th = describe_gravity(g_approx) |
|
gravity_desc_en = translate_th_to_en(gravity_desc_th) |
|
|
|
tilt_desc_th = describe_tilt(tilt_deg) |
|
tilt_desc_en = translate_th_to_en(tilt_desc_th) |
|
|
|
moon_desc_th = describe_moons(num_moon) |
|
moon_desc_en = translate_th_to_en(moon_desc_th) |
|
|
|
|
|
|
|
|
|
if oxygen_percent < 1: |
|
oxygen_desc_th = "ไม่มีออกซิเจน" |
|
elif oxygen_percent < 10: |
|
oxygen_desc_th = "ออกซิเจนจางมาก" |
|
elif oxygen_percent < 25: |
|
oxygen_desc_th = "ออกซิเจนพอเหมาะ" |
|
else: |
|
oxygen_desc_th = "ออกซิเจนสูง" |
|
oxygen_desc_en = translate_th_to_en(oxygen_desc_th) |
|
|
|
|
|
prompt_all = build_prompts_en( |
|
planet_name_en, |
|
f"{star_type_en_select} star", |
|
distance_desc_en, |
|
temp_desc_en, |
|
gravity_desc_en, |
|
tilt_desc_en, |
|
moon_desc_en, |
|
oxygen_desc_en, |
|
life_en |
|
) |
|
|
|
return child_summary, detail_th, prompt_all |
|
|
|
|
|
|
|
|
|
|
|
|
|
css_code = """ |
|
body { |
|
background-color: #F9FBFF; |
|
font-family: "Kanit", sans-serif; |
|
} |
|
#title { |
|
color: #4A90E2; |
|
text-align: center; |
|
font-size: 2rem; |
|
margin-top: 20px; |
|
margin-bottom: 10px; |
|
font-weight: bold; |
|
} |
|
.game-desc { |
|
margin: 0 auto; |
|
width: 90%; |
|
background-color: #ECF6FF; |
|
border: 2px dashed #B3DAFF; |
|
border-radius: 10px; |
|
padding: 15px; |
|
color: #333; |
|
margin-bottom: 20px; |
|
} |
|
.btn-main { |
|
background-color: #FFE066; |
|
border: 2px solid #FFCA28; |
|
font-weight: bold; |
|
font-size: 1.1rem; |
|
padding: 10px 30px; |
|
border-radius: 10px; |
|
margin-right: 10px; |
|
} |
|
#child-summary, #detail-th, #prompt-en { |
|
background-color: #FFFDF5; |
|
border: 2px solid #FFE082; |
|
border-radius: 10px; |
|
padding: 10px; |
|
margin-bottom: 20px; |
|
} |
|
""" |
|
|
|
def welcome_text(): |
|
return "ยินดีต้อนรับสู่ Planetary Adventure+! ลองกรอกข้อมูลแล้วกด 'สร้างโลกแฟนตาซี' กันนะจ๊ะ" |
|
|
|
copy_button_html = """ |
|
<button style="background-color: #F06292; border: 2px solid #E91E63; font-weight: bold; |
|
font-size: 1.0rem; padding: 8px 20px; border-radius: 10px;" |
|
onclick="copyPromptText()"> |
|
คัดลอก Prompt |
|
</button> |
|
<script> |
|
function copyPromptText() { |
|
const promptBox = document.querySelector('#prompt-en textarea'); |
|
if (!promptBox) { |
|
alert('ไม่พบข้อความ Prompt!'); |
|
return; |
|
} |
|
const promptText = promptBox.value; |
|
navigator.clipboard.writeText(promptText); |
|
alert('คัดลอก Prompt แล้ว! นำไปใช้ใน AI สร้างภาพได้เลย~'); |
|
} |
|
</script> |
|
""" |
|
|
|
import re |
|
|
|
with gr.Blocks(css=css_code) as demo: |
|
gr.Markdown("<h1 id='title'>Planetary Adventure (Advanced) - Thai Input + Real Sci + No Direct Numbers</h1>") |
|
gr.Markdown(""" |
|
<div class="game-desc"> |
|
<p>สร้างดาวเคราะห์พร้อมหลักวิทยาศาสตร์เบื้องต้น (สูตร Stefan-Boltzmann) และใช้การแปล MarianMT</p> |
|
<ol> |
|
<li>กรอกชื่อดาวเคราะห์ (ภาษาไทย)</li> |
|
<li>เลือกชนิดดาวฤกษ์ (Red Dwarf / Sun-like / Blue Giant)</li> |
|
<li>กรอกระยะห่าง (หน่วย AU), ขนาดเท่าโลก, แกนเอียง, จำนวนดวงจันทร์, % ออกซิเจน</li> |
|
<li>ระบุประเภทดาวเคราะห์ (ไทย) เช่น "ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง"</li> |
|
<li>พิมพ์สิ่งมีชีวิต (ไทย)</li> |
|
<li>กดปุ่ม <strong>"สร้างโลกแฟนตาซี"</strong></li> |
|
</ol> |
|
<p>ระบบจะ <em>คำนวณอุณหภูมิ</em>, <em>แรงโน้มถ่วง</em>, <em>ตีความเป็นคำบรรยาย</em>, และแปลไทย->อังกฤษ<br> |
|
จากนั้นสร้าง <strong>3 prompts</strong> ที่เลี่ยงตัวเลขตรงๆ ใช้คำเปรียบเทียบแทน</p> |
|
</div> |
|
""") |
|
|
|
planet_name_th = gr.Textbox(label="ชื่อดาวเคราะห์ (ภาษาไทย)", placeholder="ตัวอย่าง: ดาวซานาดา") |
|
|
|
star_type_select = gr.Dropdown( |
|
label="ประเภทดาวฤกษ์ (English)", |
|
choices=["Red Dwarf", "Sun-like", "Blue Giant"], |
|
value="Sun-like" |
|
) |
|
|
|
distance_au = gr.Textbox(label="ระยะห่างจากดาวฤกษ์ (AU)", placeholder="เช่น 1, 0.5, 2") |
|
diameter_factor = gr.Textbox(label="ขนาด (เท่าโลก)", placeholder="เช่น 1, 2, 0.5") |
|
tilt_deg = gr.Textbox(label="แกนเอียง (องศา)", placeholder="0-90") |
|
moon_number = gr.Textbox(label="จำนวนดวงจันทร์", placeholder="0, 1, 2, ...") |
|
|
|
oxygen_slider = gr.Slider( |
|
minimum=0, maximum=100, step=1, value=21, |
|
label="% ออกซิเจน (ในบรรยากาศ)" |
|
) |
|
|
|
planet_type_th = gr.Dropdown( |
|
label="ชนิดดาวเคราะห์ (ไทย)", |
|
choices=["ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง"], |
|
value="ดาวหิน" |
|
) |
|
|
|
life_th = gr.Textbox(label="สิ่งมีชีวิต (ภาษาไทย)", placeholder="ตัวอย่าง: แมลงยักษ์เรืองแสง") |
|
|
|
create_btn = gr.Button("สร้างโลกแฟนตาซี", elem_classes="btn-main") |
|
|
|
child_summary_out = gr.Textbox(label="สรุปสำหรับเด็ก (ไทย)", interactive=False, elem_id="child-summary") |
|
detail_th_out = gr.Textbox(label="รายละเอียด (ไทย)", interactive=False, elem_id="detail-th") |
|
prompt_en_out = gr.Textbox(label="3 Prompts (English)", interactive=False, elem_id="prompt-en") |
|
|
|
gr.HTML(copy_button_html) |
|
|
|
create_btn.click( |
|
fn=generate_planet_info, |
|
inputs=[ |
|
planet_name_th, |
|
star_type_select, |
|
distance_au, |
|
diameter_factor, |
|
tilt_deg, |
|
moon_number, |
|
oxygen_slider, |
|
planet_type_th, |
|
life_th |
|
], |
|
outputs=[ |
|
child_summary_out, |
|
detail_th_out, |
|
prompt_en_out |
|
] |
|
) |
|
|
|
demo.load(fn=welcome_text, inputs=None, outputs=child_summary_out) |
|
|
|
demo.launch() |
|
|