layers/region.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief `RegionLayer`, another layer applied only inside a rectangle or circle. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/layers/boulders/boulder_placer.h" | ||
| 6 | #include "terraingen/layers/craters/crater_placer.h" | ||
| 7 | #include "terraingen/layers/fade.h" | ||
| 8 | #include "terraingen/layers/layer.h" | ||
| 9 | #include "terraingen/raster/cell_loops.h" | ||
| 10 | #include "terraingen/raster/grid.h" | ||
| 11 | #include "terraingen/util/geometry.h" | ||
| 12 | |||
| 13 | #include <memory> | ||
| 14 | #include <optional> | ||
| 15 | |||
| 16 | namespace terraingen { | ||
| 17 | |||
| 18 | /// An area of the surface: a rectangle or a circle. | ||
| 19 | struct RegionShape { | ||
| 20 | /// Which shape the region is. | ||
| 21 | enum class Kind { Rectangle, Circle }; | ||
| 22 | |||
| 23 | Kind kind = Kind::Rectangle; ///< Which of the fields below apply. | ||
| 24 | Bounds2d rectangle{}; ///< For `Rectangle`; a longitude-latitude box on a sphere. | ||
| 25 | Vec2d centre{}; ///< For `Circle`; longitude and latitude on a sphere. | ||
| 26 | double radius = 0.0; ///< For `Circle`, in metres. | ||
| 27 | |||
| 28 | /// A rectangular region of `bounds` (surface coordinates). | ||
| 29 | 26 | static RegionShape Rectangle(const Bounds2d& bounds) { | |
| 30 | 26 | return {Kind::Rectangle, bounds, {}, 0.0}; | |
| 31 | } | ||
| 32 | /// A circular region around `centre` (surface coordinates). | ||
| 33 | /// @param radius In metres. | ||
| 34 | 25 | static RegionShape Circle(Vec2d centre, double radius) { | |
| 35 | 25 | return {Kind::Circle, {}, centre, radius}; | |
| 36 | } | ||
| 37 | |||
| 38 | /// How far surface (x, y) lies outside the shape, in metres; 0 inside. | ||
| 39 | /// | ||
| 40 | /// On a sphere a rectangle's distance is measured in the point's local frame, east shortened | ||
| 41 | /// by the cosine of latitude, an approximation for feathers small against the body. | ||
| 42 | double DistanceOutside(double x, double y, const Surface& surface = {}) const; | ||
| 43 | |||
| 44 | /// The smallest box holding the shape, in `surface`'s coordinates. | ||
| 45 | Bounds2d Bounds(const Surface& surface = {}) const; | ||
| 46 | |||
| 47 | bool operator==(const RegionShape&) const = default; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Checks a region shape. | ||
| 51 | /// @throws std::invalid_argument for a shape that is not finite, a rectangle with a min above its | ||
| 52 | /// max, or a circle with a negative radius. | ||
| 53 | void ValidateRegionShape(const RegionShape& shape); | ||
| 54 | |||
| 55 | /// Another layer applied only inside a region, blended back to the unchanged height over | ||
| 56 | /// `feather` metres outside it. | ||
| 57 | /// | ||
| 58 | /// The inner layer sees the same context and, on grids, is applied only to the cells near the | ||
| 59 | /// region. Of its features, those centred within the region or its feather (`Holds`) belong to it. | ||
| 60 | class RegionLayer final : public Layer, public CraterPlacer, public BoulderPlacer { | ||
| 61 | public: | ||
| 62 | /// @param feather Width of the blend outside the region, in metres; non-negative. | ||
| 63 | /// @throws std::invalid_argument for a null `inner`, an invalid `shape`, or a negative or | ||
| 64 | /// non-finite `feather`. | ||
| 65 | RegionLayer(std::shared_ptr<const Layer> inner, RegionShape shape, double feather = 0.0, | ||
| 66 | BlendShape blend = BlendShape::Smoothstep, Surface surface = {}); | ||
| 67 | |||
| 68 | /// The inner layer's weight at surface (x, y): 1 inside the shape, 0 from `feather` outside | ||
| 69 | /// it, the blend between. | ||
| 70 | double Weight(double x, double y) const; | ||
| 71 | |||
| 72 | /// The confined layer. | ||
| 73 | 2 | const Layer& Inner() const { | |
| 74 | 2 | return *inner_; | |
| 75 | } | ||
| 76 | |||
| 77 | /// Whether a feature centred at (x, y) belongs to the region: within it or its feather. | ||
| 78 | bool Holds(double x, double y) const; | ||
| 79 | |||
| 80 | /// The part of `grid` the inner layer is applied to, the cells near the region; nothing when no | ||
| 81 | /// cell is. | ||
| 82 | std::optional<RasterGrid> InnerGrid(const RasterGrid& grid) const; | ||
| 83 | |||
| 84 | /// The inner layer's craters over its part of `grid` that the region `Holds`; none when the | ||
| 85 | /// inner layer places no craters. | ||
| 86 | CraterFeatures CratersOver(const RasterGrid& grid, double spacing) const override; | ||
| 87 | |||
| 88 | /// The inner layer's boulders over its part of `grid` that the region `Holds`; none when the | ||
| 89 | /// inner layer places no boulders. | ||
| 90 | BoulderFeatures BouldersOver(const RasterGrid& grid, double spacing, | ||
| 91 | const Ground* ground) const override; | ||
| 92 | |||
| 93 | protected: | ||
| 94 | /// Applies the inner layer to the samples near the region and blends it in by `Weight`. | ||
| 95 | void ApplySamples(SampleBlock& samples, const LayerContext& context) const override; | ||
| 96 | |||
| 97 | private: | ||
| 98 | void ApplyToGrid(Raster<float>& heights, const LayerContext& context) const; | ||
| 99 | void ApplyToPoints(SampleBlock& samples, const LayerContext& context) const; | ||
| 100 | PixelWindow InnerWindow(const RasterGrid& grid) const; | ||
| 101 | |||
| 102 | std::shared_ptr<const Layer> inner_; | ||
| 103 | RegionShape shape_; | ||
| 104 | double feather_; | ||
| 105 | BlendShape blend_; | ||
| 106 | Surface surface_; | ||
| 107 | }; | ||
| 108 | |||
| 109 | } // namespace terraingen | ||
| 110 |