Esempio n. 1
0
        public static float IncludedAngleCos(Vector2 v1, Vector2 v2)
        {
            v1.Normalize(); v2.Normalize();
            float dot = Vector2.Dot(v1, v2);

            return(2 * dot / (v1.Length() + v2.Length()));
        }
Esempio n. 2
0
        private KeyValuePair <HalfEdge, float>?FindFirstParallelEdge(Seed seed, float length, float distance, float parallelThreshold)
        {
            Contract.Requires(seed != null);

            var start   = seed.Origin.Position;
            var end     = seed.Origin.Position + seed.Direction * length;
            var segment = new LineSegment2(start, end);

            //Calculate the expanded bounds to query. This is as wide as the parallel check distance
            var p           = seed.Direction.Perpendicular() * distance / 2;
            var a           = start + p;
            var b           = start - p;
            var c           = end + p;
            var d           = end - p;
            var queryBounds = new BoundingRectangle(
                Vector2.Min(Vector2.Min(a, b), Vector2.Min(c, d)),
                Vector2.Max(Vector2.Max(a, b), Vector2.Max(c, d))
                );

            //now get all lines which intersect this bounds and check them for parallelism
            var candidates = _mesh.FindEdges(queryBounds);

            KeyValuePair <HalfEdge, float>?firstParallel = null;

            foreach (var candidate in candidates)
            {
                var dirCandidate = candidate.Segment.Line.Direction;
                var dir          = segment.Line.Direction;

                //Dot product directions of lines to check parallelism (compare with threshold)
                var dot = Math.Abs(Vector2.Dot(dir, dirCandidate));
                if (dot > parallelThreshold)
                {
                    //Our query bounds were larger than the actual area we wanted to query (because we're limited to axis aligned bounds)
                    //Check that this line enters the smaller OABB area
                    //We'll do this check by checking if the line segment intersects any of the four OABB segments (AB, BC, CD, DA)

                    if (new LineSegment2(a, b).Intersects(candidate.Segment).HasValue ||
                        new LineSegment2(b, c).Intersects(candidate.Segment).HasValue ||
                        new LineSegment2(c, d).Intersects(candidate.Segment).HasValue ||
                        new LineSegment2(d, a).Intersects(candidate.Segment).HasValue)
                    {
                        //check how far along this segment the parallelism starts

                        var startDist = segment.ClosestPointDistanceAlongSegment(candidate.StartVertex.Position);
                        var endDist   = segment.ClosestPointDistanceAlongSegment(candidate.EndVertex.Position);
                        var minDist   = Math.Min(startDist, endDist);

                        if (firstParallel == null || minDist < firstParallel.Value.Value)
                        {
                            firstParallel = new KeyValuePair <HalfEdge, float>(candidate, minDist);
                        }
                    }
                }
            }

            return(firstParallel);
        }
Esempio n. 3
0
        private void CreateOutline(IEnumerable <Vector2> shape, bool createFace)
        {
            Contract.Requires(shape != null);

            var vertices = (IReadOnlyList <Vertex>)shape.Select(_mesh.GetOrConstructVertex).ToArray();
            var edges    = new List <HalfEdge>(vertices.Count * 3);

            // Create the outer edges of the floor
            for (var i = 0; i < vertices.Count; i++)
            {
                //Start and end vertex of this wall
                var b = vertices[i];
                var c = vertices[(i + 1) % vertices.Count];

                //Create a series of edges between these two vertices (not just one edge, because we drop seeds along the line as we go)
                CreateImpassableEdge(b, c, edges);

                //We want to measure the internal angle at vertex "b", for that we need the previous vertex (which we'll call "a")
                var a = vertices[(i + vertices.Count - 1) % vertices.Count];

                //Calculate the inner angle between these vectors (not always clockwise!)
                var ab    = Vector2.Normalize(b.Position - a.Position);
                var bc    = Vector2.Normalize(c.Position - b.Position);
                var dot   = Vector2.Dot(bc, -ab);
                var det   = bc.Cross(-ab);
                var angle = (float)(Math.Atan2(det, dot) % (Math.PI * 2));
                angle = det < 0 ? angle * -1 : (float)Math.PI * 2 - angle;

                if (angle < Math.PI * 0.51)
                {
                    //0 -> 90 degrees
                    //Do nothing!
                }
                else if (angle <= Math.PI * 1.01)
                {
                    //90 -> 180
                    if (_random.RandomBoolean())
                    {
                        PerpendicularSeed(b, ab);
                    }
                    else
                    {
                        PerpendicularSeed(b, bc);
                    }
                }
                else if (angle <= Math.PI * 1.51)
                {
                    //180 -> 270
                    //if (_random.RandomBoolean())
                    //    BisectorSeed(b, -ab, -bc);  //Negated, to ensure bisection is on the correct side (angle is > 180, so by default bisection would be on wrong side)
                    //else
                    {
                        PerpendicularSeed(b, ab);
                        PerpendicularSeed(b, bc);
                    }
                }
                else
                {
                    //270 -> 360
                    //BisectorSeed(b, -ab, -bc);  //Negated, to ensure bisection is on the correct side (angle is > 180, so by default bisection would be on wrong side)
                    PerpendicularSeed(b, ab);
                    PerpendicularSeed(b, bc);
                }
            }

            if (createFace)
            {
                //Ensure we're always creating the clockwise face
                if (!edges.Select(a => a.EndVertex.Position).IsClockwise())
                {
                    edges.Reverse();
                    for (var i = 0; i < edges.Count; i++)
                    {
                        edges[i] = edges[i].Pair;
                    }
                }

                //Create face
                //todo: attach spacespec metadata (passed in instead of bool:createFace)
                var f = _mesh.GetOrConstructFace(edges);
                f.Tag = new FloorplanFaceTag(false);
            }
        }
