コード例 #1
0
        public static List <Point2d> GetSettlementLocations(WaterTableField wtf, IField2d <float> wateriness,
                                                            float noiseScale = 0.1f, float noiseBlur = 3f, float noiseContribution = 0.15f)
        {
            IField2d <float> noise = new BlurredField(new MountainNoise(wtf.Width, wtf.Height, noiseScale), noiseBlur);
            Transformation2d <float, float, float> combination = new Transformation2d <float, float, float>(wateriness, noise, (w, n) => w + noiseContribution * n);

            List <Point2d> locations = new List <Point2d>();

            for (int y = 1; y < combination.Height - 1; y++)
            {
                for (int x = 1; x < combination.Width - 1; x++)
                {
                    if (wtf[y, x] > 0f &&
                        combination[y - 1, x - 1] < combination[y, x] &&
                        combination[y - 1, x + 0] < combination[y, x] &&
                        combination[y - 1, x + 1] < combination[y, x] &&
                        combination[y + 0, x + 1] < combination[y, x] &&
                        combination[y + 1, x + 1] < combination[y, x] &&
                        combination[y + 1, x + 0] < combination[y, x] &&
                        combination[y + 1, x - 1] < combination[y, x] &&
                        combination[y + 0, x - 1] < combination[y, x])
                    {
                        locations.Add(new Point2d(x, y));
                    }
                }
            }

            return(locations);
        }
コード例 #2
0
ファイル: WaterTableUtils.cs プロジェクト: septagon/demiurge
        public static IField2d <float> GetWaterinessMap(WaterTableField wtf, IField2d <float> rainfall, float waterPortability = 5f, float waterinessAttenuation = 20f)
        {
            // Generate "water flow" map using the rainfall map and the water table map, characterizing
            // how much water is in an area based on upstream.  Note that, in the simplistic case of
            // identical universal rainfall, this is just a scalar on depth; this whole shindig is
            // intended to support variable rainfall, as characterized by the rainfall map.
            Field2d <float> waterFlow = new Field2d <float>(rainfall);
            float           maxValue  = float.MinValue;

            foreach (TreeNode <Point2d> waterway in wtf.Waterways)
            {
                List <TreeNode <Point2d> > flattenedReversedRiverTree = new List <TreeNode <Point2d> >(waterway);
                flattenedReversedRiverTree.Reverse();

                foreach (var node in flattenedReversedRiverTree)
                {
                    if (node.parent != null)
                    {
                        Point2d cur = node.value;
                        Point2d par = node.parent.value;

                        waterFlow[par.y, par.x] += waterFlow[cur.y, cur.x];
                    }

                    maxValue = Math.Max(maxValue, waterFlow[node.value.y, node.value.x]);
                }
            }
            IField2d <float> waterinessUnblurred = new FunctionField <float>(waterFlow.Width, waterFlow.Height,
                                                                             (x, y) => 1f / (1f + (float)Math.Pow(Math.E, -waterinessAttenuation * waterFlow[y, x] / maxValue)));

            return(new BlurredField(waterinessUnblurred, waterPortability));
        }
コード例 #3
0
ファイル: MeterScaleMap.cs プロジェクト: septagon/demiurge
        public void OutputAsPreciseHeightmap(IField2d <float> heights, IField2d <float> rivers, string outputPath)
        {
            Bitmap bmp = new Bitmap(heights.Width, heights.Height);

            for (int y = 0; y < heights.Height; y++)
            {
                for (int x = 0; x < heights.Width; x++)
                {
                    float h = Math.Max(heights[y, x], 0f);
                    float r = Math.Max(rivers[y, x], 0f);

                    if (float.IsNaN(h))
                    {
                        Debug.Assert(false, "TODO: This should never have occurred.");
                        h = 0;
                    }

                    int m1k = (int)(h / 1000);
                    int m10 = (int)((h - m1k * 1000) / 10);
                    int m_1 = (int)((h - m1k * 1000 - m10 * 10) * 10);
                    int w   = float.IsPositiveInfinity(r) ? 255 : 128;

                    bmp.SetPixel(x, y, Color.FromArgb(w, m1k, m10, m_1));
                }
            }

            bmp.Save(outputPath);
        }
