예제 #1
0
        /// <summary>
        /// Tile in space constructor.
        /// </summary>
        /// <param name="baseObject">Base tile.</param>
        /// <param name="position">Position of the tile in space.</param>
        /// <param name="quaternion">Orientation of the tile.</param>
        public TileInSpace(Tile baseObject, Point3D position, Quaternion quaternion) :
            base(baseObject.Name, baseObject.Vertices.ToList(), baseObject.Connectors.Select(conn => (Connector)conn),
                 baseObject.SurfaceGlue, baseObject.Proteins, baseObject.Color, baseObject.Alpha, baseObject.AlphaRatio)
        {
            Vertices = baseObject.Vertices; // Must precede the connectors
            var connectors = baseObject.Connectors.Select(connector => new ConnectorOnTileInSpace(this, connector)).ToList();

            Connectors = new ReadOnlyCollection <ConnectorOnTileInSpace>(connectors);

            // Tile and the connectors are now in the basic position
            NorthConnector = Connectors.FirstOrDefault(c => c.Positions.All(p => p.Y > Position.Y));
            EastConnector  = Connectors.FirstOrDefault(c => c.Positions.All(p => p.X > Position.X));
            SouthConnector = Connectors.FirstOrDefault(c => c.Positions.All(p => p.Y < Position.Y));
            WestConnector  = Connectors.FirstOrDefault(c => c.Positions.All(p => p.X < Position.X));

            var proteins = base.Proteins.Select(protein => new ProteinOnTileInSpace(this, protein)).ToList();

            Proteins = new ReadOnlyCollection <ProteinOnTileInSpace>(proteins);

            Surface = new SurfaceConnector(this);
            Color   = baseObject.Color;

            Color = baseObject.Color;

            RotateAndMove(quaternion, position.ToVector3D());
            ID   = v_Counter++;
            Note = "";
        }
예제 #2
0
        /// <summary>
        /// Shortens a 2D object to a given length, starting at a given connector.
        /// </summary>
        /// <param name="length">New length of the object.</param>
        /// <param name="connector">Connector from which the new length is measured.</param>
        /// <exception cref="InvalidOperationException">If the object is not new
        /// or its vertices is not Segment3D or the required length >= than the current one.</exception>
        public void ShortenTo(double length, ConnectorOnTileInSpace connector)
        {
            var segment = Vertices as Segment3D;

            if (State != FState.Create || segment == null)
            {
                throw new InvalidOperationException($"{Name} is not a new 2D object and so it cannot be shortened.");
            }
            if (length >= segment.Vector.Length)
            {
                throw new ArgumentException($"Shortened length of object {Name} must be smaller than the current one.");
            }

            Note = "mutated";
            int start         = connector.Positions[0].MyEquals(Vertices[0]) ? 0 : 1;
            var newStartPoint = Vertices[start];
            var newEndPoint   = newStartPoint + length *
                                (Vertices[1 - start] - newStartPoint).Normalize();

            Vertices = new Segment3D(newStartPoint, newEndPoint, Name);

            // Move connectors at the cut-off end to the new end
            foreach (var conn in Connectors.Where(conn => conn.Positions[0].DistanceTo(newStartPoint) > length))
            {
                conn.Positions = new ReadOnlyCollection <Point3D>(new[] { newEndPoint });
            }

            // Move proteins at the cut-off end to the new end
            foreach (var prot in Proteins.Where(prot => prot.Position.DistanceTo(newStartPoint) > length))
            {
                prot.Position = newEndPoint;
            }
        }
예제 #3
0
        /// <summary>
        /// Tries to insert a new tile between two existing conected tiles at a given connection.
        /// TODO at the moment allows only insertion of rods, generalize also for 2D tiles
        /// Eventually releases the signal objects corresponding to the new connection.
        /// </summary>
        /// <param name="tile">New tile to be inserted.</param>
        /// <param name="connector1">Connector at which the new tile should be inserted.</param>
        /// <returns>
        /// True if the tile was inserted at the given connection.
        /// </returns>
        public bool Insert(Tile tile, ConnectorOnTileInSpace connector1)
        {
            if (tile == null)
            {
                throw new ArgumentNullException($"Parameter \'tile\' cannot be null");
            }
            if (connector1 == null)
            {
                throw new ArgumentNullException($"Parameter \'connector1\' cannot be null");
            }

            if (!(tile.Vertices is Segment3D))
            {
                throw new ArgumentException($"Parameter \'tile\' must be a 2D rod");
            }
            var connector2 = connector1.ConnectedTo;

            if (connector2 == null)
            {
                throw new ArgumentException($"Parameter \'connector1\' is not connected");
            }

            var connectorsOnTile = v_MSystem.MatchingConectors(tile, connector1.Glue, connector2.Glue);

            if (_Insert(connectorsOnTile, connector1))
            {
                var newTile = connector1.ConnectedTo.OnTile;
                _Add(newTile);
                AutoConnect(newTile);
                ReleaseSignalObjects(connector1);
                ReleaseSignalObjects(connector2);
                return(true);
            }
            return(false);
        }
