StackRoll¶
Namespace Nfty.Core.Imaging in assembly Nfty.Core.
Rolls a whole Recipe into the PreviewLayer stack StackPreview.Render() draws — one deterministic roll from a seed, in generation's own draw order.
The recipe's stack is decided by Ticketer.TryRoll(), not by a walk of its own. The draw order is the determinism contract — which layers are left out, which variant each present one gets, which color a Dynamic one rolls — and this used to be a second copy of it. The copy never learned about optional layers, so on any book using them it previewed stacks the seed would never cook. StackRoll.ForRecipe() now projects the recipe into a one-recipe MenuBook and rolls it exactly as Generator does, so the two cannot disagree. StackRoll.ForIngredient() still rolls a single loose layer itself: it has no recipe, so no absent chance and no rules, and it is not the roll of an asset.
What it does not reproduce. The roll includes the recipe draw every asset starts with, taken against a book that holds only this recipe — so for a CookBook whose ONLY recipe this is, previewing it at seed s is the first asset of generating that book at seed s. In a book of several recipes that draw would have picked among them, so the preview is then what this recipe's stack looks like when rolled — the same layers, in the same order, with colors drawn from the same distribution — and not any particular asset.
Methods¶
ForIngredient(LoadedIngredient, IRng, string?)¶
public static PreviewLayer ForIngredient(LoadedIngredient ingredient, IRng rng, string? variantId = null);
Rolls one layer: its variant by weight, then its color by kind.
Kind decides everything, exactly as it does at generation time. Dynamic rolls a color and so consumes RNG; Static carries a single fixed color and consumes none; Custom is composited as-is and gets no color at all. Keeping the same consumption pattern is what makes a preview of a stack reproducible against the same seed however the layer kinds are mixed.
Parameters
ingredient(LoadedIngredient) - The layer to roll.rng(IRng) - The run's RNG.variantId(string?) - Which variant to draw, when the caller has already decided — a reference layer placed beside a stack is chosen byStackPreview.PickVariant()or named outright, not rolled, so that varying the seed to explore the stack does not also keep changing the reference. Naming one consumes no variant draw: the choice was not the RNG's to make, so it must not cost the RNG a step and shift every color after it.
Returns. The layer, its variant, and the color spec to draw it in (null for Custom).
Exceptions
ArgumentNullException-ingredientorrngis null.InvalidOperationException- A colorized layer carries no usable colorization, whichValidatorreports as a problem rather than leaving to be discovered here.
ForRecipe(LoadedRecipe, IRng, int)¶
public static IReadOnlyList<PreviewLayer> ForRecipe(LoadedRecipe recipe, IRng rng, int maxAttempts = 10000);
Rolls a recipe's stack, bottom-to-top, honoring its absent chances and its incompatibility rules — through Ticketer.TryRoll(), the product's one roll of an asset.
Parameters
recipe(LoadedRecipe) - The recipe whose stack to roll.rng(IRng) - The run's RNG, fromStackRoll.RngFor(). Consumed exactly as generation consumes it: per attempt, one recipe draw, then per layer its absent-or-variant draw and, for a present Dynamic layer, its color draws.maxAttempts(int) - How many whole-stack rolls to try before giving up on the rules. Defaults to the same budget generation uses, so a recipe that previews is a recipe that cooks.
Returns. One layer per PRESENT layer, in paint order — ready for StackPreview.Render(). A layer the roll left out has no entry, exactly as it has no pixels in a cooked asset.
Exceptions
ArgumentNullException-recipeorrngis null.ArgumentOutOfRangeException-maxAttemptsis not positive.KeyNotFoundException- The layer order names an ingredient the recipe does not carry — a malformed recipe, whichValidator.ValidateRecipereports properly.InvalidOperationException- The recipe's rules rejected every attempt; or a stacked colorized layer carries no usable colorization (checked for every stacked layer before anything is rolled, including one the roll would have left out).
RngFor(string)¶
The RNG a seed string drives — SeedHash.ToUlong() into SplitMix64Rng, exactly as Generator seeds a run. Exposed so a caller rolling a recipe and then a loose extra layer draws both from one continuing stream instead of re-seeding (which would hand the two layers correlated colors), and so no front-end has to name the seeding scheme itself.
Parameters
seed(string) - The user's seed string.
Returns. A fresh RNG positioned at the start of that seed's sequence.
SpecFor(RolledColor, ColorModel)¶
A rolled hue and saturation written back out as the prefixed color spec a PreviewLayer carries.
This spelling is lossy, which is exactly why nothing renders through it. Every spec resolves via 8-bit RGB on its way back to (H,S), so a rolled hue that fell between two representable colors cannot be written down exactly. PreviewLayer therefore carries the unrounded RolledColor alongside this string and draws from that; the spec is what a person reads, pastes and re-runs. Rendering through the string instead put the preview about a quarter-degree of hue off the asset it claims to show — small, but this path promises what generation would draw, and a value generation never quantized must not be quantized on the way to being shown.
Value/lightness is still pinned at whichever end of its axis leaves the most room for hue and saturation (v=100 for HSV, l=50 for HSL — l=100 is pure white and would throw the saturation away entirely), so that the printed spec, if someone does re-run it, lands as close as the format allows.
Static and Custom layers are exact, not approximate: a Static layer's spec is passed through verbatim from its own colorization and a Custom layer has no color at all, so only a rolled color goes through this at all.
Parameters
color(RolledColor) - The rolled hue (degrees) and saturation (0..1).model(ColorModel) - The layer's color model, which decides both the prefix and the axis the third component sits on.
Returns. A spec such as hsv:210.5,64.2,100, formatted invariantly — it is printed, and re-parsed by ColorSpec, both of which a comma-decimal locale would break.