コード例 #4
0
        private static LandType Classify(IField2d <BrownianTree.Availability> field, int x, int y, int sensitivity, float shoreThreshold)
        {
            if (field[y, x] == BrownianTree.Availability.Available)
            {
                return(LandType.Land);
            }

            Point2d pos      = new Point2d(x, y);
            float   landNum  = 0f;
            float   totalNum = 0f;

            for (int j = (int)Math.Max(0, y - sensitivity); j < Math.Min(field.Height, y + sensitivity + 1); j++)
            {
                for (int i = (int)Math.Max(0, x - sensitivity); i < Math.Min(field.Width, x + sensitivity + 1); i++)
                {
                    if (Point2d.SqDist(new Point2d(i, j), pos) < sensitivity * sensitivity)
                    {
                        if (field[j, i] == BrownianTree.Availability.Available)
                        {
                            landNum++;
                        }
                        totalNum++;
                    }
                }
            }

            if (landNum / totalNum > shoreThreshold)
            {
                return(LandType.Shore);
            }

            return(LandType.Ocean);
        }
コード例 #5
0
ファイル: Erosion.cs プロジェクト: septagon/demiurge
        // TODO: Consider a more sophisticated gradient function, as it recommends in the paper.
        // But for now, I can't imagine subpixel accuracy is going to make a whole lot of difference.
        private static vFloat GradientAtPoint(this IField2d <float> field, vFloat point)
        {
            int   pX = (int)point[0];
            int   pY = (int)point[1];
            float x, y;

            if (pX % 2 == 1 || pX == field.Width - 1)
            {
                x = field[pY, pX - 1] - field[pY, pX];
            }
            else
            {
                x = field[pY, pX] - field[pY, pX + 1];
            }

            if (pY % 2 == 1 || pY == field.Height - 1)
            {
                y = field[pY - 1, pX] - field[pY, pX];
            }
            else
            {
                y = field[pY, pX] - field[pY + 1, pX];
            }

            return(new vFloat(x, y));
        }
コード例 #6
0
ファイル: MeterScaleMap.cs プロジェクト: septagon/demiurge
 public Args(IField2d <float> waters, IField2d <float> heights, IField2d <float> roughness, IField2d <float> rain)
 {
     this.watersDrawing    = waters;
     this.heightsDrawing   = heights;
     this.roughnessDrawing = roughness;
     this.rainDrawing      = rain;
 }
コード例 #7
0
ファイル: DrainageField.cs プロジェクト: septagon/demiurge
        public DrainageField(IField2d <HydrologicalField.LandType> hydroField, List <TreeNode <Point2d> > rivers)
            : base(new Transformation2d <HydrologicalField.LandType, Point2d>(hydroField, (x, y, landType) =>
        {
            switch (landType)
            {
            case HydrologicalField.LandType.Ocean:
                return(new Point2d(x, y));

            case HydrologicalField.LandType.Shore:
                return(default(Point2d));        // Handled in local constructor.

            case HydrologicalField.LandType.Land:
                Point2d?drain = Utils.Bfs(
                    new Point2d(x, y),
                    pt => x >= 0 && x < hydroField.Width && y >= 0 && y < hydroField.Height,
                    pt => hydroField[pt.y, pt.x] != HydrologicalField.LandType.Land);
                return(drain.HasValue ? drain.Value : new Point2d(x, y));

            default:
                throw new Exception();
            }
        }))
        {
            foreach (var river in rivers)
            {
                SetRiverDrainage(river);
            }
        }