예제 #4
0
 /// <summary>
 /// Connects bi-directionally this connector to another.
 /// </summary>
 /// <param name="connector">Connector to which to connect.</param>
 public void ConnectTo(ConnectorOnTileInSpace connector)
 {
     if (this.OnTile == connector.OnTile)
     {
         throw new ArgumentException($"Tile {OnTile.Name} cannot connect to itself.");
     }
     ConnectedTo           = connector;
     connector.ConnectedTo = this;
 }
예제 #5
0
        /// <summary>
        /// Releases signal objects emited by a newly established connection to the floating objects world.
        /// </summary>
        ///  <param name="connector">Old connector to which a new connector was attached.</param>
        private void ReleaseSignalObjects(ConnectorOnTileInSpace connector)
        {
            NamedMultiset signalObjects;

            if (v_MSystem.GlueRelation.TryGetValue(Tuple.Create(connector.Glue,
                                                                connector.ConnectedTo.Glue), out signalObjects))
            {
                v_FltObjectsWorld.AddAt(signalObjects, connector.SidePoint(connector.Positions.First()));
            }
        }
예제 #6
0
        /// <summary>
        /// Gets the set of floating objects within the reaction distance from any position of a given connector.
        /// Only objects unused in this simulation step are considered.
        /// TODO low priority: return floating objects close to the whole shape of the connector except endpoints
        /// </summary>
        public FloatingObjectsSet GetNearObjects(ConnectorOnTileInSpace connector, NamedMultiset targetMultiset)
        {
            FloatingObjectsSet objectsSet = new FloatingObjectsSet();

            foreach (var position in connector.Positions)
            {
                objectsSet.UnionWith(GetNearObjects(connector.SidePoint(position), targetMultiset));
            }
            return(objectsSet);
        }
예제 #7
0
 /// <summary>
 /// Disonnects bi-directionally this connection, resets the flag "SetDisconect".
 /// </summary>
 public void Disconnect()
 {
     if (ConnectedTo != null)
     {
         ConnectedTo.ConnectedTo    = null;
         ConnectedTo.WasConnectedTo = this;
         ConnectedTo.SetDisconnect  = false;
         WasConnectedTo             = ConnectedTo;
         ConnectedTo = null;
     }
     SetDisconnect = false;
 }
예제 #8
0
        /// <summary>
        /// Calculates the direction in which this growing tile pushes others.
        /// </summary>
        /// <param name="connector">Connector from which this object grows.</param>
        /// <returns>Pushing direction.</returns>
        public static UnitVector3D PushingDirection(ConnectorOnTileInSpace connector)
        {
            if (connector.ConnectedTo == null)
            {
                throw new ArgumentException("Pushing direction for unconnected connector is undefined");
            }
            var tile = connector.OnTile;

            // Point-to-point connection of a segment - pushing in the segment direction
            if (tile.Vertices is Segment3D)
            {
                return((tile.Position - connector.Positions[0]).Normalize());
            }

            // Edge-to-edge connection of a polygon - pushing from connector to the object's center
            if (connector.Positions.Count == 2)
            {
                return((new Line3D(connector.Positions[0], connector.Positions[1])).
                       LineTo(tile.Position, false).Direction);
            }

            return((connector.Positions[0] - connector.ConnectedTo.OnTile.Position).Normalize());
        }
