Esempio n. 1
0
        static (Connection, Connection) FindBestConnectionOnBrick(Vector3 pickupOffset, Brick brick, Ray ray, List <ConnectionField> selectedFields, HashSet <Brick> selectedBricks)
        {
            var physicsScene = brick.gameObject.scene.GetPhysicsScene();
            var oldPositions = new List <Vector3>();
            var oldRotations = new List <Quaternion>();

            foreach (var selected in selectedBricks)
            {
                oldPositions.Add(selected.transform.position);
                oldRotations.Add(selected.transform.rotation);
            }

            foreach (var part in brick.parts)
            {
                if (part.connectivity == null)
                {
                    continue;
                }

                // Only pick from matching feature types
                var validConnectionPairs = new List <(ConnectionField, ConnectionField)>();

                foreach (var field in part.connectivity.connectionFields)
                {
                    if (field.GetConnectedConnections().Count == field.connections.Count)
                    {
                        continue;
                    }

                    foreach (var selectedField in selectedFields)
                    {
                        if (!ConnectionField.MatchTypes(field, selectedField))
                        {
                            continue;
                        }

                        validConnectionPairs.Add((field, selectedField));
                    }
                }

                if (validConnectionPairs.Count == 0)
                {
                    continue;
                }

                // Sort by distance to ray origin and then by angle of the up angle in case the distance is the same
                validConnectionPairs = validConnectionPairs.OrderBy(f =>
                {
                    var field       = f.Item1;
                    var fieldSize   = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5 * 0.5f;
                    var localCenter = new Vector3(-fieldSize.x, 0.0f, fieldSize.z);
                    var fieldCenter = field.transform.TransformPoint(localCenter);

                    return(Vector3.Distance(ray.origin, fieldCenter));
                }).ThenBy(f => {
                    return(Vector3.Angle(f.Item1.transform.up, f.Item2.transform.up));
                }).ToList();

                // Run through every possible connectionfield
                foreach (var(field, selectedField) in validConnectionPairs)
                {
                    var oldPos = selectedField.transform.position;
                    var oldRot = selectedField.transform.rotation;

                    Quaternion newRot;
                    if (!ConnectionField.AlignRotation(selectedField.transform, field.transform, out newRot))
                    {
                        continue;
                    }

                    var selectedBrick = selectedField.connectivity.part.brick;
                    var pivot         = selectedBrick.transform.position + pickupOffset;

                    newRot.ToAngleAxis(out float angle, out Vector3 axis);
                    selectedField.transform.RotateAround(pivot, axis, angle);

                    // Project the selected field onto the static field along the ray direction
                    var selectedPos      = selectedField.transform.position;
                    var localRay         = field.transform.InverseTransformDirection(ray.direction);
                    var localOrigin      = field.transform.InverseTransformPoint(ray.origin);
                    var localSelectedPos = field.transform.InverseTransformPoint(selectedPos);

                    var localProjectedT = (-Vector3.Dot(Vector3.up, localSelectedPos)) / Vector3.Dot(Vector3.up, localRay);
                    var localNewPoint   = localSelectedPos + localRay * localProjectedT;

                    AlignToGrid(ref localNewPoint, Matrix4x4.identity);

                    // Check neighbouring positions in the order defined by connectionFieldOffsets.
                    // If we do not overlap with no offset, do not try the other offsets.
                    for (var i = 0; i < connectionFieldOffsets.Length; i++)
                    {
                        var offset       = connectionFieldOffsets[i];
                        var localToWorld = field.transform.TransformPoint(localNewPoint - Vector3.right * offset.x * LU_5 + Vector3.forward * offset.y * LU_5);

                        selectedField.transform.position = localToWorld;

                        var fieldSize         = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5;
                        var selectedFieldSize = new Vector3(selectedField.gridSize.x, 0.0f, selectedField.gridSize.y) * LU_5;

                        var f1 = new Vector3(0.0f, 0.0f, 0.0f);
                        var f2 = new Vector3(-fieldSize.x, 0.0f, fieldSize.z);
                        var f3 = new Vector3(-fieldSize.x, 0.0f, 0.0f);
                        var f4 = new Vector3(0.0f, 0.0f, fieldSize.z);

                        var p1 = new Vector3(0.0f, 0.0f, 0.0f);
                        var p2 = new Vector3(-selectedFieldSize.x, 0.0f, selectedFieldSize.z);
                        var p3 = new Vector3(-selectedFieldSize.x, 0.0f, 0.0f);
                        var p4 = new Vector3(0.0f, 0.0f, selectedFieldSize.z);

                        var s1 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p1));
                        var s2 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p2));
                        var s3 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p3));
                        var s4 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p4));

                        var sMinX = Mathf.Min(s1.x, Mathf.Min(s2.x, Mathf.Min(s3.x, s4.x)));
                        var sMinZ = Mathf.Min(s1.z, Mathf.Min(s2.z, Mathf.Min(s3.z, s4.z)));

                        var sMaxX = Mathf.Max(s1.x, Mathf.Max(s2.x, Mathf.Max(s3.x, s4.x)));
                        var sMaxZ = Mathf.Max(s1.z, Mathf.Max(s2.z, Mathf.Max(s3.z, s4.z)));

                        var fMinX = Mathf.Min(f1.x, Mathf.Min(f2.x, Mathf.Min(f3.x, f4.x)));
                        var fMinZ = Mathf.Min(f1.z, Mathf.Min(f2.z, Mathf.Min(f3.z, f4.z)));

                        var fMaxX = Mathf.Max(f1.x, Mathf.Max(f2.x, Mathf.Max(f3.x, f4.x)));
                        var fMaxZ = Mathf.Max(f1.z, Mathf.Max(f2.z, Mathf.Max(f3.z, f4.z)));

                        if (sMinX > fMaxX || fMinX > sMaxX || sMinZ > fMaxZ || fMinZ > sMaxZ)
                        {
                            selectedField.transform.position = oldPos;
                            selectedField.transform.rotation = oldRot;

                            // If we do not overlap with no offset, do not try the other offsets.
                            if (i == 0)
                            {
                                break;
                            }
                            else
                            {
                                continue;
                            }
                        }

                        // Overlapping rect
                        var minX = Mathf.RoundToInt(Mathf.Max(sMinX, fMinX) / LU_5);
                        var maxX = Mathf.RoundToInt(Mathf.Min(sMaxX, fMaxX) / LU_5);
                        var minZ = Mathf.RoundToInt(Mathf.Max(sMinZ, fMinZ) / LU_5);
                        var maxZ = Mathf.RoundToInt(Mathf.Min(sMaxZ, fMaxZ) / LU_5);

                        var currentFieldPos = selectedField.transform.position;
                        var currentFieldRot = selectedField.transform.rotation;

                        // Iterate through overlap
                        for (var x = minX; x < maxX + 1; x++)
                        {
                            for (var z = minZ; z < maxZ + 1; z++)
                            {
                                var localPos        = new Vector3(x * LU_5, 0.0f, z * LU_5);
                                var fieldConnection = field.GetConnectionAt(ConnectionField.ToGridPos(localPos));

                                if (fieldConnection != null && !fieldConnection.HasConnection())
                                {
                                    var connection = selectedField.GetConnectionAt(fieldConnection.transform.position);

                                    if (connection != null && !connection.HasConnection())
                                    {
                                        if (!Connection.MatchTypes(fieldConnection.connectionType, connection.connectionType))
                                        {
                                            continue;
                                        }

                                        selectedField.transform.position = oldPos;
                                        selectedField.transform.rotation = oldRot;

                                        // Find the rotation and position we need to offset the other selected bricks with
                                        ConnectionField.GetConnectedTransformation(connection, fieldConnection, pivot, out Vector3 connectedOffset, out angle, out axis);

                                        var oldBrickPos = selectedBrick.transform.position;
                                        var oldBrickRot = selectedBrick.transform.rotation;

                                        AlignTransformations(selectedBricks, pivot, axis, angle, connectedOffset);

                                        Physics.SyncTransforms();

                                        var colliding = Colliding(selectedBricks, selectedBrick);

                                        selectedBrick.transform.position = oldBrickPos;
                                        selectedBrick.transform.rotation = oldBrickRot;

                                        Physics.SyncTransforms();

                                        if (!colliding && ConnectionField.IsConnectionValid(connection, fieldConnection, pivot, selectedBricks))
                                        {
                                            ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick);
                                            return(connection, fieldConnection);
                                        }
                                        selectedField.transform.position = currentFieldPos;
                                        selectedField.transform.rotation = currentFieldRot;
                                        ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick);
                                    }
                                }
                            }
                        }
                    }
                    selectedField.transform.position = oldPos;
                    selectedField.transform.rotation = oldRot;
                }
            }
            return(null, null);
        }