コード例 #8
0
ファイル: WaterTableUtils.cs プロジェクト: septagon/demiurge
        public static WaterTableField GenerateWaters(Bitmap bmp, IField2d <float> baseField = null, WaterTableArgs args = null, Random random = null)
        {
            args = args ?? new WaterTableArgs()
            {
                seed = System.DateTime.UtcNow.Ticks
            };
            random    = random ?? new Random((int)args.seed);
            baseField = baseField ?? new Simplex2D(bmp.Width, bmp.Height, args.baseNoiseScale, args.seed);

            Field2d <float> field = new FieldFromBitmap(bmp);

            baseField = new NormalizedComposition2d <float>(baseField, new ScaleTransform(new Simplex2D(baseField.Width, baseField.Height, args.baseNoiseScale, args.seed), args.baseNoiseScalar));

            BrownianTree tree = BrownianTree.CreateFromOther(field, (x) => x > 0.5f ? BrownianTree.Availability.Available : BrownianTree.Availability.Unavailable, random);

            tree.RunDefaultTree();

            HydrologicalField hydro = new HydrologicalField(tree, args.hydroSensitivity, args.hydroShoreThreshold);
            WaterTableField   wtf   = new WaterTableField(baseField, hydro, args.wtfShore, args.wtfIt, args.wtfLen, args.wtfGrade, () =>
            {
                return((float)(args.wtfCarveAdd + random.NextDouble() * args.wtfCarveMul));
            });

            return(wtf);
        }
コード例 #9
0
ファイル: Streamline.cs プロジェクト: lymanzhang/SpatialSlur
 /// <summary>
 ///
 /// </summary>
 private static IEnumerable <Vector2d> IntegrateFromEuler(IField2d <Vector2d> field, Vector2d point, double stepSize)
 {
     while (true)
     {
         point += field.ValueAt(point) * stepSize;
         yield return(point);
     }
 }
コード例 #10
0
        public Transformation2d(IField2d <TFrom1> field1, IField2d <TFrom2> field2, Func <int, int, TFrom1, TFrom2, TTo> transformation)
        {
            System.Diagnostics.Debug.Assert(field1.Width == field2.Width && field1.Height == field2.Height, "Inputs to transformations must have equal dimensions.");

            this.field1         = field1;
            this.field2         = field2;
            this.transformation = transformation;
        }
コード例 #11
0
ファイル: Field2d.cs プロジェクト: septagon/demiurge
        public Field2d(IField2d <T> field)
        {
            this.values = new T[field.Height, field.Width];

            for (int x = 0, y = 0; y < this.Height; y += ++x / this.Width, x %= this.Width)
            {
                this.values[y, x] = field[y, x];
            }
        }
コード例 #12
0
ファイル: Streamline.cs プロジェクト: lymanzhang/SpatialSlur
        /// <summary>
        ///
        /// </summary>
        private static IEnumerable <Vector2d> IntegrateFromRK2(IField2d <Vector2d> field, Vector2d point, double stepSize)
        {
            while (true)
            {
                var v0 = field.ValueAt(point);
                var v1 = field.ValueAt(point + v0 * stepSize);

                point += (v0 + v1) * 0.5 * stepSize;
                yield return(point);
            }
        }
コード例 #13
0
ファイル: BrownianTree.cs プロジェクト: septagon/demiurge
        public static BrownianTree CreateFromOther <T>(IField2d <T> baseField, Func <T, Availability> conversion, Random random = null)
        {
            BrownianTree tree = new BrownianTree(baseField.Width, baseField.Height);

            for (int x = 0, y = 0; y < tree.Height; y += ++x / tree.Width, x %= tree.Width)
            {
                tree[y, x] = conversion(baseField[y, x]);
            }

            tree.random = random ?? new Random();

            return(tree);
        }
コード例 #14
0
ファイル: BlurredField.cs プロジェクト: septagon/demiurge
        public static void GaussBlur(IField2d <float> source, Field2d <float> target, float radius, int n = 3)
        {
            var             boxSizes = BoxRadiiForGaussBlur(radius, n);
            Field2d <float> buffer   = new Field2d <float>(source);

            RecursivelyBoxBlur(buffer, target, boxSizes);

            // If n was an even number, then the final blurring wound up
            // in the buffer; replicate it to the target.
            if (n % 2 == 0)
            {
                target.Replicate(buffer);
            }
        }
