poprawiono generowanie dzialek

This commit is contained in:
Kacper Donat 2019-11-17 16:20:30 +01:00
parent bf6c31dd9d
commit 841bdb9ee7
9 changed files with 162 additions and 16 deletions

View File

@ -1,12 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Assets.Cities;
using Assets.Common;
using Assets.Voronoi;
using UnityEngine.SocialPlatforms.GameCenter;
using UnityEngine.UIElements;
namespace Assets.AnnotationPass
{
@ -17,8 +14,10 @@ namespace Assets.AnnotationPass
private Graph<Point> _voronoiGraph;
public double MinimumAngle { get; set; }
public double MaxNudgeDistance { get; set; }
public double MinNudgeDistance { get; set; }
public double MaxNudgeDistance { get; set; } = .25;
public double MinNudgeDistance { get; set; } = .75;
public double MinimumRoadLength { get; set; } = 2;
public CityFieldsPass(Random random)
{
@ -38,16 +37,52 @@ namespace Assets.AnnotationPass
FixRoadsDensity(city, MinimumAngle);
CreateFieldBoundaries(city);
CreateCityFields(city);
RemoveTooShortRoads(city);
NudgeCityFields(city);
}
private void RemoveTooShortRoads(City city)
{
foreach (var field in city.Fields)
{
field.Boundary = field.Boundary.Aggregate(new List<Point>(), (boundary, point) =>
{
if (boundary.Count == 0)
{
boundary.Add(point);
}
else
{
var last = boundary[boundary.Count - 1];
if (Point.Dist(last, point) > MinimumRoadLength)
boundary.Add(point);
else
boundary[boundary.Count - 1] = (point + last) / 2;
}
return boundary;
});
var f = field.Boundary.First();
var l = field.Boundary.Last();
if (Point.Dist(l, f) < MinimumRoadLength)
{
field.Boundary[0] = (f + l) / 2;
field.Boundary.Remove(l);
}
}
city.Fields = city.Fields.Where(field => field.Boundary.Count > 2).ToList();
}
private void NudgeCityFields(City city)
{
foreach (var field in city.Fields)
{
var center = PointUtils.Mean(field.Boundary);
var nudge = _random.NextDouble() * (MaxNudgeDistance - MinNudgeDistance) + MinNudgeDistance;
field.Boundary = field.Boundary.Select(p => p - (p - center).Direction * nudge).ToList();
var nudge = _random.NextDouble() * MaxNudgeDistance + MinNudgeDistance;
field.Boundary = field.Boundary.Zip(PointUtils.CalculateNormals(field.Boundary), (a, b) => (Point: a, Normal: b)).Select(tuple => tuple.Point - tuple.Normal * nudge).ToList();
}
}
@ -74,8 +109,6 @@ namespace Assets.AnnotationPass
CityField CreateField(int start, int next)
{
CityField field = new CityField();
field.Boundary.Add(city.FieldBoundaries[start]);
int watchdog = 100;
int current = start;
@ -99,7 +132,11 @@ namespace Assets.AnnotationPass
}
// remove outside field
city.Fields.Remove(city.Fields.OrderByDescending(field => field.Boundary.Count).First());
city.Fields.RemoveAll(field =>
{
var points = field.Boundary.Take(3).ToArray();
return !PointUtils.IsClockwise(points[0], points[1], points[2]);
});
}
public void Annotate(Map map)

View File

@ -12,7 +12,7 @@ namespace Assets.Cities
public Graph<Point> Roads { get; set; } = new Graph<Point>();
public Graph<Point> FieldBoundaries { get; set; } = new Graph<Point>();
public List<CityField> Fields { get; } = new List<CityField>();
public List<CityField> Fields { get; set; } = new List<CityField>();
public List<(int, int)> Edges = new List<(int, int)>();

View File

@ -5,6 +5,6 @@ namespace Assets.Cities
{
public class CityField
{
public ICollection<Point> Boundary { get; set; } = new List<Point>();
public IList<Point> Boundary { get; set; } = new List<Point>();
}
}

View File

@ -29,6 +29,16 @@ namespace Assets.Common
return tail.Concat(head);
}
public static IEnumerable<T> RotateLeft<T>(this IEnumerable<T> collection, int count = 1)
{
var diff = collection.Count() - count;
var head = collection.Take(count);
var tail = collection.Skip(count).Take(diff);
return tail.Concat(head);
}
}
public static class HasMetadataExtensions

View File

