check point
This commit is contained in:
1 parent
33c70e776a
commit
b0f9acdb47
37 files changed
+4469
-177
No files matched your search
@@ -161,3 +161,21 @@ auto_place_panel_bubbles :: proc(panel: Panel, panel_w, panel_h: f32) -> []Speec
|
||||
|
||||
return bubbles[:]
|
||||
}
|
||||
|
||||
update_bubble_text :: proc(bubble: Speech_Bubble, new_text: string) -> Speech_Bubble {
|
||||
updated := bubble
|
||||
updated.text = new_text
|
||||
return updated
|
||||
}
|
||||
|
||||
update_bubble_position :: proc(bubble: Speech_Bubble, x, y: f32) -> Speech_Bubble {
|
||||
updated := bubble
|
||||
updated.position = Position{x = x, y = y}
|
||||
return updated
|
||||
}
|
||||
|
||||
reset_bubble_position :: proc(bubble: Speech_Bubble) -> Speech_Bubble {
|
||||
updated := bubble
|
||||
updated.position = Position{x = 0, y = 0}
|
||||
return updated
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package core
|
||||
|
||||
import "core:strings"
|
||||
import "core:fmt"
|
||||
|
||||
CHARACTER_TEMPLATE_STRING :: "{age}-year-old {gender}, {hairColor} {hairStyle} hair, {eyeColor} eyes, {skinTone} skin, {bodyType} build, wearing {outfit}, {accessories}, {distinguishingFeatures}"
|
||||
|
||||
extract_age :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
// Try digit patterns first
|
||||
i: int = 0
|
||||
for i < len(lower)-3 {
|
||||
if lower[i] >= '0' && lower[i] <= '9' {
|
||||
start := i
|
||||
for i < len(lower) && lower[i] >= '0' && lower[i] <= '9' {
|
||||
i += 1
|
||||
}
|
||||
rest := lower[i:]
|
||||
if strings.has_prefix(rest, "-year-old") || strings.has_prefix(rest, "-year") || strings.has_prefix(rest, " years") || strings.has_prefix(rest, " year") {
|
||||
return strings.clone(lower[start:i])
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
// Try word patterns
|
||||
word_ages := []string{"young", "teen", "adult", "middle-aged", "middle aged", "elderly", "old"}
|
||||
for wa in word_ages {
|
||||
if strings.contains(lower, wa) {
|
||||
return strings.clone(wa)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_gender :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
male_terms := []string{"male", "man", "boy", "guy", "he", "him"}
|
||||
female_terms := []string{"female", "woman", "girl", "lady", "she", "her"}
|
||||
nb_terms := []string{"non-binary", "nonbinary", "nb", "enby"}
|
||||
|
||||
for t in nb_terms {
|
||||
if strings.contains(lower, t) { return strings.clone("non-binary") }
|
||||
}
|
||||
for t in female_terms {
|
||||
if strings.contains(lower, t) { return strings.clone("female") }
|
||||
}
|
||||
for t in male_terms {
|
||||
if strings.contains(lower, t) { return strings.clone("male") }
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_hair_color :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
colors := []string{"black", "brown", "blonde", "blond", "red", "auburn", "brunette", "silver", "gray", "grey", "white", "blue", "green", "purple", "pink", "orange", "golden", "platinum", "chestnut", "strawberry"}
|
||||
for c in colors {
|
||||
if strings.contains(lower, c) {
|
||||
return strings.clone(c)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_hair_style :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
styles := []string{"long", "short", "medium", "curly", "straight", "wavy", "spiky", "spikey", "ponytail", "bun", "braid", "buzz cut", "bald", "mohawk", "pixie", "bob", "afro", "dreadlocks", "dreads", "crew cut", "layered"}
|
||||
for s in styles {
|
||||
if strings.contains(lower, s) {
|
||||
return strings.clone(s)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_eye_color :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
colors := []string{"blue", "brown", "green", "hazel", "amber", "gray", "grey", "violet", "purple", "black", "red"}
|
||||
for c in colors {
|
||||
if strings.contains(lower, c) {
|
||||
return strings.clone(c)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_skin_tone :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
tones := []string{"fair", "pale", "light", "medium", "tan", "olive", "brown", "dark", "ebony", "porcelain", "bronze", "caramel", "warm", "cool"}
|
||||
for t in tones {
|
||||
if strings.contains(lower, t) {
|
||||
return strings.clone(t)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_body_type :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
types := []string{"slim", "skinny", "thin", "average", "athletic", "muscular", "heavy", "large", "tall", "short", "petite", "stocky", "lean", "broad", "curvy", "slender", "chubby", "fit"}
|
||||
for t in types {
|
||||
if strings.contains(lower, t) {
|
||||
return strings.clone(t)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_outfit :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
|
||||
// Try "wearing X" pattern
|
||||
if idx := strings.index(lower, "wearing "); idx >= 0 {
|
||||
rest := lower[idx+len("wearing "):]
|
||||
end := len(rest)
|
||||
if comma := strings.index(rest, ","); comma >= 0 { end = comma }
|
||||
if period := strings.index(rest, "."); period >= 0 && period < end { end = period }
|
||||
return strings.clone(rest[:end])
|
||||
}
|
||||
|
||||
// Try "dressed in X" pattern
|
||||
if idx := strings.index(lower, "dressed in "); idx >= 0 {
|
||||
rest := lower[idx+len("dressed in "):]
|
||||
end := len(rest)
|
||||
if comma := strings.index(rest, ","); comma >= 0 { end = comma }
|
||||
return strings.clone(rest[:end])
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
extract_accessories :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
accessories := []string{"glasses", "sunglasses", "hat", "cap", "beanie", "scarf", "necklace", "pendant", "earrings", "bracelet", "watch", "ring", "backpack", "bag", "gloves", "belt", "tie", "bow", "crown", "headband"}
|
||||
found: [dynamic]string
|
||||
for a in accessories {
|
||||
if strings.contains(lower, a) {
|
||||
append(&found, a)
|
||||
}
|
||||
}
|
||||
if len(found) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := strings.join(found[:], ", ")
|
||||
defer delete(found)
|
||||
return result
|
||||
}
|
||||
|
||||
extract_distinguishing_features :: proc(description: string) -> string {
|
||||
lower := strings.to_lower(description)
|
||||
defer delete(lower)
|
||||
|
||||
features := []string{"scar", "scars", "tattoo", "tattoos", "freckles", "mole", "birthmark", "beard", "mustache", "goatee", "monocle", "prosthetic", "piercing", "piercings", "dimples", "cleft chin"}
|
||||
found: [dynamic]string
|
||||
for f in features {
|
||||
if strings.contains(lower, f) {
|
||||
append(&found, f)
|
||||
}
|
||||
}
|
||||
if len(found) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := strings.join(found[:], ", ")
|
||||
defer delete(found)
|
||||
return result
|
||||
}
|
||||
|
||||
parse_description_to_template :: proc(description: string) -> Character_Prompt_Template {
|
||||
return Character_Prompt_Template{
|
||||
age = extract_age(description),
|
||||
gender = extract_gender(description),
|
||||
hair_color = extract_hair_color(description),
|
||||
hair_style = extract_hair_style(description),
|
||||
eye_color = extract_eye_color(description),
|
||||
skin_tone = extract_skin_tone(description),
|
||||
body_type = extract_body_type(description),
|
||||
outfit = extract_outfit(description),
|
||||
accessories = extract_accessories(description),
|
||||
distinguishing_features = extract_distinguishing_features(description),
|
||||
}
|
||||
}
|
||||
|
||||
extract_color_palette :: proc(description: string) -> Color_Palette {
|
||||
return Color_Palette{
|
||||
hair = extract_hair_color(description),
|
||||
eyes = extract_eye_color(description),
|
||||
skin = extract_skin_tone(description),
|
||||
outfit = extract_outfit(description),
|
||||
}
|
||||
}
|
||||
|
||||
template_to_string :: proc(t: Character_Prompt_Template) -> string {
|
||||
parts: [dynamic]string
|
||||
|
||||
if len(t.age) > 0 { append(&parts, fmt.tprintf("%s-year-old", t.age)) }
|
||||
if len(t.gender) > 0 { append(&parts, t.gender) }
|
||||
if len(t.hair_color) > 0 || len(t.hair_style) > 0 {
|
||||
hair := ""
|
||||
if len(t.hair_color) > 0 { hair = t.hair_color }
|
||||
if len(t.hair_style) > 0 {
|
||||
if len(hair) > 0 { hair = fmt.tprintf("%s %s", hair, t.hair_style) }
|
||||
else { hair = t.hair_style }
|
||||
}
|
||||
append(&parts, fmt.tprintf("%s hair", hair))
|
||||
}
|
||||
if len(t.eye_color) > 0 { append(&parts, fmt.tprintf("%s eyes", t.eye_color)) }
|
||||
if len(t.skin_tone) > 0 { append(&parts, fmt.tprintf("%s skin", t.skin_tone)) }
|
||||
if len(t.body_type) > 0 { append(&parts, fmt.tprintf("%s build", t.body_type)) }
|
||||
if len(t.outfit) > 0 { append(&parts, fmt.tprintf("wearing %s", t.outfit)) }
|
||||
if len(t.accessories) > 0 { append(&parts, t.accessories) }
|
||||
if len(t.distinguishing_features) > 0 { append(&parts, t.distinguishing_features) }
|
||||
|
||||
result := strings.join(parts[:], ", ")
|
||||
defer delete(parts)
|
||||
return result
|
||||
}
|
||||
@@ -46,7 +46,6 @@ dispose_script_owned :: proc(script: ^Comic_Script) {
|
||||
for d in pan.dialogue {
|
||||
delete(d.speaker_id)
|
||||
delete(d.text)
|
||||
delete(d.emotion)
|
||||
}
|
||||
delete(pan.dialogue)
|
||||
for s in pan.sound_effects { delete(s) }
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package core
|
||||
|
||||
import "core:strings"
|
||||
|
||||
QUALITY_MODIFIER :: "high quality, detailed, clean lines, vibrant colors, professional illustration, best quality, masterpiece"
|
||||
|
||||
NEGATIVE_PROMPT_CHARACTER :: "blurry, low quality, distorted face, extra limbs, bad anatomy, deformed, watermark, signature"
|
||||
|
||||
NEGATIVE_PROMPT_PANEL :: "blurry, low quality, distorted face, extra limbs, bad anatomy, deformed, watermark, signature, text, speech bubble"
|
||||
|
||||
ART_STYLE_KEYWORDS :: [8]string{
|
||||
"Japanese manga style, black and white ink, screentone shading, speed lines, dramatic shadows, clean linework, right-to-left reading",
|
||||
"American comic book style, bold outlines, vibrant colors, dynamic poses, cross-hatching, Ben-Day dots, left-to-right reading",
|
||||
"16-bit pixel art style, retro game aesthetic, limited color palette, crisp pixels, dithering, nostalgic",
|
||||
"watercolor painting style, soft edges, paper texture, translucent washes, delicate linework, artistic",
|
||||
"film noir style, high contrast black and white, dramatic chiaroscuro lighting, gritty, shadow-heavy, 1940s aesthetic",
|
||||
"chibi anime style, super deformed characters, big heads, small bodies, cute, colorful, expressive",
|
||||
"pencil sketch style, rough linework, cross-hatching, construction lines, draft aesthetic, monochrome",
|
||||
"cyberpunk aesthetic, neon lighting, futuristic cityscapes, holographic elements, high contrast, synthwave colors",
|
||||
}
|
||||
|
||||
get_style_keywords :: proc(key: Art_Style_Key) -> string {
|
||||
keywords := ART_STYLE_KEYWORDS
|
||||
return keywords[int(key)]
|
||||
}
|
||||
|
||||
parse_art_style_key :: proc(raw: string) -> Art_Style_Key {
|
||||
lower := strings.to_lower(raw)
|
||||
if strings.contains(lower, "manga") { return .Manga }
|
||||
if strings.contains(lower, "western") || strings.contains(lower, "american") || strings.contains(lower, "comic") { return .Western_Comic }
|
||||
if strings.contains(lower, "pixel") || strings.contains(lower, "retro") || strings.contains(lower, "8-bit") || strings.contains(lower, "16-bit") { return .Pixel_Art }
|
||||
if strings.contains(lower, "watercolor") || strings.contains(lower, "watercolour") { return .Watercolor }
|
||||
if strings.contains(lower, "noir") || strings.contains(lower, "film noir") { return .Noir }
|
||||
if strings.contains(lower, "chibi") || strings.contains(lower, "super deformed") { return .Chibi }
|
||||
if strings.contains(lower, "sketch") || strings.contains(lower, "pencil") || strings.contains(lower, "draw") { return .Sketch }
|
||||
if strings.contains(lower, "cyberpunk") || strings.contains(lower, "neon") || strings.contains(lower, "synthwave") { return .Cyberpunk }
|
||||
return .Western_Comic
|
||||
}
|
||||
|
||||
Sdxl_Image_Size :: enum {
|
||||
Square,
|
||||
Landscape_16_9,
|
||||
Landscape_4_3,
|
||||
Portrait_4_3,
|
||||
Square_HD,
|
||||
Portrait_16_9,
|
||||
}
|
||||
|
||||
sdxl_size_name :: proc(s: Sdxl_Image_Size) -> string {
|
||||
switch s {
|
||||
case .Square: return "square"
|
||||
case .Landscape_16_9: return "landscape_16_9"
|
||||
case .Landscape_4_3: return "landscape_4_3"
|
||||
case .Portrait_4_3: return "portrait_4_3"
|
||||
case .Square_HD: return "square_hd"
|
||||
case .Portrait_16_9: return "portrait_16_9"
|
||||
}
|
||||
return "landscape_4_3"
|
||||
}
|
||||
|
||||
get_image_size_for_shot_type :: proc(shot: Shot_Type) -> Sdxl_Image_Size {
|
||||
switch shot {
|
||||
case .Establishing, .Wide, .Aerial:
|
||||
return .Landscape_16_9
|
||||
case .Medium, .Over_Shoulder:
|
||||
return .Landscape_4_3
|
||||
case .Close_Up:
|
||||
return .Portrait_4_3
|
||||
case .Extreme_Close_Up:
|
||||
return .Square_HD
|
||||
}
|
||||
return .Landscape_4_3
|
||||
}
|
||||
|
||||
emotion_name :: proc(e: Emotion) -> string {
|
||||
switch e {
|
||||
case .Happy: return "happy"
|
||||
case .Sad: return "sad"
|
||||
case .Angry: return "angry"
|
||||
case .Surprised: return "surprised"
|
||||
case .Neutral: return "neutral"
|
||||
case .Determined: return "determined"
|
||||
}
|
||||
return "neutral"
|
||||
}
|
||||
|
||||
parse_emotion :: proc(raw: string) -> Emotion {
|
||||
lower := strings.to_lower(raw)
|
||||
if strings.contains(lower, "happy") || strings.contains(lower, "joy") || strings.contains(lower, "smile") { return .Happy }
|
||||
if strings.contains(lower, "sad") || strings.contains(lower, "cry") || strings.contains(lower, "sorrow") { return .Sad }
|
||||
if strings.contains(lower, "angry") || strings.contains(lower, "rage") || strings.contains(lower, "furious") { return .Angry }
|
||||
if strings.contains(lower, "surpris") || strings.contains(lower, "shock") || strings.contains(lower, "amazed") { return .Surprised }
|
||||
if strings.contains(lower, "determin") || strings.contains(lower, "resolv") || strings.contains(lower, "focused") { return .Determined }
|
||||
return .Neutral
|
||||
}
|
||||
@@ -54,3 +54,27 @@ normalize_script :: proc(script: Comic_Script) -> Comic_Script {
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
count_character_appearances :: proc(script: ^Comic_Script) {
|
||||
for i in 0..<len(script.characters) {
|
||||
char_id := script.characters[i].id
|
||||
count := 0
|
||||
first_panel := ""
|
||||
for page_idx in 0..<len(script.pages) {
|
||||
for panel_idx in 0..<len(script.pages[page_idx].panels) {
|
||||
panel := &script.pages[page_idx].panels[panel_idx]
|
||||
for cid in panel.characters_present {
|
||||
if cid == char_id {
|
||||
count += 1
|
||||
if len(first_panel) == 0 {
|
||||
first_panel = panel.panel_id
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
script.characters[i].appearance_count = count
|
||||
script.characters[i].first_appearance_panel = first_panel
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,15 @@ Bubble_Type :: enum {
|
||||
Sound_Effect,
|
||||
}
|
||||
|
||||
Emotion :: enum {
|
||||
Happy,
|
||||
Sad,
|
||||
Angry,
|
||||
Surprised,
|
||||
Neutral,
|
||||
Determined,
|
||||
}
|
||||
|
||||
Transition_Type :: enum {
|
||||
None,
|
||||
Fade,
|
||||
@@ -78,6 +87,17 @@ Transition_Type :: enum {
|
||||
Action_Lines,
|
||||
}
|
||||
|
||||
Art_Style_Key :: enum {
|
||||
Manga,
|
||||
Western_Comic,
|
||||
Pixel_Art,
|
||||
Watercolor,
|
||||
Noir,
|
||||
Chibi,
|
||||
Sketch,
|
||||
Cyberpunk,
|
||||
}
|
||||
|
||||
Position :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
@@ -126,7 +146,7 @@ Dialogue :: struct {
|
||||
speaker_id: string,
|
||||
text: string,
|
||||
bubble_type: Bubble_Type,
|
||||
emotion: string,
|
||||
emotion: Emotion,
|
||||
}
|
||||
|
||||
Panel :: struct {
|
||||
|
||||
Reference in new issue
Block a user