コード例 #15
0
        public bool TryAddChunk(int x, int y, IField2d <T> field)
        {
            if (GetChunkForPositionImpl(x, y).HasValue)
            {
                return(false);
            }
            else
            {
                Chunk chunk = new Chunk(x, y, field);
                this.chunks.Add(chunk);
                CacheChunk(chunk);

                return(true);
            }
        }
コード例 #16
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="field"></param>
        /// <param name="points"></param>
        /// <param name="stepSize"></param>
        /// <param name="stepCount"></param>
        /// <param name="mode"></param>
        private PolylineCurve[] SolveInstanceImpl(IField2d <Vec2d> field, List <Point3d> points, double stepSize, int stepCount)
        {
            var result = new PolylineCurve[points.Count];

            Parallel.For(0, points.Count, i =>
            {
                Vec3d p0 = points[i];
                var z    = p0.Z;

                var pts   = field.IntegrateFrom(p0, stepSize, _mode).Take(stepCount).Select(p => new Point3d(p.X, p.Y, z));
                result[i] = new PolylineCurve(pts);
            });

            return(result);
        }
コード例 #17
0
ファイル: MeterScaleMap.cs プロジェクト: septagon/demiurge
        public void OutputMapForRectangle(Rectangle sourceRect, Bitmap bmp, string dir = "C:\\Users\\Justin Murray\\Desktop\\terrain\\", string name = "submap")
        {
            // Hack that adds buffers, use to work around unidentified bugs and differences of behavior near edges.
            Rectangle rect = new Rectangle(
                sourceRect.Left - sourceRect.Width / 20,
                sourceRect.Top - sourceRect.Height / 20,
                sourceRect.Width * 11 / 10,
                sourceRect.Height * 11 / 10);

            int width  = (int)Math.Ceiling(bmp.Width * 1.1f);
            int height = (int)Math.Ceiling(bmp.Height * 1.1f);

            IField2d <float> waterTable = new BlurredField(new SubContinuum <float>(width, height, new ContinuousField(this.wtf), rect), 0.5f * width / rect.Width);
            IField2d <float> mountains  = new SubContinuum <float>(width, height, this.mountainNoise, rect);
            // TODO: hills?

            IField2d <float> roughness = new BlurredField(new SubContinuum <float>(width, height, new ContinuousField(this.args.roughnessDrawing), rect), 0.5f * width / rect.Width);

            IField2d <float> riverbeds = GetRiverFieldForRectangle(width, height, rect);
            IField2d <float> damping   = GetDampingFieldForRectangle(rect, riverbeds);

            IField2d <float> heightmap;
            {
                IField2d <float> dampedNoise       = new Transformation2d <float, float, float>(mountains, damping, (m, d) => Math.Max(1f - d, 0f) * m);
                IField2d <float> scaledDampedNoise = new Transformation2d <float, float, float>(dampedNoise, roughness, (n, r) => n * r * this.args.mountainHeightMaxInMeters);
                IField2d <float> groundHeight      = new Transformation2d <float, float, float>(waterTable, scaledDampedNoise, (w, m) => w + m);

                // TODO: Erode the groundHeight.

                heightmap = new Transformation2d <float, float, float>(groundHeight, riverbeds, Math.Min);

                //DEBUG
                heightmap = Erosion.DropletHydraulic(
                    heightmap,
                    2 * heightmap.Width * heightmap.Height,
                    100,
                    maxHeight: this.args.baseHeightMaxInMeters + this.args.mountainHeightMaxInMeters,
                    radius: (int)(this.args.erosionRadiusInMeters / (this.args.metersPerPixel * sourceRect.Width / heightmap.Width))
                    );
            }

            IField2d <float> riverField  = new SubField <float>(riverbeds, new Rectangle(bmp.Width / 20, bmp.Height / 20, bmp.Width, bmp.Height));
            IField2d <float> heightField = new SubField <float>(heightmap, new Rectangle(bmp.Width / 20, bmp.Height / 20, bmp.Width, bmp.Height));

            // TODO: DEBUG
            OutputAsOBJ(heightField, new Transformation2d <float, bool>(riverField, r => !float.IsPositiveInfinity(r)), sourceRect, bmp, dir, name);
            OutputAsPreciseHeightmap(heightField, riverField, dir + name + ".png");
        }