Esempio n. 4
0
        private void GrowSeed(Seed seed)
        {
            Contract.Requires(seed != null);

            //Decide how far we're going to grow this seed
            var length = _seedDistance.SelectFloatValue(_random, _metadata);

            if (length < 0)
            {
                throw new InvalidOperationException("Seed distance must be > 0");
            }

            //Find the first edge which is parallel with this one
            var firstParallel = FindFirstParallelEdge(
                seed,
                length * _parallelLengthMultiplier.SelectFloatValue(_random, _metadata),
                _parallelCheckWidth.SelectFloatValue(_random, _metadata),
                _cosineParallelAngleThreshold.SelectFloatValue(_random, _metadata)
                );

            //Check for intersections with other edges
            var firstIntersection = FindFirstIntersection(seed, length + _seedDistance.MinValue);

            //Reject seeds with parallel edges in certain circumstances
            if (firstParallel.HasValue)
            {
                //There's a parallel edge and we don't intersect anything, so just ignore this seed altogether
                if (!firstIntersection.HasValue)
                {
                    return;
                }

                //There's a parallel edge and the first intersection if *after* the parallelism starts, so ignore this seed altogether
                if (firstIntersection.Value.Value.DistanceAlongB > firstParallel.Value.Value)
                {
                    return;
                }
            }


            if (firstIntersection.HasValue)
            {
                var intersectVert = _mesh.GetOrConstructVertex(firstIntersection.Value.Value.Position);
                if (intersectVert.Equals(seed.Origin))
                {
                    return;
                }

                //If the edge doesn't contain this vertex already then split the edge we've hit
                if (!firstIntersection.Value.Key.ConnectsTo(intersectVert))
                {
                    HalfEdge ab, bc;
                    _mesh.Split(firstIntersection.Value.Key, intersectVert, out ab, out bc);

                    var t = firstIntersection.Value.Key.Tag ?? firstIntersection.Value.Key.Pair.Tag;
                    ab.Tag = new FloorplanHalfEdgeTag(t.IsImpassable);
                    bc.Tag = new FloorplanHalfEdgeTag(t.IsImpassable);
                }

                //Create an edge to this intersection point
                _mesh.GetOrConstructHalfEdge(seed.Origin, intersectVert).Tag = new FloorplanHalfEdgeTag(false);

                //If this is not an external wall we can create a seed continuing forward
                var tag = firstIntersection.Value.Key.Tag ?? firstIntersection.Value.Key.Pair.Tag;
                var continuationChance = _intersectionContinuationChance.SelectFloatValue(_random, _metadata);
                if (!tag.IsImpassable && _random.RandomBoolean(1 - continuationChance))
                {
                    //New wall will be perpendicular to the wall we've hit...
                    var direction = firstIntersection.Value.Key.Segment.Line.Direction.Perpendicular();

                    //...but which perpendicular?
                    var dotRight = Vector2.Dot(direction, seed.Direction);
                    var dotLeft  = Vector2.Dot(-direction, seed.Direction);
                    if (dotLeft > dotRight)
                    {
                        direction *= -1;
                    }

                    //create new seed
                    var wallLength = Vector2.Distance(seed.Origin.Position, intersectVert.Position);
                    CreateSeed(intersectVert, direction, seed.T + wallLength);
                }
            }
            else
            {
                //Create edge along this distance
                var end = _mesh.GetOrConstructVertex(seed.Origin.Position + seed.Direction * length);
                _mesh.GetOrConstructHalfEdge(seed.Origin, end).Tag = new FloorplanHalfEdgeTag(false);

                float seedChance = _seedChance.SelectFloatValue(_random, _metadata);
                if (_random.RandomBoolean(seedChance))
                {
                    CreateSeed(end, seed.Direction, seed.T + length);
                }
                else
                {
                    // Choose which directions to grow in (LF, LR, FR, LFR) we're going to do
                    var newWalls = _random.RandomInteger(0, 3);

                    //Put some seeds at the end (right, left and straight on)
                    if (newWalls != 0)
                    {
                        CreateSeed(end, seed.Direction.Perpendicular(), seed.T + length);
                    }
                    if (newWalls != 2)
                    {
                        CreateSeed(end, -seed.Direction.Perpendicular(), seed.T + length);
                    }
                    if (newWalls != 1)
                    {
                        CreateSeed(end, seed.Direction, seed.T + length);
                    }
                }
            }
        }