diff --git a/Config.cs b/Config.cs index 8d11b2e..4bfeee5 100644 --- a/Config.cs +++ b/Config.cs @@ -22,13 +22,15 @@ class ConfigData [DataMember] public int iterationCount; [DataMember] - public double sampleWidthScale; + public double scanRadiusScale; [DataMember] - public double sampleLengthScale; + public double angleRange; + [DataMember] + public double angleStep; [DataMember] public double shoreContrast; [DataMember] - public double maxDifference; + public double advanceRate; [DataMember] public bool debug; @@ -49,11 +51,12 @@ static Config() lon1 = 52.2209239, lat2 = 64.9032122, lon2 = 52.2213061, - iterationCount = 500, - sampleWidthScale = 1.7, - sampleLengthScale = 0.6, + iterationCount = 300, + scanRadiusScale = 2.0, + angleRange = 90.0, + angleStep = 4.0, shoreContrast = 10.0, - maxDifference = 14.0, + advanceRate = 0.5, debug = false }; if (File.Exists(fileName)) diff --git a/Tracer.cs b/Tracer.cs index a76aa11..c1c2c5a 100644 --- a/Tracer.cs +++ b/Tracer.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; @@ -7,9 +8,9 @@ namespace RiverTrace { class Tracer { - private int sampleWidth; - private int sampleLength; private double riverWidthM; + private double scanRadius; + private Color waterColor; private TileMap tileMap; void WriteOsm(List result) @@ -53,6 +54,14 @@ void CalcSampleDimensions(Vector startPoint, Vector direction) for (int i = 0; i < pickCount; i++) { Color refColor = tileMap.GetPixel(pickPoint1.X, pickPoint1.Y); + + if (i == 0) + waterColor = refColor; + else + waterColor = new Color((byte)((waterColor.R + refColor.R) / 2), + (byte)((waterColor.G + refColor.G) / 2), + (byte)((waterColor.B + refColor.B) / 2)); + for (int j = 0; j < 2; j++) { Vector pickPoint2 = pickPoint1; @@ -71,96 +80,13 @@ void CalcSampleDimensions(Vector startPoint, Vector direction) riverHalfWidth[0] /= pickCount; riverHalfWidth[1] /= pickCount; double riverWidthPx = riverHalfWidth[0] + riverHalfWidth[1] + 1.0; - sampleWidth = Math.Max((int)Math.Ceiling(riverWidthPx * Config.Data.sampleWidthScale), 5); - sampleLength = Math.Max((int)Math.Ceiling(riverWidthPx * Config.Data.sampleLengthScale), 3); + scanRadius = riverWidthPx * Config.Data.scanRadiusScale; Vector wp1 = startPoint + sideDirs[0] * (riverWidthPx / 2.0); Vector wp2 = startPoint + sideDirs[1] * (riverWidthPx / 2.0); riverWidthM = Projection.Distance(wp1, wp2, Config.Data.zoom); } - SimpleBitmap GetSample(Vector origin, Vector direction) - { - SimpleBitmap sample = new SimpleBitmap(sampleWidth, sampleLength); - - Vector dv = direction.Rotated(-90); - for (int i = 0; i < sampleWidth; i++) - for (int j = 0; j < sampleLength; j++) - { - int xs = sampleWidth / 2 - i; - int ys = j; - double x = xs * dv.X - ys * dv.Y + origin.X; - double y = xs * dv.Y + ys * dv.X + origin.Y; - sample.SetPixel(i, j, tileMap.GetPixel(x, y)); - } - - return sample; - } - - double GetSampleDifference(SimpleBitmap s1, SimpleBitmap s2) - { - double totalDelta = 0.0; - for (int i = 0; i < sampleWidth; i++) - for (int j = 0; j < sampleLength; j++) - { - Color c1 = s1.GetPixel(i, j); - Color c2 = s2.GetPixel(i, j); - double pixelDelta = c1.DifferenceTo(c2); - totalDelta += pixelDelta; - } - return totalDelta / (sampleWidth * sampleLength); - } - - void GetBestAngle(Vector lastPoint, Vector lastVector, SimpleBitmap avgSample, - double minAngle, double maxAngle, double step, out SimpleBitmap bestSample, - out double bestDiff, out Vector bestVector, out double bestAngle) - { - SimpleBitmap localBestSample = null; - double localBestDiff = double.MaxValue; - Vector localBestVector = null; - double localBestAngle = 0.0; - var lockObj = new object(); - - int stepCount = (int)Math.Round((maxAngle - minAngle) / step) + 1; - Parallel.For(0, stepCount, i => - { - double angle = minAngle + i * step; - Vector rv = lastVector.Rotated(angle); - SimpleBitmap candidateSample = GetSample(lastPoint, rv); - double diff = GetSampleDifference(avgSample, candidateSample); - lock (lockObj) - { - localBestDiff = Math.Min(localBestDiff, diff); - if (diff == localBestDiff) - { - localBestSample = candidateSample; - localBestVector = rv; - localBestAngle = angle; - } - } - }); - bestVector = localBestVector; - bestSample = localBestSample; - bestDiff = localBestDiff; - bestAngle = localBestAngle; - } - - SimpleBitmap GetAvgSample(SimpleBitmap s1, SimpleBitmap s2) - { - SimpleBitmap sample = new SimpleBitmap(sampleWidth, sampleLength); - for (int i = 0; i < sampleWidth; i++) - for (int j = 0; j < sampleLength; j++) - { - Color c1 = s1.GetPixel(i, j); - Color c2 = s2.GetPixel(i, j); - sample.SetPixel(i, j, - (byte)((c1.R + c2.R) / 2), - (byte)((c1.G + c2.G) / 2), - (byte)((c1.B + c2.B) / 2)); - } - return sample; - } - public Tracer() { Stopwatch sw = new Stopwatch(); @@ -181,35 +107,76 @@ public Tracer() CalcSampleDimensions(p1, lastDirection); + int pixelRange = (int)(scanRadius * 2) + 1; + List samples = new List(); - SimpleBitmap firstSample = GetSample(p1, lastDirection); - SimpleBitmap avgSample = firstSample; - Vector lastPoint = p1 + lastDirection * sampleLength; - way.Add(lastPoint); - samples.Add(firstSample); - - double totalDiff = 0.0; - for (int i = 0; i < Config.Data.iterationCount; i++) + + Vector lastPoint = p1; + for (int z = 0; z < Config.Data.iterationCount; z++) { - SimpleBitmap bestSample; - double bestDiff; - Vector bestVector; - double bestAngle; - GetBestAngle(lastPoint, lastDirection, avgSample, -50.0, 50.0, 5.0, - out bestSample, out bestDiff, out bestVector, out bestAngle); - GetBestAngle(lastPoint, lastDirection, avgSample, bestAngle - 4.0, bestAngle + 4.0, 1.0, - out bestSample, out bestDiff, out bestVector, out bestAngle); - - if (bestDiff > Config.Data.maxDifference) - break; + SimpleBitmap sb = null; + if (Config.Data.debug) + sb = new SimpleBitmap(pixelRange * 2, pixelRange); + var angles = new Dictionary(); + for (int i = 0; i < pixelRange; i++) + { + for (int j = 0; j < pixelRange; j++) + { + int x = (int)(lastPoint.X + i - scanRadius); + int y = (int)(lastPoint.Y + j - scanRadius); + Vector pixelVector = new Vector(x, y) - lastPoint; + double pixelVectorLen = pixelVector.Length(); + + if (pixelVector.Length() < 1.0) + continue; + if (pixelVectorLen > scanRadius) + continue; + + double angle = lastDirection.AngleTo(pixelVector); + if (angle < -Config.Data.angleRange) + continue; + if (angle > Config.Data.angleRange) + continue; + + Color c = tileMap.GetPixel(x, y); + if (Config.Data.debug) + sb.SetPixel(i, j, c); + + double invDiff = Math.Max(1.0 - + waterColor.DifferenceTo(c) / Config.Data.shoreContrast, 0.0); + + if (Config.Data.debug) + { + byte diffColor = (byte)Math.Round(invDiff * 255.0); + sb.SetPixel(i + pixelRange, j, new Color(diffColor, diffColor, diffColor)); + } + + if (invDiff != 0.0) + { + if (!angles.ContainsKey(angle)) + angles[angle] = 0.0; + angles[angle] += invDiff; + } + } + } - totalDiff += bestDiff; + samples.Add(sb); - samples.Add(bestSample); - avgSample = GetAvgSample(firstSample, bestSample); + if (angles.Count == 0) + break; - lastDirection = bestVector; - lastPoint = lastPoint + lastDirection * sampleLength; + double[] anglesGrid = new double[(int)Math.Round( + Config.Data.angleRange * 2 / Config.Data.angleStep + 1)]; + foreach (var kv in angles) + { + anglesGrid[(int)Math.Round((kv.Key + + Config.Data.angleRange) / Config.Data.angleStep)] += kv.Value; + } + + double bestAngle = (anglesGrid.ToList().IndexOf(anglesGrid.Max()) * + Config.Data.angleStep - Config.Data.angleRange); + lastDirection = lastDirection.Rotated(bestAngle); + lastPoint += lastDirection * scanRadius * Config.Data.advanceRate; way.Add(lastPoint); } sw.Stop(); @@ -220,15 +187,13 @@ public Tracer() if (Config.Data.debug) { SimpleBitmap sampleChain = new SimpleBitmap( - sampleWidth, sampleLength * samples.Count); + samples[0].Width, samples[0].Height * samples.Count); for (int i = 0; i < samples.Count; i++) - samples[i].CopyTo(sampleChain, i * sampleLength); + samples[i].CopyTo(sampleChain, i * samples[0].Height); sampleChain.WriteTo("sample_chain.png"); - for (int i = 0; i < 50; i++) Console.WriteLine(); Console.WriteLine(""); } diff --git a/Vector.cs b/Vector.cs index 774a7ab..e1e22a1 100644 --- a/Vector.cs +++ b/Vector.cs @@ -33,11 +33,16 @@ public Vector(double x, double y) public void Normalize() { - double l = Math.Sqrt(X * X + Y * Y); + double l = Length(); X /= l; Y /= l; } + public double Length() + { + return Math.Sqrt(X * X + Y * Y); + } + public Vector Rotated(double degrees) { double rad = DegToRad(degrees); @@ -48,9 +53,24 @@ public Vector Rotated(double degrees) X * s + Y * c); } + public double AngleTo(Vector p2) + { + double angle = RadToDeg(Math.Atan2(p2.Y, p2.X) - Math.Atan2(Y, X)); + if (angle < -180.0) + angle += 360.0; + else if (angle > 180.0) + angle -= 360.0; + return angle; + } + public static double DegToRad(double degrees) { - return Math.PI * degrees / 180.0; + return degrees * Math.PI / 180.0; + } + + public static double RadToDeg(double radians) + { + return radians * 180.0 / Math.PI; } public double X;