コード例 #18
0
ファイル: Erosion.cs プロジェクト: septagon/demiurge
        private static void DropSedimentFromKernel(Field2d <float> field, IField2d <float> kernel, int centerX, int centerY, float targetSediment)
        {
            for (int y = 0; y < kernel.Height; y++)
            {
                for (int x = 0; x < kernel.Width; x++)
                {
                    int cX = centerX + x - kernel.Width / 2;
                    int cY = centerY + y - kernel.Height / 2;

                    if (cX >= 0 && cY >= 0 && cX < field.Width && cY < field.Height)
                    {
                        field[cY, cX] += targetSediment * kernel[y, x];
                    }
                }
            }
        }
コード例 #19
0
ファイル: Streamline.cs プロジェクト: lymanzhang/SpatialSlur
        /// <summary>
        /// Returns a streamline through the given vector field starting at the given point.
        /// </summary>
        /// <param name="field"></param>
        /// <param name="point"></param>
        /// <param name="stepSize"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public static IEnumerable <Vector2d> IntegrateFrom(IField2d <Vector2d> field, Vector2d point, double stepSize, IntegrationMode mode = IntegrationMode.Euler)
        {
            switch (mode)
            {
            case IntegrationMode.Euler:
                return(IntegrateFromEuler(field, point, stepSize));

            case IntegrationMode.RK2:
                return(IntegrateFromRK2(field, point, stepSize));

            case IntegrationMode.RK4:
                return(IntegrateFromRK4(field, point, stepSize));
            }

            throw new NotSupportedException();
        }
コード例 #20
0
ファイル: Streamline.cs プロジェクト: lymanzhang/SpatialSlur
        /// <summary>
        ///
        /// </summary>
        private static IEnumerable <Vector2d> IntegrateFromRK4(IField2d <Vector2d> field, Vector2d point, double stepSize)
        {
            double dt2 = stepSize * 0.5;
            double dt6 = stepSize / 6.0;

            while (true)
            {
                var v0 = field.ValueAt(point);
                var v1 = field.ValueAt(point + v0 * dt2);
                var v2 = field.ValueAt(point + v1 * dt2);
                var v3 = field.ValueAt(point + v2 * stepSize);

                point += (v0 + 2.0 * v1 + 2.0 * v2 + v3) * dt6;
                yield return(point);
            }
        }
コード例 #21
0
ファイル: MeterScaleMap.cs プロジェクト: septagon/demiurge
        private IField2d <float> GetDampingFieldForRectangle(Rectangle rect, IField2d <float> riverbeds)
        {
            float metersPerPixel = this.args.metersPerPixel * rect.Width / riverbeds.Width;

            IField2d <float> upres   = new BlurredField(new SubContinuum <float>(riverbeds.Width, riverbeds.Height, this.distanceToWater, rect), riverbeds.Width / rect.Width);
            IField2d <float> manhats = new ScaleTransform(new ManhattanDistanceField(new Transformation2d <float, bool>(riverbeds, r => !float.IsPositiveInfinity(r))), metersPerPixel);

            IField2d <float> dists = new BlurredField(new Transformation2d <float, float, float>(upres, manhats, Math.Min), 200f / metersPerPixel);

            return(new Transformation2d(dists, d =>
            {
                float valleyFactor = 1f - d / this.args.valleyRadiusInMeters;
                float canyonFactor = 1f - (float)Math.Pow(d / this.args.canyonRadiusInMeters, 2f);
                return Math.Max(Math.Max(valleyFactor * this.args.valleyStrength, canyonFactor * this.args.canyonStrength), 0f);
            }));
        }