Esempio n. 2
0
        static (Connection, Connection) FindBestConnectionOnBrick(Vector3 pickupOffset, Brick brick, Ray ray, List <ConnectionField> selectedFields, HashSet <Brick> selectedBricks)
        {
            var physicsScene = brick.gameObject.scene.GetPhysicsScene();
            var oldPositions = new List <Vector3>();
            var oldRotations = new List <Quaternion>();

            foreach (var selected in selectedBricks)
            {
                oldPositions.Add(selected.transform.position);
                oldRotations.Add(selected.transform.rotation);
            }

            foreach (var part in brick.parts)
            {
                if (part.connectivity == null)
                {
                    continue;
                }

                // Only pick from matching feature types
                var validConnectionPairs = new List <(ConnectionField, ConnectionField)>();

                foreach (var field in part.connectivity.connectionFields)
                {
                    if (!field.HasAvailableConnections())
                    {
                        continue;
                    }

                    foreach (var selectedField in selectedFields)
                    {
                        if (field.kind == selectedField.kind)
                        {
                            continue;
                        }

                        if (!ConnectionField.MatchTypes(field, selectedField))
                        {
                            continue;
                        }

                        validConnectionPairs.Add((field, selectedField));
                    }
                }

                if (validConnectionPairs.Count == 0)
                {
                    continue;
                }

                // Sort by distance to ray origin and then by angle of the up angle in case the distance is the same
                validConnectionPairs = validConnectionPairs.OrderBy(f =>
                {
                    var field       = f.Item1;
                    var fieldSize   = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5 * 0.5f;
                    var localCenter = new Vector3(-fieldSize.x, 0.0f, fieldSize.z);
                    var fieldCenter = field.transform.TransformPoint(localCenter);

                    return(Vector3.Distance(ray.origin, fieldCenter));
                }).ThenBy(f => {
                    return(Vector3.Angle(f.Item1.transform.up, f.Item2.transform.up));
                }).ToList();

                // Run through every possible connectionfield
                foreach (var(field, selectedField) in validConnectionPairs)
                {
                    var oldPos = selectedField.transform.position;
                    var oldRot = selectedField.transform.rotation;

                    Quaternion newRot;
                    if (!ConnectionField.AlignRotation(selectedField.transform, field.transform, out newRot))
                    {
                        continue;
                    }

                    var selectedBrick = selectedField.connectivity.part.brick;
                    var pivot         = selectedBrick.transform.position + pickupOffset;

                    newRot.ToAngleAxis(out float angle, out Vector3 axis);
                    selectedField.transform.RotateAround(pivot, axis, angle);

                    // Project the selected field onto the static field along the ray direction
                    var selectedPos      = selectedField.transform.position;
                    var localRay         = field.transform.InverseTransformDirection(ray.direction);
                    var localOrigin      = field.transform.InverseTransformPoint(ray.origin);
                    var localSelectedPos = field.transform.InverseTransformPoint(selectedPos);

                    var localProjectedT = (-Vector3.Dot(Vector3.up, localSelectedPos)) / Vector3.Dot(Vector3.up, localRay);
                    var localNewPoint   = localSelectedPos + localRay * localProjectedT;

                    AlignToGrid(ref localNewPoint, Matrix4x4.identity);

                    // Check neighbouring positions in the order defined by connectionFieldOffsets.
                    // If we do not overlap with no offset, do not try the other offsets.
                    for (var i = 0; i < connectionFieldOffsets.Length; i++)
                    {
                        var offset       = connectionFieldOffsets[i];
                        var localToWorld = field.transform.TransformPoint(localNewPoint - Vector3.right * offset.x * LU_5 + Vector3.forward * offset.y * LU_5);

                        selectedField.transform.position = localToWorld;

                        if (!ConnectionField.GetOverlap(field, selectedField, out Vector2Int min, out Vector2Int max))
                        {
                            selectedField.transform.position = oldPos;
                            selectedField.transform.rotation = oldRot;

                            // If we do not overlap with no offset, do not try the other offsets.
                            if (i == 0)
                            {
                                break;
                            }
                            else
                            {
                                continue;
                            }
                        }

                        var currentFieldPos = selectedField.transform.position;
                        var currentFieldRot = selectedField.transform.rotation;

                        var reject = false;

                        List <(Vector2, Connection, Connection)> matches = new List <(Vector2, Connection, Connection)>();

                        // Check for rejections on the overlap
                        for (var x = min.x; x < max.x + 1; x++)
                        {
                            if (reject)
                            {
                                break;
                            }

                            for (var z = min.y; z < max.y + 1; z++)
                            {
                                if (reject)
                                {
                                    break;
                                }

                                var localPos        = new Vector3(x * LU_5, 0.0f, z * LU_5);
                                var fieldConnection = field.GetConnectionAt(ConnectionField.ToGridPos(localPos));

                                if (fieldConnection != null && !field.HasConnection(fieldConnection))
                                {
                                    var fieldConnectionPosition = fieldConnection.field.GetPosition(fieldConnection);
                                    var connection = selectedField.GetConnectionAt(fieldConnectionPosition);
                                    if (connection != null && !selectedField.HasConnection(connection))
                                    {
                                        var pairMatch = Connection.MatchTypes(fieldConnection.connectionType, connection.connectionType);
                                        if (pairMatch == Connection.ConnectionMatch.reject)
                                        {
                                            reject = true;
                                            break;
                                        }
                                        else if (pairMatch == Connection.ConnectionMatch.connect)
                                        {
                                            matches.Add((new Vector2(x, z), connection, fieldConnection));
                                        }
                                    }
                                }
                            }
                        }

                        if (reject)
                        {
                            continue;
                        }

                        foreach (var(pos, c1, c2) in matches)
                        {
                            if (reject)
                            {
                                break;
                            }

                            selectedField.transform.position = oldPos;
                            selectedField.transform.rotation = oldRot;

                            // Find the rotation and position we need to offset the other selected bricks with
                            ConnectionField.GetConnectedTransformation(c1, c2, pivot, out Vector3 connectedOffset, out angle, out axis);

                            var oldBrickPos = selectedBrick.transform.position;
                            var oldBrickRot = selectedBrick.transform.rotation;

                            AlignTransformations(selectedBricks, pivot, axis, angle, connectedOffset);
                            Physics.SyncTransforms();

                            foreach (var checkBrick in selectedBricks)
                            {
                                if (reject)
                                {
                                    break;
                                }

                                foreach (var checkPart in checkBrick.parts)
                                {
                                    if (reject)
                                    {
                                        break;
                                    }

                                    foreach (var checkField in checkPart.connectivity.connectionFields)
                                    {
                                        var cons = checkField.QueryConnections(out Connection.ConnectionMatch match, true);
                                        if (match == Connection.ConnectionMatch.reject)
                                        {
                                            selectedBrick.transform.position = oldBrickPos;
                                            selectedBrick.transform.rotation = oldBrickRot;
                                            ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick);
                                            reject = true;
                                            break;
                                        }
                                    }
                                }
                            }

                            if (reject)
                            {
                                break;
                            }

                            Physics.SyncTransforms();

                            var colliding = Colliding(selectedBricks, selectedBrick);

                            selectedBrick.transform.position = oldBrickPos;
                            selectedBrick.transform.rotation = oldBrickRot;

                            Physics.SyncTransforms();

                            if (!colliding && ConnectionField.IsConnectionValid(c1, c2, pivot, selectedBricks))
                            {
                                ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick);
                                return(c1, c2);
                            }
                            selectedField.transform.position = currentFieldPos;
                            selectedField.transform.rotation = currentFieldRot;
                            ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick);
                        }
                    }
                    selectedField.transform.position = oldPos;
                    selectedField.transform.rotation = oldRot;
                }
            }
            return(null, null);
        }