private static IEnumerable <Seed> SeedsAlongEdge(Region region, BaseScalarField distanceField, IVector2Field major, IVector2Field minor) { float d = 0; Vector2?previous = null; foreach (var vertex in region.Vertices) { if (previous.HasValue) { var pos = previous.Value; var v = vertex - previous.Value; var length = v.Length(); var dir = v / length; for (int i = 0; i < length; i++) { var separation = distanceField.Sample(pos + dir * i); d += separation; if (d >= separation) { yield return(new Seed(pos + dir * i, major, minor)); d -= separation; } } } previous = vertex; } }
public TracingConfiguration(BaseScalarField priorityField, BaseScalarField separationField, ITensorField tensorField, IValueGenerator roadWidth, float searchAngle = 0.3926991f, //22.5 degrees in radians float segmentLength = 10, float mergeDistance = 25 ) { Contract.Requires(priorityField != null); Contract.Requires(separationField != null); Contract.Requires(tensorField != null); Contract.Requires(roadWidth != null); _priorityField = priorityField; _separationField = separationField; _tensorField = tensorField; _roadWidth = roadWidth; _consineSearchConeAngle = (float)Math.Cos(searchAngle); _segmentLength = segmentLength; _mergeDistance = mergeDistance; }
private Seed?RemoveSeed(IMinHeap <KeyValuePair <float, Seed> > seeds, BaseScalarField separation, float cosineSearchAngle, Func <Edge, bool> edgeFilter = null) { Contract.Requires(seeds != null); Contract.Requires(separation != null); while (seeds.Count > 0) { //Get the highest priority seed var s = seeds.RemoveMin().Value; //Check if it's valid var d = s.Field.Sample(s.Point); //Degenerate point? var l = d.Length(); if (l < 0.001f) { continue; } var sep = separation.Sample(s.Point); //Normalize direction d /= l; //Get edges near this point and check if there is a parallel edge if (FindEdges(s.Point, sep).Where(e => edgeFilter == null || edgeFilter(e)).Any(e => Math.Abs(Vector2.Dot(e.Direction, d)) > cosineSearchAngle)) { continue; } return(s); } //No valid seeds found return(null); }
public Heightmap(BaseScalarField height) { Contract.Requires(height != null); _gradient = new Gradient(height); }
private static void AddSeed(IMinHeap <KeyValuePair <float, Seed> > seeds, Seed seed, BaseScalarField priority = null, BaseScalarField separation = null) { Contract.Requires(seeds != null); var prio = priority.SafeSample(seed.Point) + 1 / separation.SafeSample(seed.Point); seeds.Add(new KeyValuePair <float, Seed>(-prio, seed)); }
private Streamline Trace(Seed seed, bool reverse, MinHeap <KeyValuePair <float, Seed> > seeds, Func <Vector2, bool> isOutOfBounds, float maxSegmentLength, float mergeDistance, float cosineSearchAngle, BaseScalarField separation) { var maxSegmentLengthSquared = maxSegmentLength * maxSegmentLength; var seedingDistance = float.MaxValue; var direction = Vector2.Zero; var position = seed.Point; var stream = new Streamline(FindOrCreateVertex(position, mergeDistance, cosineSearchAngle)); //This is a weird way to do a for loop! What gives? //This is, in many respects, a better way to do it if you don't want i to be mutated within the loop //In this case I'm using it to pacify a persistent CodeContracts false positive (this loop is too complex for it to analyze, I guess?) foreach (var i in Enumerable.Range(0, 10000)) { direction = seed.Field.TraceVectorField(position, direction, maxSegmentLength); if (i == 0) { direction *= reverse ? -1 : 1; } //degenerate step check var segmentLength = direction.Length(); if (segmentLength < 0.00005f) { break; } //Excessive step check if (segmentLength > maxSegmentLength) { direction /= segmentLength * maxSegmentLength; segmentLength = maxSegmentLength; } //Step along path position += direction; seedingDistance += segmentLength; //Bounds check if (isOutOfBounds(position)) { CreateEdge(stream, position, Vector2.Normalize(direction), maxSegmentLength, maxSegmentLengthSquared, mergeDistance, cosineSearchAngle, skipDistanceCheck: true); break; } //Create the segment and break if it says so if (CreateEdge(stream, position, Vector2.Normalize(direction), maxSegmentLength, maxSegmentLengthSquared, mergeDistance, cosineSearchAngle)) { break; } //Accumulate seeds to trace into the alternative field var seedSeparation = separation.Sample(position); if (seedingDistance > seedSeparation) { seedingDistance = 0; AddSeed(seeds, new Seed(position, seed.AlternativeField, seed.Field)); } } return(stream); }
private void Build(IEnumerable <Seed> initialSeeds, Vector2 min, Vector2 max, BaseScalarField separation, bool forward, bool backward, float maxSegmentLength, float mergeDistance, float cosineSearchAngle, Func <Vector2, bool> isOutOfBounds, Func <Edge, bool> edgeFilter, Action <Streamline> streamCreated) { Contract.Requires(initialSeeds != null); Contract.Requires(separation != null); Contract.Requires(isOutOfBounds != null); Contract.Requires(streamCreated != null); var seeds = new MinHeap <KeyValuePair <float, Seed> >(1024, new KeyComparer <float, Seed>()); foreach (var initialSeed in initialSeeds) { AddSeed(seeds, initialSeed); } //Trace out roads for every single seed while (seeds.Count > 0) { var s = RemoveSeed(seeds, separation, cosineSearchAngle, edgeFilter); if (!s.HasValue) { continue; } if (forward) { var stream = CheckStream(Trace(s.Value, false, seeds, isOutOfBounds, maxSegmentLength, mergeDistance, cosineSearchAngle, separation)); if (stream != null) { _streams.Add(stream); streamCreated(stream); } } if (backward) { var stream = CheckStream(Trace(s.Value, true, seeds, isOutOfBounds, maxSegmentLength, mergeDistance, cosineSearchAngle, separation)); if (stream != null) { _streams.Add(stream); streamCreated(stream); } } } }
public Gradient(BaseScalarField scalar) { Contract.Requires(scalar != null); _scalar = scalar; }