예제 #9
0
        /// <summary>
        /// Tries to add a new tile to a given connection on an existing tile.
        /// Tries possible various connectors on the new object => various orientations of the new object.
        /// Eventually releases the signal objects corresponding to the new connection.
        /// </summary>
        /// <param name="tile">New tile to be attached.</param>
        /// <param name="freeConnector">Connector to which the new tile should be attached.</param>
        /// <param name="newlyCreatedTile">Out parameter with newly created tile in space.</param>
        /// <returns>
        /// True if the tile was attached to the given connection.
        /// </returns>
        public bool Add(Tile tile, ConnectorOnTileInSpace freeConnector, out TileInSpace newlyCreatedTile)
        {
            if (tile == null)
            {
                throw new ArgumentNullException($"Parameter \'tile\' cannot be null");
            }
            if (freeConnector == null)
            {
                throw new ArgumentNullException($"Parameter \'freeConnector\' cannot be null");
            }

            // already connected connector cannot be used
            if (freeConnector.ConnectedTo != null)
            {
                throw new ArgumentException($"Parameter \'freeConnector\' is already connected");
            }

            foreach (var connector in tile.Connectors.Where(connector => v_MSystem.AreCompatible(freeConnector, connector)))
            {
                freeConnector.ConnectObject(connector);
                if (Attach(freeConnector.ConnectedTo))
                {
                    TileInSpace newTile = freeConnector.ConnectedTo.OnTile;
                    _Add(newTile);
                    AutoConnect(newTile);
                    FltObjectsWorld.ExpandWith(new Box3D(newTile.Vertices), v_MSystem.RefillEnvironment);
                    ReleaseSignalObjects(freeConnector);

                    newlyCreatedTile = newTile;
                    return(true);
                }
                freeConnector.Disconnect();
            }

            newlyCreatedTile = null;
            return(false);
        }
예제 #10
0
파일: MSystem.cs 프로젝트: mmaverikk/Cytos
        /// <summary>   TODO low priority: move to class Connector when MSystem is available as singleton
        /// Checks whether two connector are mutually compatible (dimension, size, glues).
        /// Order of connectors matters due to possibly asymmetric glue relation!
        /// </summary>
        /// <param name="connector1"></param>
        /// <param name="connector2"></param>
        public bool AreCompatible(ConnectorOnTileInSpace connector1, ConnectorOnTile connector2)
        {
            // compatible glues and  connector sizes
            bool result = GlueRelation.ContainsKey(Tuple.Create(connector1.Glue, connector2.Glue)) &&
                          connector1.IsSizeComppatibleWith(connector2);

            if (!result || Nu0 <= 0)
            {
                return(result);
            }

            //if Nu0 > 0, then this is a cTAM: check if connector voltage exceeds the threshold
            if (connector2.OnTile.Connectors.Count == 4)
            {
                // Assume that an inner cTAM tile can connect only to an east or south connector
                // It is attachable only if it connects to two connectors simultaneously and
                // the sum o heir voltage exceeds the threshold Tau
                ConnectorOnTileInSpace otherConnector = null;
                if (connector1 == connector1.OnTile.EastConnector)
                {
                    otherConnector = connector1.OnTile.NorthConnector?.ConnectedTo?.OnTile.EastConnector?.ConnectedTo
                                     ?.OnTile.SouthConnector;
                }
                if (connector1 == connector1.OnTile.SouthConnector)
                {
                    otherConnector = connector1.OnTile.WestConnector?.ConnectedTo?.OnTile.SouthConnector?.ConnectedTo
                                     ?.OnTile.EastConnector;
                }
                return(otherConnector != null && otherConnector.Voltage + connector1.Voltage >= Tau);
            }
            else
            {
                // Border cTAM
                return(connector1.Voltage >= Tau);
            }
        }
