540 lines
26 KiB
Python
540 lines
26 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
try:
|
|
from . import outercourse_action_policy as outercourse_policy
|
|
from .krea_action_context import (
|
|
axis_values_text,
|
|
is_climax_text,
|
|
is_oral_text,
|
|
is_outercourse_text,
|
|
is_toy_assisted_double_text,
|
|
position_context_text,
|
|
)
|
|
from .krea_detail import limit_detail_for_density
|
|
except ImportError: # Allows local smoke tests with `python -c`.
|
|
import outercourse_action_policy as outercourse_policy
|
|
from krea_action_context import (
|
|
axis_values_text,
|
|
is_climax_text,
|
|
is_oral_text,
|
|
is_outercourse_text,
|
|
is_toy_assisted_double_text,
|
|
position_context_text,
|
|
)
|
|
from krea_detail import limit_detail_for_density
|
|
|
|
|
|
def _clean(value: Any) -> str:
|
|
text = "" if value is None else str(value)
|
|
text = text.replace("\n", " ")
|
|
text = re.sub(r"\s+", " ", text).strip()
|
|
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
|
return text
|
|
|
|
|
|
def pov_ejaculation_target(context: str) -> str:
|
|
if any(
|
|
token in context
|
|
for token in (
|
|
"lower back",
|
|
"ass",
|
|
"rear-entry",
|
|
"rear entry",
|
|
"face-down",
|
|
"face down",
|
|
"bent-over",
|
|
"bent over",
|
|
"doggy",
|
|
"on all fours",
|
|
"hips high",
|
|
"hips raised",
|
|
"raised ass",
|
|
"behind her",
|
|
)
|
|
):
|
|
return "across her ass, thighs, and lower back"
|
|
if any(token in context for token in ("pussy", "open thighs", "thighs", "legs open")):
|
|
return "across her pussy and thighs"
|
|
if any(token in context for token in ("face", "mouth", "lips", "tongue", "chin")):
|
|
return "onto her face and chest"
|
|
return "onto her body"
|
|
|
|
|
|
def pov_contact_clause(
|
|
action: Any,
|
|
role_graph: Any,
|
|
hard_item: Any,
|
|
axis_values: Any,
|
|
context: str,
|
|
) -> str:
|
|
is_climax = is_climax_text(action, role_graph, hard_item, axis_values_text(axis_values))
|
|
if is_climax:
|
|
return f"as he ejaculates semen {pov_ejaculation_target(context)}"
|
|
is_anal = any(
|
|
token in context
|
|
for token in (
|
|
"anal",
|
|
"into her ass",
|
|
"penis entering ass",
|
|
"ass stretched",
|
|
"thrusts into her ass",
|
|
)
|
|
)
|
|
contact = "as his penis penetrates her ass" if is_anal else "as his penis penetrates her pussy"
|
|
if is_toy_assisted_double_text(action, role_graph, hard_item, axis_values_text(axis_values)):
|
|
contact = f"{contact} while a toy is positioned at the second penetration point"
|
|
return contact
|
|
|
|
|
|
def pov_clean_detail(detail: Any, context: str, detail_density: str) -> str:
|
|
detail = _clean(detail).strip(" .;")
|
|
if not detail:
|
|
return ""
|
|
detail = re.sub(r"\bthe POV viewer\b", "the viewer", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bthe man's\b", "the viewer's", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bthe man\b", "the viewer", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bhe\b", "the viewer", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bhis\b", "the viewer's", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bhim\b", "the viewer", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(
|
|
r"^(?:missionary|cowgirl|reverse cowgirl|doggy style|standing sex|spooning sex|edge-supported|edge-of-bed|raised edge|kneeling straddle|lotus sex|bent-over|face-down ass-up|side-lying|kneeling rear-entry)\s+(?:position|pose)\s+(?:featuring|with|while|,)?\s*",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(
|
|
r"^(?:kneeling oral|standing oral|chair oral|side-lying oral|sixty-nine|edge-supported oral|edge-of-bed oral|reclining cunnilingus|straddled oral|spread-leg oral)\s+(?:position|pose),?\s*",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(r"^(?:featuring|with)\s+", "", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(
|
|
r"^(?:full-body|explicit|close-contact|deep|hardcore|vaginal|anal)?\s*(?:penetrative sex|vaginal sex|anal sex|penetration with visible genital contact|hardcore vaginal thrusting|hardcore anal thrusting),?\s*",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(
|
|
r"\b(?:front-facing|close-up|wide full-body|wide|overhead|mirror-reflected|low-angle|side-profile|bed-level)\s+view of\b",
|
|
"visible",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(
|
|
r",?\s*\bthe viewer is behind her at hip level with (?:his|the viewer's) hands on her hips in the foreground as (?:his|the viewer's) penis (?:thrusts into her|penetrates her pussy)\b",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(
|
|
r",?\s*\bthe woman is on all fours directly in front of the viewer with hips raised and back arched\b",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
if any(token in context for token in ("ass raised", "on all fours", "doggy", "rear-entry", "bent-over", "face-down")):
|
|
detail = re.sub(
|
|
r",?\s*\b(?:one body pinned under another|bodies stacked close together|bodies tangled on the sheets)\b",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
if "toy is positioned at the second penetration point" in context:
|
|
detail = re.sub(
|
|
r",?\s*\b(?:toy aligned for a second penetration point|toy-assisted second contact aligned behind the body)\b",
|
|
"",
|
|
detail,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
detail = re.sub(r"\bwith with\b", "with", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\s*,\s*", ", ", detail)
|
|
detail = re.sub(r",\s*,", ",", detail).strip(" ,;")
|
|
return limit_detail_for_density(detail, detail_density, is_climax_text(context, detail))
|
|
|
|
|
|
def pov_clean_oral_detail(detail: Any, context: str, detail_density: str) -> str:
|
|
detail = pov_clean_detail(detail, context, detail_density)
|
|
if not detail:
|
|
return ""
|
|
duplicate_patterns = (
|
|
r"\bthe woman takes the viewer's penis in her mouth with\s+",
|
|
r"\bthe woman takes the viewer's penis in her mouth\b,?\s*",
|
|
r"\bher mouth on the viewer's penis\b,?\s*",
|
|
r"\bthe viewer's mouth on the woman's pussy\b,?\s*",
|
|
r"\bmouth on the viewer's penis\b,?\s*",
|
|
r"\bmouth on the woman's pussy\b,?\s*",
|
|
)
|
|
for pattern in duplicate_patterns:
|
|
detail = re.sub(pattern, "", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\bwith\s+(?=[,;.]|$)", "", detail, flags=re.IGNORECASE)
|
|
detail = re.sub(r"\s*,\s*", ", ", detail)
|
|
detail = re.sub(r",\s*,", ",", detail)
|
|
return _clean(detail).strip(" ,;")
|
|
|
|
|
|
def pov_hardcore_pose_sentence(
|
|
action: Any,
|
|
role_graph: Any,
|
|
hard_item: Any,
|
|
composition: Any = "",
|
|
axis_values: Any = None,
|
|
detail_density: str = "balanced",
|
|
) -> str:
|
|
context = position_context_text(role_graph, hard_item, composition, axis_values)
|
|
action_text = _clean(action)
|
|
action_lower = action_text.lower()
|
|
if not context:
|
|
context = action_lower
|
|
position_text = ""
|
|
if isinstance(axis_values, dict):
|
|
position_text = _clean(axis_values.get("position", "")).lower()
|
|
position_context = position_text or context
|
|
|
|
def sentence(base: str) -> str:
|
|
details = ""
|
|
if ";" in action_text:
|
|
details = pov_clean_detail(action_text.split(";", 1)[1], f"{context} {base}", detail_density)
|
|
return f"{base}; {details}" if details else base
|
|
|
|
def outercourse_sentence(base: str) -> str:
|
|
return _clean(base).rstrip(".")
|
|
|
|
def oral_sentence(base: str) -> str:
|
|
details = ""
|
|
if ";" in action_text:
|
|
details = pov_clean_oral_detail(action_text.split(";", 1)[1], f"{context} {base}", detail_density)
|
|
return _clean(f"{base}; {details}" if details else base).rstrip(".")
|
|
|
|
def oral_direction() -> tuple[bool, bool]:
|
|
oral_context = f"{context} {action_lower}"
|
|
woman_gives = any(
|
|
token in oral_context
|
|
for token in (
|
|
"fellatio",
|
|
"blowjob",
|
|
"deepthroat",
|
|
"penis sucking",
|
|
"penis in her mouth",
|
|
"mouth on the viewer's penis",
|
|
"mouth on viewer's penis",
|
|
"takes the viewer's penis",
|
|
"takes the man's penis",
|
|
"mouth at penis level",
|
|
"lips wrapped",
|
|
)
|
|
)
|
|
man_gives = any(
|
|
token in oral_context
|
|
for token in (
|
|
"cunnilingus",
|
|
"pussy licking",
|
|
"mouth on the woman's pussy",
|
|
"mouth on her pussy",
|
|
"mouth pressed to her pussy",
|
|
"tongue on pussy",
|
|
"face-sitting",
|
|
"straddles his face",
|
|
"straddling the viewer's face",
|
|
)
|
|
)
|
|
if "sixty-nine" in oral_context:
|
|
return True, True
|
|
return woman_gives, man_gives
|
|
|
|
penetrative_tokens = (
|
|
"penetrat",
|
|
"thrust",
|
|
"anal",
|
|
"cowgirl",
|
|
"missionary",
|
|
"doggy",
|
|
"rear-entry",
|
|
"spooning",
|
|
"bent-over",
|
|
"face-down",
|
|
"ejaculat",
|
|
"semen",
|
|
"cumshot",
|
|
"climax",
|
|
)
|
|
has_penetrative_context = any(token in context or token in action_lower for token in penetrative_tokens)
|
|
|
|
if (
|
|
"face-sitting" in context
|
|
or "face sitting" in context
|
|
or ("straddles" in context and "face" in context and "pussy" in context)
|
|
):
|
|
return outercourse_sentence(
|
|
"The woman is above the camera in a close first-person underview, straddling the viewer's face with her thighs on both sides of his head; "
|
|
"her pussy is directly over the viewer's mouth in the lower foreground, tongue contact visible from below"
|
|
)
|
|
|
|
if is_outercourse_text(context, action_lower):
|
|
action_kind = outercourse_policy.infer_outercourse_action_kind(position_text)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_GENERIC:
|
|
action_kind = outercourse_policy.infer_outercourse_action_kind(context, action_lower)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_BOOBJOB:
|
|
return outercourse_sentence(
|
|
"POV boobjob position: the viewer reclines with thighs open while the woman kneels upright between his legs facing him; "
|
|
"the viewer's penis rises vertically in the lower foreground and is squeezed between her pressed-together breasts; "
|
|
"the woman's own fingers and nails cup her breasts from the outside and push soft breast tissue inward around the shaft, "
|
|
"with the glans emerging above the cleavage directly below her mouth"
|
|
)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_TESTICLE:
|
|
return outercourse_sentence(
|
|
"The woman bends forward and kneels very low between the viewer's open thighs with her shoulders between his knees; "
|
|
"her face is below the viewer's penis at testicle height, mouth and tongue licking the viewer's balls while his penis points upward in the lower foreground above her forehead"
|
|
)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_PENIS_LICKING:
|
|
return outercourse_sentence(
|
|
"The woman bends forward between the viewer's open thighs with her head low under the viewer's penis; "
|
|
"her face is just under the penis while her tongue touches the underside from the base toward the glans at the tip, "
|
|
"one hand steadying the base of the viewer's penis in the lower foreground"
|
|
)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_HANDJOB:
|
|
return outercourse_sentence(
|
|
"POV handjob position: the viewer reclines with thighs open while the woman kneels between his legs facing him, "
|
|
"torso leaning forward and face visible behind the penis; "
|
|
"the woman's right hand wraps around the viewer's penis in the lower foreground and strokes along the shaft toward the glans, "
|
|
"while her left hand steadies the base with her fingers and nails visibly gripping the penis; "
|
|
"the viewer's thighs and pelvis frame the lower edges without his hands covering the action"
|
|
)
|
|
if action_kind == outercourse_policy.OUTERCOURSE_FOOTJOB:
|
|
return outercourse_sentence(
|
|
"The woman faces the viewer with her hips back, torso visible behind her raised legs, and both knees bent open toward the camera; "
|
|
"her soles wrap around the penis shaft in the lower foreground, toes curled around the penis shaft with her face visible beyond her feet"
|
|
)
|
|
return outercourse_sentence(
|
|
"The woman stays close to the viewer's pelvis, keeping the non-penetrative contact centered in the lower foreground with her face visible behind the contact"
|
|
)
|
|
|
|
if is_oral_text(context, action_lower) and not has_penetrative_context:
|
|
woman_gives, man_gives = oral_direction()
|
|
if "sixty-nine" in position_context:
|
|
return oral_sentence(
|
|
"POV sixty-nine oral position: the woman lies head-to-hips over the viewer, her pelvis close to his face and her head lowered toward his hips; "
|
|
"her mouth on the viewer's penis and the viewer's mouth on the woman's pussy, with her torso, hips, mouth, and the viewer's lower-foreground body cues aligned in one first-person frame"
|
|
)
|
|
if "side-lying oral" in position_context or "side lying oral" in position_context:
|
|
if woman_gives and not man_gives:
|
|
return oral_sentence(
|
|
"POV side-lying oral position: the viewer lies on his side with hips angled toward the woman while she lies beside his thighs; "
|
|
"her head stays at penis height with her mouth on the viewer's penis, shoulders and hands close to his pelvis in the lower foreground"
|
|
)
|
|
return oral_sentence(
|
|
"POV side-lying cunnilingus position: the woman lies on her side with her top thigh lifted while the viewer lies beside her hips; "
|
|
"his face is at pussy height, with her thigh, hip, and torso forming a clear side-profile first-person frame"
|
|
)
|
|
if (
|
|
"edge-supported oral" in position_context
|
|
or "edge-of-bed oral" in position_context
|
|
or "edge of bed oral" in position_context
|
|
or "raised edge" in position_context
|
|
):
|
|
if woman_gives and not man_gives:
|
|
return oral_sentence(
|
|
"POV raised-edge oral position: the viewer sits at the raised edge with legs apart while the woman kneels directly between his thighs; "
|
|
"her head is at penis height, mouth on the viewer's penis, with his thighs framing her shoulders in the lower foreground"
|
|
)
|
|
return oral_sentence(
|
|
"POV raised-edge cunnilingus position: the woman reclines at the raised edge with thighs open toward the viewer; "
|
|
"the viewer kneels between her legs with his face at pussy height, her hips and open thighs framing the first-person view"
|
|
)
|
|
if "chair oral" in position_context:
|
|
if woman_gives and not man_gives:
|
|
return oral_sentence(
|
|
"POV chair oral position: the viewer sits in a chair with legs apart while the woman kneels between the viewer's thighs; "
|
|
"her head is low at his pelvis, mouth on the viewer's penis, with chair seat, thighs, and hands anchoring the lower foreground"
|
|
)
|
|
return oral_sentence(
|
|
"POV chair cunnilingus position: the woman sits in the chair with thighs open while the viewer kneels between her legs; "
|
|
"his face is at pussy height and her hips, knees, and chair seat define the first-person geometry"
|
|
)
|
|
if "standing oral" in position_context:
|
|
if man_gives and not woman_gives:
|
|
return oral_sentence(
|
|
"POV standing cunnilingus position: the woman stands braced with one thigh lifted while the viewer kneels in front of her; "
|
|
"his face is at pussy height, with her raised thigh, hips, and standing leg clearly framing the view"
|
|
)
|
|
return oral_sentence(
|
|
"POV standing oral position: the viewer stands over her with hips forward while the woman kneels directly in front of him at hip height; "
|
|
"her head is tilted up at penis level, mouth on the viewer's penis, with his thighs and hands in the lower foreground"
|
|
)
|
|
if (
|
|
"reclining cunnilingus" in position_context
|
|
or "spread-leg oral" in position_context
|
|
or "open-thigh" in position_context
|
|
or "open thigh" in position_context
|
|
):
|
|
if woman_gives and not man_gives:
|
|
return oral_sentence(
|
|
"POV reclining oral position: the viewer reclines with thighs apart while the woman kneels low between his legs; "
|
|
"her face stays at penis height with her mouth on the viewer's penis and his thighs framing the first-person view"
|
|
)
|
|
return oral_sentence(
|
|
"POV open-thigh cunnilingus position: the woman reclines on her back with thighs spread toward the viewer; "
|
|
"the viewer kneels between her legs with his face at pussy height, her knees, hips, and torso aligned toward the camera"
|
|
)
|
|
if "straddled oral" in position_context:
|
|
if woman_gives and not man_gives:
|
|
return oral_sentence(
|
|
"POV straddled oral position: the viewer leans forward near the woman's face while she kneels below his pelvis; "
|
|
"her mouth stays on the viewer's penis with her head tilted upward and his thighs framing the lower foreground"
|
|
)
|
|
return oral_sentence(
|
|
"POV straddled cunnilingus position: the woman straddles above the viewer's face with her thighs framing his head; "
|
|
"her pussy stays directly over the viewer's mouth in a close first-person oral frame"
|
|
)
|
|
if "kneeling oral" in position_context or "kneeling" in position_context:
|
|
if man_gives and not woman_gives:
|
|
return oral_sentence(
|
|
"POV kneeling cunnilingus position: the woman kneels with thighs parted and hips angled forward while the viewer kneels in front of her; "
|
|
"his face is at pussy height, with her knees, hips, and torso readable from the first-person angle"
|
|
)
|
|
return oral_sentence(
|
|
"POV kneeling oral position: the viewer stands over her with hips forward while the woman kneels directly in front of him; "
|
|
"her head is at penis height, mouth on the viewer's penis, shoulders below his hips and his thighs framing the lower foreground"
|
|
)
|
|
if man_gives and not woman_gives:
|
|
return oral_sentence(
|
|
"POV cunnilingus position: the woman lies back with thighs open toward the viewer while he kneels between her legs; "
|
|
"his face is at pussy height, with her knees, hips, and torso forming the first-person frame"
|
|
)
|
|
return oral_sentence(
|
|
"POV oral position: the woman kneels close at the viewer's pelvis with her head at penis height; "
|
|
"her mouth is on the viewer's penis, shoulders between his thighs, and the viewer's hands or thighs anchor the lower foreground"
|
|
)
|
|
|
|
if not has_penetrative_context:
|
|
return ""
|
|
oral_only = any(token in context for token in ("oral", "blowjob", "cunnilingus", "mouth on", "penis in her mouth"))
|
|
if oral_only and not any(token in context for token in ("penetrat", "thrust", "anal", "ejaculat", "semen", "cumshot", "climax")):
|
|
return ""
|
|
|
|
contact = pov_contact_clause(action, role_graph, hard_item, axis_values, context)
|
|
|
|
if "reverse cowgirl" in position_context:
|
|
return sentence(
|
|
"POV reverse cowgirl position: the viewer lies on his back while the woman straddles his hips facing away; "
|
|
f"her back, ass, thighs, and the viewer's foreground legs are visible {contact}"
|
|
)
|
|
if "cowgirl" in position_context or "straddling a partner" in position_context or "squatting on top" in position_context:
|
|
return sentence(
|
|
"POV cowgirl position: the viewer lies on his back while the woman straddles his hips facing him; "
|
|
f"her torso, hips, and open thighs fill the frame from below {contact}"
|
|
)
|
|
if "lotus" in position_context or "seated in a partner's lap" in position_context:
|
|
return sentence(
|
|
"POV lotus position: the viewer sits upright while the woman sits in his lap facing him with her legs around his hips; "
|
|
f"her torso and hips stay close to the viewer {contact}"
|
|
)
|
|
if "kneeling straddle" in position_context:
|
|
return sentence(
|
|
"POV kneeling straddle position: the viewer kneels upright while the woman straddles his hips facing him; "
|
|
f"both torsos are upright and her hips press directly against him {contact}"
|
|
)
|
|
if "face-down" in position_context or "face down" in position_context:
|
|
return sentence(
|
|
"The woman is seen from behind with her ass raised toward the POV viewer, lying face-down with hips lifted; "
|
|
f"the viewer looks down at her raised ass with foreground hands on her hips {contact}"
|
|
)
|
|
if (
|
|
"edge-supported" in position_context
|
|
or "raised edge" in position_context
|
|
or "edge of bed" in position_context
|
|
or "bed edge" in position_context
|
|
or (not position_text and "kneels between her legs" in context)
|
|
):
|
|
return sentence(
|
|
"POV raised-edge penetration position: the woman reclines at the raised edge with thighs open toward the viewer; "
|
|
f"the viewer kneels between her legs with his hands near her hips {contact}"
|
|
)
|
|
if "standing" in position_context:
|
|
return sentence(
|
|
"POV standing rear-entry position: the woman stands braced in front of the viewer with hips angled back and legs steady; "
|
|
f"the viewer stands behind her at hip level {contact}"
|
|
)
|
|
if "spooning" in position_context or "side-lying" in position_context or "lies on her side" in position_context:
|
|
return sentence(
|
|
"POV side-lying sex position: the woman lies on her side in front of the viewer with thighs parted; "
|
|
f"the viewer is behind her along the same body line {contact}"
|
|
)
|
|
if "doggy" in position_context or "all fours" in position_context or "rear-entry" in position_context:
|
|
return sentence(
|
|
"Top-down POV doggy position from behind: the camera looks down over the viewer's hands onto the woman's raised hips; "
|
|
f"the woman is on all fours with chest low, forearms folded, cheek turned sideways far ahead, back arched, and hips raised high toward the camera; "
|
|
f"the viewer's hands hold her hips with natural lower-body POV cues in the foreground {contact}"
|
|
)
|
|
if "kneeling" in position_context:
|
|
return sentence(
|
|
"POV kneeling rear-entry position: the woman kneels forward in front of the viewer with hips raised and thighs apart; "
|
|
f"the viewer kneels behind her at hip level with foreground hands near her waist {contact}"
|
|
)
|
|
if "bent-over" in position_context or "bent over" in position_context or "bent forward" in position_context:
|
|
return sentence(
|
|
"The woman is seen from behind with her ass raised toward the POV viewer, bent forward at the waist with hips lifted and head turned back; "
|
|
f"the viewer looks down at her raised ass from behind with foreground hands near her hips {contact}"
|
|
)
|
|
if "missionary" in position_context or (not position_text and "lies on her back" in context and ("legs open" in context or "thighs open" in context)):
|
|
return sentence(
|
|
"POV missionary position: the woman lies on her back with legs open around the viewer's hips; "
|
|
f"the viewer is above her with foreground arms braced beside her body {contact}"
|
|
)
|
|
return sentence(
|
|
"POV penetrative sex position: the woman is directly in front of the viewer with legs open around his hips; "
|
|
f"the viewer's foreground hands and body position define the first-person angle {contact}"
|
|
)
|
|
|
|
|
|
def pov_action_phrase(
|
|
action: Any,
|
|
pov_labels: list[str],
|
|
role_graph: Any = "",
|
|
hard_item: Any = "",
|
|
composition: Any = "",
|
|
axis_values: Any = None,
|
|
detail_density: str = "balanced",
|
|
) -> str:
|
|
rendered = _clean(action)
|
|
if not rendered or not pov_labels:
|
|
return rendered
|
|
if "Man A" in pov_labels:
|
|
pov_sentence = pov_hardcore_pose_sentence(
|
|
rendered,
|
|
role_graph,
|
|
hard_item,
|
|
composition,
|
|
axis_values,
|
|
detail_density,
|
|
)
|
|
if pov_sentence:
|
|
return pov_sentence
|
|
for label in sorted(pov_labels, key=len, reverse=True):
|
|
escaped = re.escape(label)
|
|
rendered = re.sub(rf"\b{escaped}'s\b", "the viewer's", rendered)
|
|
rendered = re.sub(rf"\b{escaped}\b", "the viewer", rendered)
|
|
if "Man A" in pov_labels:
|
|
rendered = re.sub(r"\bthe man's\b", "the viewer's", rendered, flags=re.IGNORECASE)
|
|
rendered = re.sub(r"\bthe man\b", "the viewer", rendered, flags=re.IGNORECASE)
|
|
rendered = re.sub(r"\bhe\b", "the viewer", rendered, flags=re.IGNORECASE)
|
|
rendered = re.sub(r"\bhim\b", "the viewer", rendered, flags=re.IGNORECASE)
|
|
rendered = re.sub(r"\bhis\b", "the viewer's", rendered, flags=re.IGNORECASE)
|
|
rendered = re.sub(
|
|
r"\bthe viewer lies on the viewer's back under her\b",
|
|
"the viewer reclines underneath her",
|
|
rendered,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
rendered = re.sub(
|
|
r"\bthe viewer lies on the viewer's back\b",
|
|
"the viewer reclines",
|
|
rendered,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
rendered = re.sub(r"\bthe viewer is positioned\b", "the POV camera is positioned", rendered, flags=re.IGNORECASE)
|
|
return rendered
|