コード例 #22
0
ファイル: SplineTree.cs プロジェクト: septagon/demiurge
        public SplineTree(TreeNode <Point2d> tree, IField2d <float> altitudes, Random random, int minSizeForFork = 3, float alpha = 0.5f)
        {
            this.splines   = new List <CenCatRomSpline>();
            this.altitudes = altitudes;
            this.random    = random;

            this.minSizeForFork = minSizeForFork;
            this.alpha          = alpha;

            var lastList = BuildSplinesRecursively(tree, null);

            lastList.Add(GetParentPoint(lastList));
            this.splines.Add(new CenCatRomSpline(lastList.ToArray(), this.alpha));

            this.altitudes = null;
            this.random    = null;
        }
コード例 #23
0
        public ManhattanDistanceField(IField2d <bool> isTarget)
            : base(new Transformation2d <bool, float>(isTarget, b => b ? 0f : float.PositiveInfinity))
        {
            float lastWater;

            for (int y = 0; y < this.Height; y++)
            {
                lastWater = float.PositiveInfinity;
                for (int x = 0; x < this.Width; x++)
                {
                    lastWater  = Math.Min(lastWater + 1f, this[y, x]);
                    this[y, x] = lastWater;
                }
            }

            for (int y = 0; y < this.Height; y++)
            {
                lastWater = float.PositiveInfinity;
                for (int x = this.Width - 1; x >= 0; x--)
                {
                    lastWater  = Math.Min(lastWater + 1f, this[y, x]);
                    this[y, x] = lastWater;
                }
            }

            for (int x = 0; x < this.Width; x++)
            {
                lastWater = float.PositiveInfinity;
                for (int y = 0; y < this.Height; y++)
                {
                    lastWater  = Math.Min(lastWater + 1f, this[y, x]);
                    this[y, x] = lastWater;
                }
            }

            for (int x = 0; x < this.Width; x++)
            {
                lastWater = float.PositiveInfinity;
                for (int y = this.Height - 1; y >= 0; y--)
                {
                    lastWater  = Math.Min(lastWater + 1f, this[y, x]);
                    this[y, x] = lastWater;
                }
            }
        }
コード例 #24
0
ファイル: Erosion.cs プロジェクト: septagon/demiurge
        // TODO: Don't allow the field to erode below zero.
        // TODO: Here, at last, is the source of the insane bug.  The way this bug works is easiest to understand
        // if one envisions a kernel of small radius and a "trough" consisting of two elevated walls and a lower
        // middle.  The nature of a trough is that a particle can become "trapped" in one, rolling back and forth
        // as each wall turns the particle back.  If a trough is narrow enough that a particle dropping sediment in
        // the middle will also drop sediment on both walls by virtue of its kernel, then oscillation in the trough
        // can cause the particle to "build towers."  This happens because when the particle is picking up sediment--
        // i.e., when it's beginning to go down-hill--its kernel covers part of the trough and a little bit outside;
        // however, when the particle is depositing sediment--i.e., when it's beginning to go uphill--its kernel is
        // entirely over the trough.  By repeated action, this allows the particle to "dig" sediment from just outside
        // the walls of its trough and bring that sediment back into the trough, creating the extremely
        // characteristic "bars" of high and low elevation in extremely close proximity.  The solution to this,
        // presumably, is to prevent this "digging" behavior, presumably by taking sediment in a more cautious
        // manner that won't allow erosion computed from a high place to induce the removal of sediment from a low
        // place.
        private static void PickUpSedimentFromKernel(Field2d <float> field, IField2d <float> kernel, int centerX, int centerY, float targetSediment)
        {
            if (targetSediment == 0)
            {
                return;
            }

            float targetMin = field[centerY, centerX] - kernel[kernel.Height / 2, kernel.Width / 2] * targetSediment;

            float collected = 0f;

            for (int y = 0; y < kernel.Height; y++)
            {
                for (int x = 0; x < kernel.Width; x++)
                {
                    int cX = centerX + x - kernel.Width / 2;
                    int cY = centerY + y - kernel.Height / 2;

                    if (cX >= 0 && cY >= 0 && cX < field.Width && cY < field.Height && field[cY, cX] >= targetMin)
                    {
                        collected += targetSediment * kernel[y, x];
                    }
                }
            }

            float scalar = targetSediment / collected;

            for (int y = 0; y < kernel.Height; y++)
            {
                for (int x = 0; x < kernel.Width; x++)
                {
                    int cX = centerX + x - kernel.Width / 2;
                    int cY = centerY + y - kernel.Height / 2;

                    if (cX >= 0 && cY >= 0 && cX < field.Width && cY < field.Height && field[cY, cX] >= targetMin)
                    {
                        field[cY, cX] -= targetSediment * kernel[y, x] * scalar;
                    }
                }
            }
        }
