package detail // Classes is what the painted class asks of the detail passes, per cell, already blended. // // It exists because two classes can have the same uplift rate and the same erodibility - which is everything // the geology grid knows about them - and still be completely different ground. A desert and a wet lowland // are both "low, slowly rising"; what separates them is at two metres, in how much running water crosses // them, how sharp their ledges stay and how much of them is dune. // // **Four fields rather than a class index and a lookup table**, which is what this was. The index is the // right thing to carry - a class is a name, and a name is never interpolated - but the *numbers* it stands // for are quantities, and quantities interpolate. Kept as a lookup, a desert meeting a lowland changed from // seven metres of dune amplitude to two in the width of one cell, along a line the painter drew with a mouse, // and it read exactly as what it was: a boundary in a picture rather than a change in the ground. Blended, // the same boundary is a few hundred metres of one becoming the other, which is what the edge of a sand sea // looks like from the ground. // // The blending happens where the fields are built (see planet.blendedClasses), because that is where the // class raster and the tile's margin both are; by the time a pass reads one it is just a number per cell. // // Nil means every cell uses the pipeline's own numbers, which is what happens on a template whose legend // overrides nothing. type Classes struct { Droplets []float32 // per cell: droplets a cell spawns AmpLo []float32 // per cell: detail noise amplitude on flat ground AmpHi []float32 // per cell: and on steep ground Contrast []float32 // per cell: strata hardness contrast } // droplets, amp and contrast read a cell, falling back to the uniform value when there is no table. func (c *Classes) droplets(i int, def float64) float64 { if c == nil || c.Droplets == nil { return def } return float64(c.Droplets[i]) } func (c *Classes) amp(i int, defLo, defHi float64) (float64, float64) { if c == nil || c.AmpLo == nil { return defLo, defHi } return float64(c.AmpLo[i]), float64(c.AmpHi[i]) } func (c *Classes) contrast(i int, def float64) float64 { if c == nil || c.Contrast == nil { return def } return float64(c.Contrast[i]) }