Extract Krea action family dispatch
This commit is contained in:
@@ -158,14 +158,16 @@ Already isolated:
|
|||||||
outercourse, oral, penetration, toy/double-contact, and anchor dedupe paths.
|
outercourse, oral, penetration, toy/double-contact, and anchor dedupe paths.
|
||||||
- `krea_action_climax.py` owns climax-specific role/detail cleanup and aftermath
|
- `krea_action_climax.py` owns climax-specific role/detail cleanup and aftermath
|
||||||
view dedupe.
|
view dedupe.
|
||||||
- `krea_actions.py` owns non-POV hardcore action sentence dispatch.
|
- `krea_action_dispatch.py` owns non-POV role normalization, action-family
|
||||||
|
classification, and family-specific detail cleanup.
|
||||||
|
- `krea_actions.py` owns final non-POV hardcore action sentence assembly.
|
||||||
- `krea_pov_actions.py` owns POV hardcore action sentence rewriting and
|
- `krea_pov_actions.py` owns POV hardcore action sentence rewriting and
|
||||||
first-person body geometry.
|
first-person body geometry.
|
||||||
|
|
||||||
Improve later:
|
Improve later:
|
||||||
|
|
||||||
- make `krea_actions.hardcore_action_sentence` dispatch by action family instead
|
- add metadata fields such as `action_family` / `position_family` to reduce
|
||||||
of long conditional chains;
|
keyword guessing in hardcore formatter dispatch;
|
||||||
- add route-level smoke fixtures for representative metadata rows;
|
- add route-level smoke fixtures for representative metadata rows;
|
||||||
|
|
||||||
### SDXL Formatter Path
|
### SDXL Formatter Path
|
||||||
@@ -344,10 +346,9 @@ Medium-term:
|
|||||||
|
|
||||||
## Recommended Next Passes
|
## Recommended Next Passes
|
||||||
|
|
||||||
1. Split `krea_actions.hardcore_action_sentence` into action-family dispatch
|
1. Add metadata fields such as `action_family` / `position_family` to reduce
|
||||||
helpers, using `krea_cast.py` as the pattern for stable import aliases and
|
keyword guessing in hardcore filters and formatter dispatch.
|
||||||
smoke coverage.
|
|
||||||
2. Split `__init__.py` node classes by family after behavior is covered by smoke
|
2. Split `__init__.py` node classes by family after behavior is covered by smoke
|
||||||
checks.
|
checks.
|
||||||
3. Add metadata fields such as `action_family` / `position_family` to reduce
|
3. Add route-level smoke fixtures for representative Krea/SDXL/caption metadata
|
||||||
keyword guessing in hardcore filters and formatter dispatch.
|
rows.
|
||||||
|
|||||||
@@ -279,7 +279,8 @@ Edit targets:
|
|||||||
- Krea2 non-POV position anchors/arrangements: `krea_action_positions.py`.
|
- Krea2 non-POV position anchors/arrangements: `krea_action_positions.py`.
|
||||||
- Krea2 non-climax item/detail cleanup: `krea_action_details.py`.
|
- Krea2 non-climax item/detail cleanup: `krea_action_details.py`.
|
||||||
- Krea2 climax role/detail cleanup: `krea_action_climax.py`.
|
- Krea2 climax role/detail cleanup: `krea_action_climax.py`.
|
||||||
- Krea2 non-POV action rewrite: `krea_actions.py`.
|
- Krea2 non-POV action-family routing: `krea_action_dispatch.py`.
|
||||||
|
- Krea2 non-POV action sentence assembly: `krea_actions.py`.
|
||||||
- Krea2 POV position rewrite: `krea_pov_actions.py`.
|
- Krea2 POV position rewrite: `krea_pov_actions.py`.
|
||||||
|
|
||||||
### Composition
|
### Composition
|
||||||
@@ -474,7 +475,9 @@ What each part owns:
|
|||||||
details.
|
details.
|
||||||
- `krea_action_climax.py`: rewrites climax role graphs and dedupes aftermath
|
- `krea_action_climax.py`: rewrites climax role graphs and dedupes aftermath
|
||||||
detail/view clauses.
|
detail/view clauses.
|
||||||
- `krea_actions.py`: dispatches non-POV hardcore action sentence rewriting.
|
- `krea_action_dispatch.py`: normalizes non-POV role graphs, classifies action
|
||||||
|
families, and applies the matching detail cleanup.
|
||||||
|
- `krea_actions.py`: assembles the final non-POV hardcore action sentence.
|
||||||
- `krea_pov_actions.py`: rewrites POV variants with first-person geometry.
|
- `krea_pov_actions.py`: rewrites POV variants with first-person geometry.
|
||||||
|
|
||||||
Current broad hardcore families:
|
Current broad hardcore families:
|
||||||
@@ -565,6 +568,7 @@ Key Krea2 ownership:
|
|||||||
- Non-POV pose anchors and arrangements: `krea_action_positions.py`.
|
- Non-POV pose anchors and arrangements: `krea_action_positions.py`.
|
||||||
- Non-climax item/detail cleanup: `krea_action_details.py`.
|
- Non-climax item/detail cleanup: `krea_action_details.py`.
|
||||||
- Climax role/detail cleanup: `krea_action_climax.py`.
|
- Climax role/detail cleanup: `krea_action_climax.py`.
|
||||||
|
- Non-POV action-family routing: `krea_action_dispatch.py`.
|
||||||
- Non-POV hardcore action sentence: `krea_actions.hardcore_action_sentence`.
|
- Non-POV hardcore action sentence: `krea_actions.hardcore_action_sentence`.
|
||||||
- POV labels, filtering, and camera/composition support: `krea_pov.py`.
|
- POV labels, filtering, and camera/composition support: `krea_pov.py`.
|
||||||
- Detail clause splitting and density limiting: `krea_detail.py`.
|
- Detail clause splitting and density limiting: `krea_detail.py`.
|
||||||
@@ -756,7 +760,8 @@ Use these traces to narrow a problem in one pass.
|
|||||||
`krea_action_context.py` family predicates first, then
|
`krea_action_context.py` family predicates first, then
|
||||||
`krea_action_positions.py` pose anchors/arrangements,
|
`krea_action_positions.py` pose anchors/arrangements,
|
||||||
`krea_action_details.py` item/detail cleanup, `krea_action_climax.py`
|
`krea_action_details.py` item/detail cleanup, `krea_action_climax.py`
|
||||||
climax cleanup, and `krea_actions.py` action sentence dispatch.
|
climax cleanup, `krea_action_dispatch.py` family routing, and
|
||||||
|
`krea_actions.py` action sentence assembly.
|
||||||
|
|
||||||
### POV position is spatially wrong
|
### POV position is spatially wrong
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .krea_action_context import (
|
||||||
|
axis_values_text,
|
||||||
|
is_climax_text,
|
||||||
|
is_foreplay_text,
|
||||||
|
is_oral_text,
|
||||||
|
is_outercourse_text,
|
||||||
|
is_toy_assisted_double_text,
|
||||||
|
is_vaginal_penetration_text,
|
||||||
|
normalize_hardcore_detail_density,
|
||||||
|
)
|
||||||
|
from .krea_detail import limit_detail_for_density
|
||||||
|
from .krea_action_positions import hardcore_pose_anchor
|
||||||
|
from .krea_action_details import (
|
||||||
|
dedupe_anchor_detail,
|
||||||
|
dedupe_oral_detail,
|
||||||
|
dedupe_outercourse_detail,
|
||||||
|
dedupe_penetration_detail,
|
||||||
|
dedupe_toy_double_detail,
|
||||||
|
hardcore_item_detail,
|
||||||
|
sanitize_foreplay_detail,
|
||||||
|
)
|
||||||
|
from .krea_action_climax import climax_role_graph, dedupe_climax_detail
|
||||||
|
except ImportError: # Allows local smoke tests with `python -c`.
|
||||||
|
from krea_action_context import (
|
||||||
|
axis_values_text,
|
||||||
|
is_climax_text,
|
||||||
|
is_foreplay_text,
|
||||||
|
is_oral_text,
|
||||||
|
is_outercourse_text,
|
||||||
|
is_toy_assisted_double_text,
|
||||||
|
is_vaginal_penetration_text,
|
||||||
|
normalize_hardcore_detail_density,
|
||||||
|
)
|
||||||
|
from krea_detail import limit_detail_for_density
|
||||||
|
from krea_action_positions import hardcore_pose_anchor
|
||||||
|
from krea_action_details import (
|
||||||
|
dedupe_anchor_detail,
|
||||||
|
dedupe_oral_detail,
|
||||||
|
dedupe_outercourse_detail,
|
||||||
|
dedupe_penetration_detail,
|
||||||
|
dedupe_toy_double_detail,
|
||||||
|
hardcore_item_detail,
|
||||||
|
sanitize_foreplay_detail,
|
||||||
|
)
|
||||||
|
from krea_action_climax import climax_role_graph, dedupe_climax_detail
|
||||||
|
|
||||||
|
|
||||||
|
ACTION_CLIMAX = "climax"
|
||||||
|
ACTION_FOREPLAY = "foreplay"
|
||||||
|
ACTION_OUTERCOURSE = "outercourse"
|
||||||
|
ACTION_ORAL = "oral"
|
||||||
|
ACTION_PENETRATION = "penetration"
|
||||||
|
ACTION_TOY_DOUBLE = "toy_double"
|
||||||
|
ACTION_DEFAULT = "default"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class HardcoreActionParts:
|
||||||
|
family: str
|
||||||
|
role_graph: str
|
||||||
|
hard_item: str
|
||||||
|
detail: str
|
||||||
|
anchor: str
|
||||||
|
detail_density: str
|
||||||
|
|
||||||
|
|
||||||
|
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 normalize_hardcore_role_graph(role_graph: str) -> str:
|
||||||
|
role_graph = _clean(role_graph).rstrip(".")
|
||||||
|
replacements = (
|
||||||
|
(
|
||||||
|
r"\bthe man penetrates the woman while a toy adds a second point of contact\b",
|
||||||
|
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b",
|
||||||
|
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe man thrusts his penis into the woman\b",
|
||||||
|
"the man's penis thrusts into the woman",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe man penetrates the woman anally\b",
|
||||||
|
"the man's penis thrusts into the woman's ass",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe man thrusts his penis into the woman's ass\b",
|
||||||
|
"the man's penis thrusts into the woman's ass",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe man penetrates the woman\b",
|
||||||
|
"the man's penis thrusts into the woman",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe woman and the man are in mutual oral contact with mouth-to-genital contact visible\b",
|
||||||
|
"the woman has the man's penis in her mouth while the man uses his mouth on her pussy",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"\bthe woman gives oral to the man\b",
|
||||||
|
"the woman takes the man's penis in her mouth",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for pattern, replacement in replacements:
|
||||||
|
role_graph = re.sub(pattern, replacement, role_graph, flags=re.IGNORECASE)
|
||||||
|
return role_graph
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_toy_double_role_graph(role_graph: str) -> str:
|
||||||
|
return re.sub(
|
||||||
|
r"\s+while a toy adds (?:the|a) second penetration point\b",
|
||||||
|
" while a toy is positioned at the second penetration point",
|
||||||
|
role_graph,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def hardcore_action_family(
|
||||||
|
role_graph: str,
|
||||||
|
hard_item: str,
|
||||||
|
composition: str = "",
|
||||||
|
axis_values: Any = None,
|
||||||
|
*,
|
||||||
|
is_climax: bool | None = None,
|
||||||
|
) -> str:
|
||||||
|
axis_text = axis_values_text(axis_values)
|
||||||
|
if is_climax is None:
|
||||||
|
is_climax = is_climax_text(role_graph, hard_item, composition, axis_text)
|
||||||
|
if is_climax:
|
||||||
|
return ACTION_CLIMAX
|
||||||
|
if is_foreplay_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
return ACTION_FOREPLAY
|
||||||
|
if is_outercourse_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
return ACTION_OUTERCOURSE
|
||||||
|
if is_oral_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
return ACTION_ORAL
|
||||||
|
if is_vaginal_penetration_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
return ACTION_PENETRATION
|
||||||
|
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
return ACTION_TOY_DOUBLE
|
||||||
|
return ACTION_DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
def action_detail_for_family(
|
||||||
|
family: str,
|
||||||
|
detail: str,
|
||||||
|
role_graph: str,
|
||||||
|
hard_item: str,
|
||||||
|
composition: str = "",
|
||||||
|
axis_values: Any = None,
|
||||||
|
*,
|
||||||
|
anchor: str = "",
|
||||||
|
detail_density: str = "balanced",
|
||||||
|
) -> tuple[str, str]:
|
||||||
|
if family == ACTION_CLIMAX:
|
||||||
|
return "", dedupe_climax_detail(detail, role_graph, detail_density)
|
||||||
|
if family == ACTION_FOREPLAY:
|
||||||
|
detail = sanitize_foreplay_detail(detail, role_graph, composition)
|
||||||
|
return "", limit_detail_for_density(detail, detail_density, False)
|
||||||
|
if family == ACTION_OUTERCOURSE:
|
||||||
|
detail = dedupe_outercourse_detail(detail, role_graph, hard_item, axis_values)
|
||||||
|
return "", limit_detail_for_density(detail, detail_density, False)
|
||||||
|
if family == ACTION_ORAL and role_graph:
|
||||||
|
detail = dedupe_oral_detail(detail, role_graph, hard_item, axis_values)
|
||||||
|
return "", limit_detail_for_density(detail, detail_density, False)
|
||||||
|
if family == ACTION_PENETRATION and role_graph:
|
||||||
|
detail = dedupe_penetration_detail(detail, role_graph, hard_item, axis_values)
|
||||||
|
return "", limit_detail_for_density(detail, detail_density, False)
|
||||||
|
|
||||||
|
if anchor:
|
||||||
|
detail = dedupe_anchor_detail(detail, anchor)
|
||||||
|
if family == ACTION_TOY_DOUBLE:
|
||||||
|
detail = dedupe_toy_double_detail(detail)
|
||||||
|
return anchor, limit_detail_for_density(detail, detail_density, False)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_hardcore_action_parts(
|
||||||
|
role_graph: str,
|
||||||
|
hard_item: str,
|
||||||
|
composition: str = "",
|
||||||
|
axis_values: Any = None,
|
||||||
|
detail_density: str = "balanced",
|
||||||
|
) -> HardcoreActionParts:
|
||||||
|
detail_density = normalize_hardcore_detail_density(detail_density)
|
||||||
|
role_graph = normalize_hardcore_role_graph(role_graph)
|
||||||
|
hard_item = _clean(hard_item).rstrip(".")
|
||||||
|
axis_text = axis_values_text(axis_values)
|
||||||
|
is_climax = is_climax_text(role_graph, hard_item, composition, axis_text)
|
||||||
|
if is_climax:
|
||||||
|
role_graph = climax_role_graph(role_graph, hard_item, axis_values)
|
||||||
|
|
||||||
|
detail = hardcore_item_detail(hard_item)
|
||||||
|
anchor = hardcore_pose_anchor(role_graph, hard_item, composition, axis_values)
|
||||||
|
family = hardcore_action_family(role_graph, hard_item, composition, axis_values, is_climax=is_climax)
|
||||||
|
|
||||||
|
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_text):
|
||||||
|
role_graph = normalize_toy_double_role_graph(role_graph)
|
||||||
|
|
||||||
|
anchor, detail = action_detail_for_family(
|
||||||
|
family,
|
||||||
|
detail,
|
||||||
|
role_graph,
|
||||||
|
hard_item,
|
||||||
|
composition,
|
||||||
|
axis_values,
|
||||||
|
anchor=anchor,
|
||||||
|
detail_density=detail_density,
|
||||||
|
)
|
||||||
|
return HardcoreActionParts(
|
||||||
|
family=family,
|
||||||
|
role_graph=role_graph,
|
||||||
|
hard_item=hard_item,
|
||||||
|
detail=detail,
|
||||||
|
anchor=anchor,
|
||||||
|
detail_density=detail_density,
|
||||||
|
)
|
||||||
+7
-134
@@ -4,59 +4,17 @@ import re
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .krea_action_context import (
|
|
||||||
axis_values_text,
|
|
||||||
is_climax_text,
|
|
||||||
is_foreplay_text,
|
|
||||||
is_oral_text,
|
|
||||||
is_outercourse_text,
|
|
||||||
is_toy_assisted_double_text,
|
|
||||||
is_vaginal_penetration_text,
|
|
||||||
normalize_hardcore_detail_density,
|
|
||||||
)
|
|
||||||
from .krea_detail import limit_detail_for_density
|
|
||||||
from .krea_action_positions import (
|
from .krea_action_positions import (
|
||||||
arrangement_duplicates_role,
|
arrangement_duplicates_role,
|
||||||
hardcore_pose_anchor,
|
|
||||||
hardcore_pose_arrangement,
|
hardcore_pose_arrangement,
|
||||||
)
|
)
|
||||||
from .krea_action_details import (
|
from .krea_action_dispatch import resolve_hardcore_action_parts
|
||||||
dedupe_anchor_detail,
|
|
||||||
dedupe_oral_detail,
|
|
||||||
dedupe_outercourse_detail,
|
|
||||||
dedupe_penetration_detail,
|
|
||||||
dedupe_toy_double_detail,
|
|
||||||
hardcore_item_detail,
|
|
||||||
sanitize_foreplay_detail,
|
|
||||||
)
|
|
||||||
from .krea_action_climax import climax_role_graph, dedupe_climax_detail
|
|
||||||
except ImportError: # Allows local smoke tests with `python -c`.
|
except ImportError: # Allows local smoke tests with `python -c`.
|
||||||
from krea_action_context import (
|
|
||||||
axis_values_text,
|
|
||||||
is_climax_text,
|
|
||||||
is_foreplay_text,
|
|
||||||
is_oral_text,
|
|
||||||
is_outercourse_text,
|
|
||||||
is_toy_assisted_double_text,
|
|
||||||
is_vaginal_penetration_text,
|
|
||||||
normalize_hardcore_detail_density,
|
|
||||||
)
|
|
||||||
from krea_detail import limit_detail_for_density
|
|
||||||
from krea_action_positions import (
|
from krea_action_positions import (
|
||||||
arrangement_duplicates_role,
|
arrangement_duplicates_role,
|
||||||
hardcore_pose_anchor,
|
|
||||||
hardcore_pose_arrangement,
|
hardcore_pose_arrangement,
|
||||||
)
|
)
|
||||||
from krea_action_details import (
|
from krea_action_dispatch import resolve_hardcore_action_parts
|
||||||
dedupe_anchor_detail,
|
|
||||||
dedupe_oral_detail,
|
|
||||||
dedupe_outercourse_detail,
|
|
||||||
dedupe_penetration_detail,
|
|
||||||
dedupe_toy_double_detail,
|
|
||||||
hardcore_item_detail,
|
|
||||||
sanitize_foreplay_detail,
|
|
||||||
)
|
|
||||||
from krea_action_climax import climax_role_graph, dedupe_climax_detail
|
|
||||||
|
|
||||||
|
|
||||||
def _clean(value: Any) -> str:
|
def _clean(value: Any) -> str:
|
||||||
@@ -87,96 +45,11 @@ def hardcore_action_sentence(
|
|||||||
axis_values: Any = None,
|
axis_values: Any = None,
|
||||||
detail_density: str = "balanced",
|
detail_density: str = "balanced",
|
||||||
) -> str:
|
) -> str:
|
||||||
detail_density = normalize_hardcore_detail_density(detail_density)
|
parts = resolve_hardcore_action_parts(role_graph, hard_item, composition, axis_values, detail_density)
|
||||||
role_graph = _clean(role_graph).rstrip(".")
|
role_graph = parts.role_graph
|
||||||
hard_item = _clean(hard_item).rstrip(".")
|
hard_item = parts.hard_item
|
||||||
role_graph = re.sub(
|
detail = parts.detail
|
||||||
r"\bthe man penetrates the woman while a toy adds a second point of contact\b",
|
anchor = parts.anchor
|
||||||
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b",
|
|
||||||
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe man thrusts his penis into the woman\b",
|
|
||||||
"the man's penis thrusts into the woman",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe man penetrates the woman anally\b",
|
|
||||||
"the man's penis thrusts into the woman's ass",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe man thrusts his penis into the woman's ass\b",
|
|
||||||
"the man's penis thrusts into the woman's ass",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe man penetrates the woman\b",
|
|
||||||
"the man's penis thrusts into the woman",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe woman and the man are in mutual oral contact with mouth-to-genital contact visible\b",
|
|
||||||
"the woman has the man's penis in her mouth while the man uses his mouth on her pussy",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\bthe woman gives oral to the man\b",
|
|
||||||
"the woman takes the man's penis in her mouth",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
is_climax = is_climax_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
||||||
if is_climax:
|
|
||||||
role_graph = climax_role_graph(role_graph, hard_item, axis_values)
|
|
||||||
detail = hardcore_item_detail(hard_item)
|
|
||||||
anchor = hardcore_pose_anchor(role_graph, hard_item, composition, axis_values)
|
|
||||||
is_outercourse = is_outercourse_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
||||||
is_oral = is_oral_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
||||||
is_penetrative = is_vaginal_penetration_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
||||||
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
||||||
role_graph = re.sub(
|
|
||||||
r"\s+while a toy adds (?:the|a) second penetration point\b",
|
|
||||||
" while a toy is positioned at the second penetration point",
|
|
||||||
role_graph,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
if is_climax:
|
|
||||||
anchor = ""
|
|
||||||
detail = dedupe_climax_detail(detail, role_graph, detail_density)
|
|
||||||
elif is_foreplay_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
||||||
anchor = ""
|
|
||||||
detail = sanitize_foreplay_detail(detail, role_graph, composition)
|
|
||||||
detail = limit_detail_for_density(detail, detail_density, False)
|
|
||||||
elif is_outercourse:
|
|
||||||
anchor = ""
|
|
||||||
detail = dedupe_outercourse_detail(detail, role_graph, hard_item, axis_values)
|
|
||||||
detail = limit_detail_for_density(detail, detail_density, False)
|
|
||||||
elif is_oral and role_graph:
|
|
||||||
anchor = ""
|
|
||||||
detail = dedupe_oral_detail(detail, role_graph, hard_item, axis_values)
|
|
||||||
detail = limit_detail_for_density(detail, detail_density, False)
|
|
||||||
elif is_penetrative and role_graph:
|
|
||||||
anchor = ""
|
|
||||||
detail = dedupe_penetration_detail(detail, role_graph, hard_item, axis_values)
|
|
||||||
detail = limit_detail_for_density(detail, detail_density, False)
|
|
||||||
else:
|
|
||||||
detail = dedupe_anchor_detail(detail, anchor) if anchor else detail
|
|
||||||
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
||||||
detail = dedupe_toy_double_detail(detail)
|
|
||||||
detail = limit_detail_for_density(detail, detail_density, False)
|
|
||||||
arrangement = hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values)
|
arrangement = hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values)
|
||||||
anchor_phrase = _with_indefinite_article(anchor) if anchor else ""
|
anchor_phrase = _with_indefinite_article(anchor) if anchor else ""
|
||||||
if arrangement and anchor_phrase and not arrangement_duplicates_role(arrangement, role_graph):
|
if arrangement and anchor_phrase and not arrangement_duplicates_role(arrangement, role_graph):
|
||||||
|
|||||||
Reference in New Issue
Block a user