@ -1,5 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Assets.Common
{
@ -21,6 +23,20 @@ namespace Assets.Common
return result;
}
public static IEnumerable<Point> CalculateNormals(IEnumerable<Point> points)
{
var pairs = points.RotateRight().Zip(points, (a, b) => (a, b));
var thirds = pairs.Zip(points.RotateLeft(), (pair, c) => (pair.a, pair.b, c));
return thirds.Select(third =>
{
var left = (third.a - third.b).Direction;
var right = (third.c - third.b).Direction;
return ((left + right) / 2).Direction * (IsClockwise(third.a, third.b, third.c) ? -1 : 1);
});
}
public static double AngleBetween(Point center, Point a, Point b)
{
a = a - center;

View File

@ -65,6 +65,8 @@ namespace Assets
public float minimumNudgeDistance = 0.5f;
[Range(0f, 4f)]
public float maximumNudgeDistance = 1f;
[Range(0f, 4f)]
public float minimumRoadLength = 1f;
[Range(2.0f, 64.0f)]
public float radius = 8;
@ -113,6 +115,7 @@ namespace Assets
MinimumAngle = Mathf.Deg2Rad * minimumRoadAngle,
MaxNudgeDistance = maximumNudgeDistance,
MinNudgeDistance = minimumNudgeDistance,
MinimumRoadLength = minimumRoadLength
});
return generator;
@ -267,9 +270,10 @@ namespace Assets
foreach (var field in city.Fields)
{
foreach (var (a, b) in field.Boundary.RotateRight().Zip(field.Boundary, (a, b) => (a, b)))
{
Gizmos.DrawLine(a.ToVector3(), b.ToVector3());
}
foreach (var tuple in field.Boundary.Zip(PointUtils.CalculateNormals(field.Boundary), (a, b) => (Point: a, Normal: a + b)))
Gizmos.DrawLine(tuple.Point.ToVector3(), tuple.Normal.ToVector3());
}
}
}

View File

@ -18,7 +18,8 @@ namespace Assets
private IList<IRenderer> _renderers = new List<IRenderer>
{
new LandmassRenderer()
new LandmassRenderer(),
new CityRenderer(),
};
public void GenerateRandom()

View File

@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Linq;
using Assets.AnnotationPass;
using Assets.Cities;
using Assets.Common;
using UnityEngine;
namespace Assets.RenderPass
{
public class CityRenderer : IRenderer
{
public void Render(Map map, Component component)
{
var go = new GameObject();
go.AddComponent<MeshFilter>();
go.AddComponent<MeshRenderer>();
var cities = map.GetProperty<IEnumerable<City>>(LocateCitiesPass.CitiesProperty);
var meshes = cities.Select(city => new CombineInstance { mesh = CreateCityMesh(city), transform = component.transform.localToWorldMatrix });
var mesh = new Mesh();
mesh.CombineMeshes(meshes.ToArray());
go.GetComponent<MeshFilter>().sharedMesh = mesh;
}
private Mesh CreateCityMesh(City city)
{
List<Vector3> vertices = new List<Vector3>();
List<Vector3> normals = new List<Vector3>();
List<int> triangles = new List<int>();
int start = 0, n = 0;
foreach (var field in city.Fields)
{
start = vertices.Count;
n = field.Boundary.Count;
vertices.AddRange(field.Boundary.Select(p => p.ToVector3() + Vector3.up * 5));
normals.AddRange(field.Boundary.Select(v => Vector3.up));
triangles.AddRange(Triangulate(field).Select(x => x + start));
start = vertices.Count;
vertices.AddRange(field.Boundary.Select(p => p.ToVector3() + Vector3.up * 5));
normals.AddRange(PointUtils.CalculateNormals(field.Boundary).Select(p => p.ToVector3()));
vertices.AddRange(field.Boundary.Select(p => p.ToVector3()));
normals.AddRange(PointUtils.CalculateNormals(field.Boundary).Select(p => p.ToVector3()));
for (int i = 0; i < n; i++)
{
triangles.AddRange(new []{ start + i, start + n + i, start + n + (i + 1) % n });
triangles.AddRange(new []{ start + i, start + n + (i + 1) % n, start + (i + 1) % n });
}
}
return new Mesh
{
vertices = vertices.ToArray(),
normals = normals.ToArray(),
triangles = triangles.ToArray(),
};
}
private static IEnumerable<int> Triangulate(CityField field)
{
return new Triangulator(field.Boundary.Select(p => new Vector2((float)p.x, (float)p.y)).ToArray()).Triangulate();
}
public void DrawGizmos(Map map, Component component)
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9804b7a1c403484e9a6c51c550be879b
timeCreated: 1573942634