コード例 #25
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="field"></param>
        /// <param name="other"></param>
        /// <param name="converter"></param>
        /// <param name="parallel"></param>
        public static void Sample <T, U>(this IDiscreteField2d <T> field, IField2d <U> other, Func <U, T> converter, bool parallel = false)
        {
            if (parallel)
            {
                Parallel.ForEach(Partitioner.Create(0, field.Count), range => Body(range.Item1, range.Item2));
            }
            else
            {
                Body(0, field.Count);
            }

            void Body(int from, int to)
            {
                var vals = field.Values;

                for (int i = from; i < to; i++)
                {
                    vals[i] = converter(other.ValueAt(field.CoordinateAt(i)));
                }
            }
        }
コード例 #26
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="field"></param>
        /// <param name="other"></param>
        /// <param name="converter"></param>
        /// <param name="parallel"></param>
        public static void Sample <T, U>(this IDiscreteField2d <T> field, IField2d <U> other, Func <U, T> converter, bool parallel = false)
        {
            var vals = field.Values;

            Action <Tuple <int, int> > body = range =>
            {
                for (int i = range.Item1; i < range.Item2; i++)
                {
                    vals[i] = converter(other.ValueAt(field.CoordinateAt(i)));
                }
            };

            if (parallel)
            {
                Parallel.ForEach(Partitioner.Create(0, field.Count), body);
            }
            else
            {
                body(Tuple.Create(0, field.Count));
            }
        }
コード例 #27
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field"></param>
        /// <param name="other"></param>
        /// <param name="parallel"></param>
        public static void Sample <T>(this ISampledField2d <T> field, IField2d <T> other, bool parallel = false)
        {
            if (parallel)
            {
                Parallel.ForEach(Partitioner.Create(0, field.Count), range => Body(range.Item1, range.Item2));
            }
            else
            {
                Body(0, field.Count);
            }

            void Body(int from, int to)
            {
                var vals = field.Values;

                for (int i = from; i < to; i++)
                {
                    vals[i] = other.ValueAt(field.PointAt(i));
                }
            }
        }
コード例 #28
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="field"></param>
 /// <param name="point"></param>
 /// <param name="stepSize"></param>
 /// <param name="mode"></param>
 /// <returns></returns>
 public static IEnumerable <Vec2d> IntegrateFrom(this IField2d <Vec2d> field, Vec2d point, double stepSize, IntegrationMode mode = IntegrationMode.Euler)
 {
     return(SimulationUtil.IntegrateFrom(field, point, stepSize, mode));
 }
コード例 #29
0
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="transform"></param>
 /// <param name="other"></param>
 /// <returns></returns>
 public static IField2d <T> CreateTransformed <T>(IField2d <T> other, Transform2d transform)
 {
     transform.Invert();
     return(Create(p => other.ValueAt(transform.Apply(p))));
 }