예제 #11
0
        /// <summary>
        /// Calculates position and angle of a new tile connecting to this connection and connects it.
        /// The connector given as parameter determines also the connecting object.
        /// </summary>
        /// <param name="connector">Connector on the new tile by which it connects</param>
        /// <returns>New tile in space connected to this connection.</returns>
        public TileInSpace ConnectObject(ConnectorOnTile connector)
        {
            // Place the old and the new object into the same plane or line
            TileInSpace            newTile      = new TileInSpace(connector.OnTile, Point3D.Origin, OnTile.Quaternion);
            int                    index        = connector.OnTile.Connectors.IndexOf(connector);
            ConnectorOnTileInSpace newConnector = newTile.Connectors[index];

            UnitVector3D axis = OnTile.Vertices.Normal;   // rotation axis - TRY REVERSE IN CASE OF MALFUNCTION

            // Edge-to-edge
            if (newConnector.Positions.Count == 2 && Positions.Count == 2)
            {
                // Rotate the new object so that connector directions are opposite
                Vector3D vector1 = Positions[1] - Positions[0];
                Vector3D vector2 = newConnector.Positions[0] - newConnector.Positions[1];
                Angle    angle   = vector2.SignedAngleTo(vector1, axis);
                newTile.Rotate(axis, angle);

                // Rotate the new tile around the connector edge so that
                // the angle between both tiles is the angle associated with the connector
                newTile.Rotate(vector1.Normalize(), Angle.FromDegrees(180) - Angle);    // TRY REVERSE IN CASE OF MALFUNCTION

                // Move the new object so that the connectors match
                newTile.Move(Positions[0] - newConnector.Positions[1]);
            }
            // Point-to-point
            else if (newConnector.Positions.Count == 1 && Positions.Count == 1)
            {
                // Rotate the new object around an axis perpendicular to it by the angle associated with the connector

                var newSegment  = newTile.Vertices as Segment3D;
                var thisSegment = OnTile.Vertices as Segment3D;
                var thisAxis    = thisSegment?.Vector.Normalize() ?? OnTile.Vertices.Normal;

                if (newSegment == null)
                {
                    // Tile connecting to a segment
                    if (thisSegment != null)
                    {
                        axis = newTile.Vertices.Normal.CrossProduct(-Math.Sign(connector.Angle.Radians) * thisSegment.Vector).Normalize();
                    }
                    // ELSE 2D tile connecting to a 2D tile by a single point - should never happen
                }
                else
                {
                    if (OnTile.Vertices is Polygon3D) // Segment connecting to a tile
                    {
                        axis = axis.CrossProduct(-newSegment.Vector).Normalize();
                    }
                    // ELSE segment connecting to a segment - axis set above as a normal to this segment
                }
                newTile.Rotate(axis, Angle);    // TRY REVERSE AXIS OR ROTATION IN CASE OF MALFUNCTION

                // the newly connected object has one degree of freedom - rotate randomly
                newTile.Rotate(thisAxis, Angle.FromRadians(Randomizer.NextDoubleBetween(0, 2 * Math.PI)));
                // Move the new object so that the connectors match
                newTile.Move(Positions[0] - newConnector.Positions[0]);
            }
            else
            {
                throw new ArgumentException("Connecting incompatible connectors:" + this + "\n and" + newConnector);
            }

            ConnectTo(newConnector);
            return(newTile);
        }
예제 #12
0
 /// <summary>
 /// True if (vertices of) both connectors overlap within "tolerance".
 /// </summary>
 public bool Overlaps(ConnectorOnTileInSpace another, double tolerance = MSystem.Tolerance)
 {
     return(Geometry.Overlap(Positions, another.Positions, tolerance));
 }
예제 #13
0
        /// <summary>
        /// Tries to insert a tile between two existing conected tiles due to space constraints.
        /// Resolves pushing of fixed and floating objects.
        /// TODO at the moment allows only insertion of rods, generalize also for 2D tiles
        /// </summary>
        /// <param name="connectorsOnTile">Connectors on the new tile by which it should be attached.</param>
        /// <param name="connector1">COnnector on an existing tile at which the new tile is inserted</param>
        /// <returns>
        /// True if the given tile in space can be inserted to the world.
        /// </returns>
        private bool _Insert(Tuple <ConnectorOnTile, ConnectorOnTile> connectorsOnTile, ConnectorOnTileInSpace connector1)
        {
            var newTile        = connectorsOnTile.Item1.OnTile; // TODO create new tile by "ConnectObject(olderConnector)"
            var connector2     = connector1.ConnectedTo;
            var olderConnector = (connector1.OnTile.ID < connector2.OnTile.ID ? connector1 : connector2);
            var pushingVector  = newTile.Vertices[1] - newTile.Vertices[0];    // TODO correct

            // TODO FINISH
            return(false);
        }
예제 #14
0
        /// <summary>
        /// Tries to attach a tile in space to the tiles world due to space constraints.
        /// Resolves pushing of fixed and floating objects.
        /// TODO More realistic pushing:
        /// For each component containing tiles: calculate pushing vectors by tiles growing from them.
        /// Calculate their common (average) pushing veector.
        /// If its scalar product with all particular pushing vectors is positive, then try to apply the average vector
        /// to all objects pushed by these interconnected growing tiles.
        /// </summary>
        /// <param name="connector">Connector on the new tile by which it is attached to an existing one.</param>
        /// <returns>
        /// True if the given tile in space can be attached to the world.
        /// </returns>
        private bool Attach(ConnectorOnTileInSpace connector)
        {
            var pushingDirection = PushingDirection(connector);

            /* Randomize - up to phi/4 deviation - a parameter of randomization angle would have to be added to the input XML
             * Vector3D randomVector = new Vector3D(Randomizer.Rng.NextDouble(), Randomizer.Rng.NextDouble(),
             *  Randomizer.Rng.NextDouble());
             * var length = randomVector.Length;
             * if (length > 0.001)
             *  randomVector = randomVector / (2 * length);     // Length is 0.5 now
             * pushingDirection = (pushingDirection + randomVector).Normalize(); */

            Vector3D maxPushingVector = default;               // This will be the longest of all pushing vectors in the world
            var      newTile          = connector.OnTile;

            var objectsToBeChecked = new HashSet <TileInSpace>(); // Objects to be checked for pushing by newObject
            var objectsToBePushed  = new HashSet <TileInSpace>(); // Objects which passed the check and will be pushed

            // Check whether the newObject intersects existing ones, and add those to pushingList
            // together with their components
            foreach (var checkedObject in v_TileSet)       // TODO PARALLEL.FOREACH
            {
                if (newTile.IntersectsWith(checkedObject) || newTile.OverlapsWith(checkedObject))
                {
                    var pushingVector = newTile.PushingIntersected(checkedObject, pushingDirection);
                    if (newTile.Vertices is Polygon3D)
                    {
                        pushingVector = v_MSystem.PushingCoef * pushingVector;
                    }

                    if (pushingVector.Length > float.Epsilon)
//                        lock(objectsToBeChecked)
                    {
                        objectsToBeChecked.UnionWith(checkedObject.SetAndGetPushedComponent(pushingVector));
                        if (pushingVector.Length > maxPushingVector.Length)
                        {
                            maxPushingVector = pushingVector;
                        }
                    }
                }
            }

            var invalidPushing = newTile.PushingVector != default;

            // Check all objects in checkList whether they can be pushed, as they may in turn push other objects
            while (objectsToBeChecked.Any() && !invalidPushing)
            {
                var checkedObject = objectsToBeChecked.First();

                foreach (var existingObject in v_TileSet)       // TODO PARALLEL.FOREACH
                {
                    if (existingObject != checkedObject)
                    // checked object is not checked against itself
                    {
                        var pushingVector = checkedObject.PushingNonIntersected(existingObject);
                        // Secondary pushing
                        // Fact: pushingVector <= checkedObject.PushingVector;
                        // hence any object with maximum pushing vector in checkList is never returned back to the list.
                        // This guarantees that the *while* cycle will eventually end

                        if (pushingVector.Length > float.Epsilon)
                        //                        lock(objectsToBeChecked)
                        {
                            objectsToBeChecked.UnionWith(existingObject.SetAndGetPushedComponent(pushingVector));
                        }
                    }
                }
                objectsToBeChecked.Remove(checkedObject);
                objectsToBePushed.Add(checkedObject);
                invalidPushing |= newTile.PushingVector != default;
            }

            // If any object after pushing still intersects with newObjectm, then the pushing is unsuccessful.
            foreach (var pushedObject in v_TileSet.Where(obj => obj.PushingVector != default))
            {
                if (invalidPushing)     // No further cycling needed
                {
                    break;
                }

                // Push the object for intersection test
                var oldState = pushedObject.State;
                pushedObject.Move(pushedObject.PushingVector);
                invalidPushing |= newTile.IntersectsWith(pushedObject) || newTile.OverlapsWith(pushedObject);
                // Revert back
                pushedObject.Move(-pushedObject.PushingVector);
                pushedObject.State = oldState;
            }

            // If newObject == segment has not enough room, it can be shortened
            if (invalidPushing)
            {
                var segment   = newTile.Vertices as Segment3D;
                var newLength = Math.Max(0D, segment?.Vector.Length - maxPushingVector.Length ?? 0D);
                if (newLength < MSystem.Tolerance)
                {
                    // newObject not attached = there is just a tiny room, shortening would not help
                    ClearPushing();
                    return(false);
                }
                newTile.ShortenTo(newLength, connector);
            }
            else
            {
                // Pushing possible => it is applied now
                ApplyPushing(objectsToBePushed);
            }

            ClearPushing();
            return(true);
        }