コード例 #30
0
ファイル: Erosion.cs プロジェクト: septagon/demiurge
        // Based on the approach by Hans Theobald Beyer, "Implementation of a method for hydraulic erosion," 2015
        public static Field2d <float> DropletHydraulic(IField2d <float> inputHeightmap, int numDroplets, int iterationsPerDrop, float minSlope = 0f, float maxHeight = 1f, int radius = 0)
        {
            Random random     = new Random();
            float  pFriction  = 0.3f;
            float  pCapacity  = 1f;
            float  pErode     = 0.3f;
            float  pDeposit   = 0.3f;
            float  pGravity   = 0.8f / maxHeight;
            float  pEvaporate = 0.01f;

            const int STARTING_DIRECTION_GRANULARITY = 32;

            var kernel = GetKernel(radius);

            Field2d <float> heightmap = new Field2d <float>(inputHeightmap);

            for (int idx = 0; idx < numDroplets; idx++)
            {
                Droplet droplet = new Droplet()
                {
                    Position  = new vFloat(random.Next(heightmap.Width), random.Next(heightmap.Height)),
                    Direction = new vFloat(random.Next(STARTING_DIRECTION_GRANULARITY) - STARTING_DIRECTION_GRANULARITY / 2,
                                           random.Next(STARTING_DIRECTION_GRANULARITY) - STARTING_DIRECTION_GRANULARITY / 2).norm(),
                    Speed    = 0,
                    Water    = 1,
                    Sediment = 0,
                };

                for (int iteration = 0; iteration < iterationsPerDrop; iteration++)
                {
                    (int x, int y)oldPos = ((int)droplet.Position[0], (int)droplet.Position[1]);
                    float oldHeight = heightmap[oldPos.y, oldPos.x];

                    var gradient = heightmap.GradientAtPoint(droplet.Position);
                    droplet.Direction = (droplet.Direction + gradient) * (1 - pFriction);

                    if (droplet.Direction.magSq() > 0)
                    {
                        droplet.Position += droplet.Direction.norm();
                    }

                    if (droplet.Position[0] < 0f || droplet.Position[1] < 0f ||
                        droplet.Position[0] >= heightmap.Width || droplet.Position[1] >= heightmap.Height)
                    {
                        break;
                    }

                    (int x, int y)newPos = ((int)droplet.Position[0], (int)droplet.Position[1]);
                    float newHeight = heightmap[newPos.y, newPos.x];

                    if (newHeight > oldHeight)
                    {
                        float droppedSediment = Math.Min(newHeight - oldHeight, droplet.Sediment);

                        DropSedimentFromKernel(heightmap, kernel, oldPos.x, oldPos.y, droppedSediment);
                        droplet.Sediment -= droppedSediment;
                    }
                    else if (newHeight < oldHeight)
                    {
                        float capacity = Math.Max(oldHeight - newHeight, minSlope) * droplet.Speed * droplet.Water * pCapacity;

                        if (droplet.Sediment > capacity)
                        {
                            float droppedSediment = (droplet.Sediment - capacity) * pDeposit;

                            DropSedimentFromKernel(heightmap, kernel, oldPos.x, oldPos.y, droppedSediment);
                            droplet.Sediment -= droppedSediment;
                        }
                        else
                        {
                            float pickedUpSediment = Math.Min((capacity - droplet.Sediment) * pErode, oldHeight - newHeight);

                            PickUpSedimentFromKernel(heightmap, kernel, oldPos.x, oldPos.y, pickedUpSediment);
                            droplet.Sediment += pickedUpSediment;
                        }
                    }

                    // This is from the paper, but it's super weird.  So, the drops will pick up speed even if they go uphill?
                    // I think speed is the wrong term for this variable.  In fact, this whole concept is very magical.  Speed should
                    // be determined by the magnitude of the velocity, not by some random accumulator.  On the other hand, I tried
                    // that, and this works way better.  So...
                    droplet.Speed = (float)Math.Sqrt(droplet.Speed * droplet.Speed + Math.Abs(newHeight - oldHeight) * pGravity);
                    droplet.Water = droplet.Water * (1 - pEvaporate);
                }
            }

            return(heightmap);
        }