Beispiel #1
0
 /// <summary>
 /// read and set the two parameter to follow a chain of brick until we found a flexible part. This function only
 /// advance the search for one step (one link).
 /// </summary>
 /// <param name="connectionLead">the current connection point, from which we need to find the next one</param>
 /// <param name="isConnectionLeadingToFlexiblePart">a flag to tell if a flexible brick is found</param>
 private void followConnectionToFindFlexiblePart(ref LayerBrick.Brick.ConnectionPoint connectionLead, ref bool isConnectionLeadingToFlexiblePart)
 {
     if (connectionLead != null)
     {
         // set the flag if not already true
         isConnectionLeadingToFlexiblePart |= (BrickLibrary.Instance.getConnexionHingeAngle(connectionLead.Type) != 0.0f);
         // if we found a flexible part, stop following the path
         if (isConnectionLeadingToFlexiblePart)
         {
             connectionLead = null;                     // stop following this path
         }
         else
         {
             // jump to the attach brick accross the link (which may be null, in such case, the search will stop)
             connectionLead = connectionLead.ConnectionLink;
             // if there's a brick connected, check if it is a 2 connections brick and take the other
             if (connectionLead != null)
             {
                 // get the list of connection point for the first leading brick
                 List <LayerBrick.Brick.ConnectionPoint> connectionList = connectionLead.mMyBrick.ConnectionPoints;
                 if (connectionList.Count == 2)
                 {
                     // check the next connections and set it
                     int otherIndex = (connectionList[0] == connectionLead) ? 1 : 0;
                     connectionLead = connectionList[otherIndex];
                 }
                 else
                 {
                     // if it is a 0, 1 or 3 or more connections brick type, stop the search
                     connectionLead = null;
                 }
             }
         }
     }
 }
Beispiel #2
0
        /// <summary>
        /// Draw some debug information like the chain bones
        /// </summary>
        /// <param name="g">the graphic context in which draw</param>
        /// <param name="areaInStud">the area in which draw</param>
        /// <param name="scalePixelPerStud">the current scale</param>
        public void draw(Graphics g, RectangleF areaInStud, double scalePixelPerStud)
        {
            int lastIndex = mBoneList.Count - 1;

            // draw the bones
            for (int i = 0; i < lastIndex; i++)
            {
                IKSolver.Bone_2D_CCD firstBone = mBoneList[i];
                IKSolver.Bone_2D_CCD nextBone  = mBoneList[i + 1];

                g.DrawLine(Pens.Black, (float)((firstBone.worldX - areaInStud.Left) * scalePixelPerStud),
                           (float)((-firstBone.worldY - areaInStud.Top) * scalePixelPerStud),
                           (float)((nextBone.worldX - areaInStud.Left) * scalePixelPerStud),
                           (float)((-nextBone.worldY - areaInStud.Top) * scalePixelPerStud));
            }

            // the last bone vector
            LayerBrick.Brick.ConnectionPoint endConnection = mBoneList[lastIndex].connectionPoint;
            if (!endConnection.IsFree)
            {
                // rotate the second target vector according to the orientation of the snapped connection
                PointF[] translation = { mLastBoneVector };
                Matrix   rotation    = new Matrix();
                rotation.Rotate(endConnection.ConnectedBrick.Orientation + endConnection.ConnectionLink.Angle + 180);
                rotation.TransformVectors(translation);

                // draw the translation
                IKSolver.Bone_2D_CCD lastBone = mBoneList[mBoneList.Count - 1];
                float endX = (float)(lastBone.worldX - areaInStud.Left);
                float endY = (float)(-lastBone.worldY - areaInStud.Top);
                g.DrawLine(Pens.Green, endX * (float)scalePixelPerStud, endY * (float)scalePixelPerStud,
                           (endX + translation[0].X) * (float)scalePixelPerStud,
                           (endY + translation[0].Y) * (float)scalePixelPerStud);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Try to reach the specify target position with this chain
        /// </summary>
        /// <param name="targetInWorldStudCoord">the target position in world stud coord</param>
        /// <param name="targetConnection">If the end of the flex chain need to be connected to a connection, this parameter indicates which one, otherwise it is null</param>
        public void reachTarget(PointF targetInWorldStudCoord, LayerBrick.Brick.ConnectionPoint targetConnection)
        {
            // save the primary target
            mPrimaryTarget = targetInWorldStudCoord;

            // check if we need to compute a second target position
            mUseTwoTargets = (targetConnection != null);
            if (mUseTwoTargets)
            {
                // rotate the second target vector according to the orientation of the snapped connection
                PointF[] translation = { mLastBoneVector };
                Matrix   rotation    = new Matrix();
                rotation.Rotate(targetConnection.mMyBrick.Orientation + targetConnection.Angle + 180);
                rotation.TransformVectors(translation);

                // translate the target with the rotated vector for the second target
                mSecondaryTarget.X = targetInWorldStudCoord.X + translation[0].X;
                mSecondaryTarget.Y = targetInWorldStudCoord.Y + translation[0].Y;
            }

            // reset the status flag
            mCurrentUpdateStatus = IKSolver.CCDResult.Processing;

            // and call the update method
            update();
        }
Beispiel #4
0
        /// <summary>
        /// The constructor should be called before starting to update the flex chain,
        /// because the action save the initial state of the bricks in the constructor
        /// This constructor Create a flex chain from a set of brick given a starting brick. From the best connection point
        /// of this starting brick go through all the linked connection points while the brick only have
        /// two connections. If the brick has only one or more than 2 connection (like a joint or crossing)
        /// we stop the chain.
        /// </summary>
        /// <param name="layer">The layer on which the flex move is performed</param>
        /// <param name="trackList">A set of track hopefully connected together, and hopefully containing flex track</param>
        /// <param name="grabbedTrack">The part which will try to reach the target. It should be part of the list.</param>
        /// <param name="mouseCoordInStud">the coordinate of the mouse in stud coord</param>
        public FlexMove(LayerBrick layer, List <Layer.LayerItem> trackList, LayerBrick.Brick grabbedTrack, PointF mouseCoordInStud)
        {
            // first we check if we can create a valid flex chain
            mIsValid = false;
            // check that the grabbed part is a part has 1 or 2 connection points (no more no less)
            if (!grabbedTrack.HasConnectionPoint || grabbedTrack.ConnectionPoints.Count > 2)
            {
                return;
            }

            // if the grabbed part is not in the list, it failed
            if (!trackList.Contains(grabbedTrack))
            {
                return;
            }

            // declare a variable for the starting connection, we will pass it to the flex creation
            // this variable can stay null, in that case the middle of the grabbed part will be used instead
            LayerBrick.Brick.ConnectionPoint startConnection = null;

            // declare a variable to know from which connection to start the flex chain
            if (grabbedTrack.ConnectionPoints.Count == 1)
            {
                // if there's only one connection, if must be a flexible one, and
                // the starting connection will be the brick itself
                // and the only connection will be the second one from which starting the chain
                if (BrickLibrary.Instance.getConnexionHingeAngle(grabbedTrack.ConnectionPoints[0].Type) == 0.0f)
                {
                    return;
                }
            }
            else
            {
                // try to find the starting connection point, if the result is null, the flex chain cannot be created
                startConnection = findStartingConnectionPoint(trackList, grabbedTrack, mouseCoordInStud);
                if (startConnection == null)
                {
                    return;
                }
            }

            // if we pass all the tests, the flex move is valid
            mIsValid = true;

            // init other data member
            mBrickLayer = layer;
            //create the edition data
            ceateFlexChain(trackList, grabbedTrack, startConnection);
            // and save the action data
            recordState(ref mInitState);

            // also save the current selection, because we will only select the flex brick for visual feedback
            // Do not save directly the list object because we will modified the selection
            mSelectedBricksBeforeFlexMove = new List <Layer.LayerItem>(layer.SelectedObjects);
            // now clear the slection and select only the bricks in the flex chain
            layer.unsafeSetSelection(mBricksInTheFlexChain);
        }
Beispiel #5
0
 private void addNewBone(LayerBrick.Brick.ConnectionPoint connection, LayerBrick.Brick brick, double maxAngleInDeg)
 {
     IKSolver.Bone_2D_CCD newBone = new IKSolver.Bone_2D_CCD();
     newBone.maxAbsoluteAngleInRad = maxAngleInDeg * (Math.PI / 180);
     if (connection != null)
     {
         newBone.worldX = connection.PositionInStudWorldCoord.X;
         newBone.worldY = -connection.PositionInStudWorldCoord.Y;                 // BlueBrick use an indirect coord sys, and the IKSolver a direct one
     }
     else
     {
         newBone.worldX = brick.Center.X;
         newBone.worldY = -brick.Center.Y;                 // BlueBrick use an indirect coord sys, and the IKSolver a direct one
     }
     newBone.connectionPoint = connection;
     mBoneList.Insert(0, newBone);
 }
Beispiel #6
0
 public ChainLink(LayerBrick.Brick.ConnectionPoint firstConnection, LayerBrick.Brick.ConnectionPoint secondConnection)
 {
     mFirstConnection  = firstConnection;
     mSecondConnection = secondConnection;
     if (firstConnection != null)
     {
         int firstIndex  = firstConnection.mMyBrick.ConnectionPoints.IndexOf(firstConnection);
         int secondIndex = secondConnection.mMyBrick.ConnectionPoints.IndexOf(secondConnection);
         mAngleBetween = BrickLibrary.Instance.getConnectionAngleDifference(secondConnection.mMyBrick.PartNumber, secondIndex,
                                                                            firstConnection.mMyBrick.PartNumber, firstIndex);
     }
     else
     {
         // if the first connection point is null, create a dummy connection point in the world,
         // at the position of the second connection and not attached to a brick.
         // The angle between is in that case of course null
         mFirstConnection = new LayerBrick.Brick.ConnectionPoint(secondConnection.PositionInStudWorldCoord);
     }
 }
Beispiel #7
0
        private void moveToConnect(LayerBrick.Brick.ConnectionPoint connectionToUse)
        {
            // get the owner of the connection
            LayerBrick.Brick brickToConnect = connectionToUse.mMyBrick;
            // memorise the previous position to compute the shift
            PointF oldPosition = brickToConnect.Position;

            // set the position of the brick via its connection point
            connectionToUse.PositionInStudWorldCoord = mConnexionPosition;
            // compute the shift
            PointF deltaMove = brickToConnect.Position;

            deltaMove.X -= oldPosition.X;
            deltaMove.Y -= oldPosition.Y;
            // move all the other bricks
            foreach (Layer.LayerItem item in mItems)
            {
                if (item != brickToConnect)
                {
                    item.Position = new PointF(item.Position.X + deltaMove.X, item.Position.Y + deltaMove.Y);
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// compute the position and orientation of all the bricks along the Flex Chain
        /// based on the angle of each rotationable (flexible) connection point
        /// </summary>
        private void computeBrickPositionAndOrientation()
        {
            // start with the first bone of the list
            int boneIndex = 0;

            IKSolver.Bone_2D_CCD currentBone = mBoneList[boneIndex];
            // start with a null total flexible orientation that we will increase with the angle of every bone
            float flexibleCumulativeOrientation = 0.0f;
            // init the static cumulative orientation with the one saved in this class.
            // we cannot use the orientation of the root brick of the chain because this brick orientation
            // is also changed in the loop, leading to some divergence
            float staticCumulativeOrientation = mInitialStaticCumulativeOrientation;

            // iterate on the link list and change the world angle everytime we meet an hinge
            // start with the first hinged connection of the chain (because everything before doesn't move)
            for (int linkIndex = mRootHingedLinkIndex; linkIndex < mFlexChainList.Count; ++linkIndex)
            {
                // get the previous and current brick
                ChainLink currentLink = mFlexChainList[linkIndex];
                LayerBrick.Brick.ConnectionPoint previousConnection = currentLink.mFirstConnection;
                PointF previousPosition = previousConnection.PositionInStudWorldCoord;
                LayerBrick.Brick.ConnectionPoint currentConnection = currentLink.mSecondConnection;
                LayerBrick.Brick currentBrick = currentConnection.mMyBrick;

                // check if we reach an hinge connection
                if (currentConnection == currentBone.connectionPoint)
                {
                    // set the new world position to the current bone with the previous connection position
                    // because the previous brick was already placed at the correct position
                    currentBone.worldX = previousPosition.X;
                    currentBone.worldY = -previousPosition.Y;                     // BlueBrick use an indirect coord sys, and the IKSolver a direct one
                    // increase the flexible angle
                    flexibleCumulativeOrientation += (float)(currentBone.localAngleInRad * (180.0 / Math.PI));
                    // take the next bone
                    boneIndex++;
                    if (boneIndex < mBoneList.Count)
                    {
                        currentBone = mBoneList[boneIndex];
                    }
                }

                // add the difference of orientation between the previous brick and current brick through their linked connections
                staticCumulativeOrientation += currentLink.mAngleBetween;

                // set the orientation of the current brick
                currentBrick.Orientation = -flexibleCumulativeOrientation - staticCumulativeOrientation;

                // compute the new position of the current brick by putting the current connection at the same
                // place than the previous connection
                PointF newBrickPosition = currentBrick.Position;
                newBrickPosition.X   += previousPosition.X - currentConnection.PositionInStudWorldCoord.X;
                newBrickPosition.Y   += previousPosition.Y - currentConnection.PositionInStudWorldCoord.Y;
                currentBrick.Position = newBrickPosition;
            }

            // update the last bone position
            if (mBoneList.Count > 0)
            {
                int lastIndex = mBoneList.Count - 1;
                // get the last position
                PointF lastPosition;
                if (mBoneList[lastIndex].connectionPoint != null)
                {
                    lastPosition = mBoneList[lastIndex].connectionPoint.PositionInStudWorldCoord;
                }
                else
                {
                    lastPosition = mBoneList[lastIndex - 1].connectionPoint.mMyBrick.Center;
                }
                // and set it in the last bone
                mBoneList[lastIndex].worldX = lastPosition.X;
                mBoneList[lastIndex].worldY = -lastPosition.Y;                 // BlueBrick use an indirect coord sys, and the IKSolver a direct one
            }

            // update the bounding rectangle and connectivity
            mBrickLayer.updateBoundingSelectionRectangle();
        }
Beispiel #9
0
        /// <summary>
        /// Create a flex chain from a set of brick given a starting brick. From the free connection point
        /// of this starting brick go through all the linked connection points while the brick only have
        /// two connections. If the brick has only one or more than 2 connection (like a joint or crossing)
        /// we stop the chain.
        /// </summary>
        /// <param name="trackList">A set of track hopefully connected together, and hopefully containing flex track</param>
        /// <param name="grabbedTrack">The part which will try to reach the target. It should be part of the list.</param>
        /// <param name="currentFirstConnection">The first connection from which to start, which can be null and belongs to the grabbed track</param>
        public void ceateFlexChain(List <Layer.LayerItem> trackList, LayerBrick.Brick grabbedTrack, LayerBrick.Brick.ConnectionPoint currentFirstConnection)
        {
            // init all the arrays
            int boneCount = trackList.Count;

            mBoneList             = new List <IKSolver.Bone_2D_CCD>(boneCount);
            mFlexChainList        = new List <ChainLink>(boneCount * 2);
            mBricksInTheFlexChain = new List <Layer.LayerItem>(boneCount);

            // start to iterate from the grabbed track
            LayerBrick.Brick currentBrick = grabbedTrack;
            ChainLink        hingedLink   = null;

            addNewBone(currentFirstConnection, grabbedTrack, 0.0);
            // continue to iterate until we reach the end (no current brick) or the end of the selection
            // and continue only if the chain is made with track that exclusively has 2 connections to make a chain
            // (or one for the first iteration).
            while ((currentBrick != null) && trackList.Contains(currentBrick) &&
                   ((currentFirstConnection == null) || (currentBrick.ConnectionPoints.Count == 2)))
            {
                // get the other connection on the current brick
                int secondIndex = (currentBrick.ConnectionPoints[0] == currentFirstConnection) ? 1 : 0;
                LayerBrick.Brick.ConnectionPoint currentSecondConnection = currentBrick.ConnectionPoints[secondIndex];
                LayerBrick.Brick nextBrick = currentSecondConnection.ConnectedBrick;
                LayerBrick.Brick.ConnectionPoint nextFirstConnection = currentSecondConnection.ConnectionLink;

                // add the two connections of the brick
                ChainLink link = new ChainLink(nextFirstConnection, currentSecondConnection);
                mFlexChainList.Insert(0, link);

                // check if the connection can rotate (if it is an hinge)
                float hingeAngle = BrickLibrary.Instance.getConnexionHingeAngle(currentSecondConnection.Type);
                if (hingeAngle != 0.0f)
                {
                    // advance the hinge conncetion
                    hingedLink = link;
                    // add the link in the list
                    addNewBone(currentSecondConnection, currentBrick, hingeAngle);
                    // compute the current angle between the hinge connection and set it to the current bone
                    // to do that we use the current angle between the connected brick and remove the static angle between them
                    // to only get the flexible angle.
                    float angleInDegree = 0.0f;
                    if (nextBrick != null)
                    {
                        angleInDegree = simplifyAngle(nextBrick.Orientation - currentBrick.Orientation - link.mAngleBetween);
                    }
                    mBoneList[0].localAngleInRad = angleInDegree * (Math.PI / 180);

                    // save the initial static cumulative orientation: start with the orientation of the root brick
                    // if the hinge is connected to a brick, otherwise, if the hinge is free (connected to the world)
                    // use the orientation of the hinge brick.
                    // we set the value several time in the loop, in order to set it with the brick directly
                    // connected to the last hinge in the chain
                    if (nextBrick != null)
                    {
                        mInitialStaticCumulativeOrientation = -nextBrick.Orientation;
                    }
                    else
                    {
                        mInitialStaticCumulativeOrientation = -currentBrick.Orientation;
                    }
                }

                // advance to the next link
                currentBrick           = nextBrick;
                currentFirstConnection = nextFirstConnection;

                // check if the track is not a loop, otherwise we will have an infinite loop.
                // but don't add the test in the while because the first iteration must pass
                if (currentBrick == grabbedTrack)
                {
                    break;
                }
            }

            // store the root hinge connection index (the last hinge found is the flexible root)
            if (hingedLink != null)
            {
                mRootHingedLinkIndex = mFlexChainList.IndexOf(hingedLink);
            }

            // compute the last bone vector if there is enough bones
            if (mBoneList.Count > 2)
            {
                int lastIndex       = mBoneList.Count - 1;
                int beforeLastIndex = lastIndex - 1;
                mLastBoneVector = new PointF((float)(mBoneList[beforeLastIndex].worldX - mBoneList[lastIndex].worldX),
                                             (float)(mBoneList[beforeLastIndex].worldY - mBoneList[lastIndex].worldY));
                // rotate the vector such as to compute it for a null angle
                // but if the last bone doesn't have connection, it can never snap, so the last bone will never be used
                if (mBoneList[lastIndex].connectionPoint != null)
                {
                    PointF[] translation = { mLastBoneVector };
                    Matrix   rotation    = new Matrix();
                    rotation.Rotate(mBoneList[lastIndex].connectionPoint.mMyBrick.Orientation);
                    rotation.TransformVectors(translation);
                    mLastBoneVector = translation[0];
                }
            }

            // add the current brick in the list of bricks, by not going further than the root brick
            LayerBrick.Brick.ConnectionPoint rootConnection = mFlexChainList[mRootHingedLinkIndex].mFirstConnection;
            if (rootConnection.mMyBrick != null)
            {
                mBricksInTheFlexChain.Add(rootConnection.mMyBrick);
            }
            for (int i = mRootHingedLinkIndex; i < mFlexChainList.Count; ++i)
            {
                mBricksInTheFlexChain.Add(mFlexChainList[i].mSecondConnection.mMyBrick);
            }
        }
Beispiel #10
0
        /// <summary>
        /// This function try different strategy to find the best connection point to use on the specified grabbed part
        /// to start the flex chain. It is assumed that the grab part only have two connection, then:
        /// 1) if one connection is flexible and not the other, return this one,
        /// 2) else if one connection leads to a flexible connection and not the other, return this one
        /// 3) else if one connection is connected to a brick part of the track list and not the other, return this one,
        /// 4) else if both connections are or lead to a flexible one, return the connection closest to the mouse click
        /// 5) else if no connection are or lead to a flexible one, return null (no flexible chain can be created)
        /// </summary>
        /// <param name="trackList">A set of track hopefully connected together, and hopefully containing flex track</param>
        /// <param name="grabbedTrack">The part which will try to reach the target. It should be part of the list.</param>
        /// <param name="mouseCoordInStud">the coordinate of the mouse in stud coord</param>
        /// <returns>The best connection point of the grabbed part or null</returns>
        private LayerBrick.Brick.ConnectionPoint findStartingConnectionPoint(List <Layer.LayerItem> trackList, LayerBrick.Brick grabbedTrack, PointF mouseCoordInStud)
        {
            // try to find the best first connection point which will be the end target of the flex.
            LayerBrick.Brick.ConnectionPoint grabbedFirstConnection  = grabbedTrack.ConnectionPoints[0];
            LayerBrick.Brick.ConnectionPoint grabbedSecondConnection = grabbedTrack.ConnectionPoints[1];
            // by preference choose the not flexible connection on the grabbed part
            float firstHingeAngle  = BrickLibrary.Instance.getConnexionHingeAngle(grabbedFirstConnection.Type);
            float secondHingeAngle = BrickLibrary.Instance.getConnexionHingeAngle(grabbedSecondConnection.Type);

            if (firstHingeAngle != 0.0f && secondHingeAngle == 0.0f)
            {
                return(grabbedSecondConnection);
            }
            else if (firstHingeAngle == 0.0f && secondHingeAngle != 0.0f)
            {
                return(grabbedFirstConnection);
            }
            else if (firstHingeAngle == 0.0f && secondHingeAngle == 0.0f)
            {
                // neither connection are flexibles on the grab part, so we choose the connection that
                // do not lead to a flexible part (and the other connection does)
                bool firstConnectionLeadToFlexiblePart  = false;
                bool secondConnectionLeadToFlexiblePart = false;
                LayerBrick.Brick.ConnectionPoint firstConnectionLead  = grabbedFirstConnection;
                LayerBrick.Brick.ConnectionPoint secondConnectionLead = grabbedSecondConnection;
                // use a clone of the track list because we will remove the tested track to avoid infinite loop
                // in case of a close track chain
                List <Layer.LayerItem> remaingTrackToTestList = new List <Layer.LayerItem>(trackList);
                while ((!firstConnectionLeadToFlexiblePart || !secondConnectionLeadToFlexiblePart) &&
                       (firstConnectionLead != null || secondConnectionLead != null))
                {
                    // check if the leading bricks are in the trask list otherwise stop to follow the lead
                    LayerBrick.Brick firstBrickLead  = (firstConnectionLead != null) ? firstConnectionLead.mMyBrick : null;
                    LayerBrick.Brick secondBrickLead = (secondConnectionLead != null) ? secondConnectionLead.mMyBrick : null;
                    if (!remaingTrackToTestList.Contains(firstBrickLead))
                    {
                        firstConnectionLead = null;
                    }
                    if (!remaingTrackToTestList.Contains(secondBrickLead))
                    {
                        secondConnectionLead = null;
                    }
                    // remove those check brick from the list. Do it after and not in the if because first and second brick
                    // may be the same and flexible, so it could be unfair to the second test
                    remaingTrackToTestList.Remove(firstBrickLead);
                    remaingTrackToTestList.Remove(secondBrickLead);
                    // set the flag and follow the lead for the two leads
                    followConnectionToFindFlexiblePart(ref firstConnectionLead, ref firstConnectionLeadToFlexiblePart);
                    followConnectionToFindFlexiblePart(ref secondConnectionLead, ref secondConnectionLeadToFlexiblePart);
                }
                // either the loop is finished because we found flexible part on both side, or because we
                // finished to explore the two paths. Check if we have only one side leading to a flexible part
                // and choose the other one, otherwise go to the default method
                if (firstConnectionLeadToFlexiblePart && !secondConnectionLeadToFlexiblePart)
                {
                    return(grabbedSecondConnection);
                }
                else if (!firstConnectionLeadToFlexiblePart && secondConnectionLeadToFlexiblePart)
                {
                    return(grabbedFirstConnection);
                }
                else if (!firstConnectionLeadToFlexiblePart && !secondConnectionLeadToFlexiblePart)
                {
                    return(null);                    // neither connection lead to a flexible part
                }
            }

            // both connection of the grab part are flexibles, or both side of the grab part
            // lead to a flexible part, so we choose the connection which neighboor is not in the list
            bool isGrabbedFirstNeighborInList  = trackList.Contains(grabbedFirstConnection.ConnectedBrick);
            bool isGrabbedSecondNeighborInList = trackList.Contains(grabbedSecondConnection.ConnectedBrick);

            if (isGrabbedFirstNeighborInList && !isGrabbedSecondNeighborInList)
            {
                return(grabbedSecondConnection);
            }
            else if (!isGrabbedFirstNeighborInList && isGrabbedSecondNeighborInList)
            {
                return(grabbedFirstConnection);
            }
            else
            {
                // both or neither neighboor are in the list, so we choose the closest connection to the mouse
                PointF distToFirstConnection        = new PointF(grabbedFirstConnection.PositionInStudWorldCoord.X - mouseCoordInStud.X, grabbedFirstConnection.PositionInStudWorldCoord.Y - mouseCoordInStud.Y);
                PointF distToSecondConnection       = new PointF(grabbedSecondConnection.PositionInStudWorldCoord.X - mouseCoordInStud.X, grabbedSecondConnection.PositionInStudWorldCoord.Y - mouseCoordInStud.Y);
                float  squareDistToFirstConnection  = (distToFirstConnection.X * distToFirstConnection.X) + (distToFirstConnection.Y * distToFirstConnection.Y);
                float  squareDistToSecondConnection = (distToSecondConnection.X * distToSecondConnection.X) + (distToSecondConnection.Y * distToSecondConnection.Y);
                if (squareDistToFirstConnection < squareDistToSecondConnection)
                {
                    return(grabbedFirstConnection);
                }
                else
                {
                    return(grabbedSecondConnection);
                }
            }
        }
Beispiel #11
0
 public void remove(LayerBrick.Brick.ConnectionPoint connection)
 {
     mFreeConnections[connection.Type].Remove(connection);
 }
Beispiel #12
0
 public void add(LayerBrick.Brick.ConnectionPoint connection)
 {
     mFreeConnections[connection.Type].Add(connection);
 }
        /// <summary>
        /// This function check all the electric circuit connected to the specified brick, starting from
        /// the specified brick. The startingBrick must have an ElectricCircuitIndexList not null.
        /// </summary>
        /// <param name="startingBrick">The brick to start from.</param>
        private static void checkFromOneBrick(LayerBrick.Brick startingBrick)
        {
            // the list of all the shortcut found
            List <LayerBrick.Brick.ConnectionPoint> shortcuts = new List <LayerBrick.Brick.ConnectionPoint>();

            // clear the list and add the first node
            mBricksToExplore.Clear();
            mBricksToExplore.Add(startingBrick);
            // init the first connection of the starting brick with the new timestamp
            LayerBrick.Brick.ConnectionPoint firstConnection = startingBrick.ConnectionPoints[startingBrick.ElectricCircuitIndexList[0].mIndex1];
            firstConnection.Polarity = sTimeStamp;
            // if the first connection is connected to another brick, also add this brick in the list
            if (firstConnection.ConnectionLink != null)
            {
                firstConnection.ConnectionLink.Polarity = (short)(-sTimeStamp);
                mBricksToExplore.Add(firstConnection.ConnectedBrick);
            }

            //explore while the list is not empty
            while (mBricksToExplore.Count > 0)
            {
                // pop the first node of the list
                LayerBrick.Brick brick = mBricksToExplore[0];
                mBricksToExplore.RemoveAt(0);

                // declare a boolean variable to check if during the exploration of all the circuits of the
                // brick, one was ignore. If yes and later we transfert electricity on the brick, we will
                // have to re-explore the brick for checking again the ignored circuits
                bool needToReexploreTheBrick = false;

                // iterate on all the brick connection
                if (brick.ElectricCircuitIndexList != null)
                {
                    foreach (BrickLibrary.Brick.ElectricCircuit circuit in brick.ElectricCircuitIndexList)
                    {
                        // get the connection point of the current circuit inside the brick
                        LayerBrick.Brick.ConnectionPoint start = brick.ConnectionPoints[circuit.mIndex1];
                        LayerBrick.Brick.ConnectionPoint end   = brick.ConnectionPoints[circuit.mIndex2];

                        // check wich one has the incoming electricity, if it's not the start,
                        // swap the two connections point in order to have only one algorithm in the following
                        // after this swap, normally we should have the start +/-timestamp
                        if (Math.Abs(end.Polarity) == sTimeStamp)
                        {
                            LayerBrick.Brick.ConnectionPoint swap = start;
                            start = end;
                            end   = swap;
                        }

                        // transfert the time stamp from the start to the end
                        if (Math.Abs(start.Polarity) == sTimeStamp)
                        {
                            // check if we have a shortcut in the current circuit.
                            // If the end is already set with the same polarity than the start, we have a shortcut
                            if (end.Polarity == start.Polarity)
                            {
                                // shortcut!!
                                shortcuts.Add(start);
                            }
                            // else if no shorcut check if we didn't already transfer the electricity to the end
                            else if (end.Polarity != -start.Polarity)
                            {
                                // the end connection was not explored yet, so we will set its timestamp and push
                                // his neighbor for future exploration.

                                // transfert the electricity to the end
                                end.Polarity = (short)(-start.Polarity);

                                // for complex part that has several circuit in it, if we transfert electricity
                                // in a circuit, this circuit may also be linked to a previous one already iterated.
                                // This is the case of the rail jonction point. The circuit 0 is for example the
                                // straight line, whereas circuit 1, is the curved derivation. If the current comes
                                // from circuit 1, then the circuit 0 was already ignored and must be re-tested.
                                // So we reinsert the part if some circuit werz ignored on this part
                                if (needToReexploreTheBrick)
                                {
                                    mBricksToExplore.Insert(0, brick);
                                    needToReexploreTheBrick = false;
                                }

                                // add the neighbor if any
                                LayerBrick.Brick.ConnectionPoint connectionLink = end.ConnectionLink;
                                if (connectionLink != null)
                                {
                                    // check if we have a shortcut
                                    if (connectionLink.Polarity == end.Polarity)
                                    {
                                        // shortcut!!
                                        shortcuts.Add(end);
                                    }
                                    // if no shortcut, check if we have to to explore the connection
                                    else if (connectionLink.Polarity != -end.Polarity)
                                    {
                                        // transfert the polarity to the linked connection
                                        connectionLink.Polarity = (short)(-end.Polarity);
                                        // and add the new brick in the list for furture exploration
                                        mBricksToExplore.Add(end.ConnectedBrick);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // no electric current in this circuit. This may be the case with a part
                            // with different independant circuit.
                            // Maybe the circuits are linked, maybe they are independant, anyway, we
                            // check the flag to tell that the brick need to be re-explored
                            needToReexploreTheBrick = true;
                        }
                    }
                }
            }

            // set the polarity to 0 for all the connection where we found the shortcuts
            foreach (LayerBrick.Brick.ConnectionPoint connection in shortcuts)
            {
                connection.HasElectricShortcut = true;
            }
        }
Beispiel #14
0
 public ConnectionAndIndex(LayerBrick.Brick.ConnectionPoint connection, int index)
 {
     mConnection      = connection;
     mIndexInTheGroup = index;
 }
Beispiel #15
0
        public RotateBrick(LayerBrick layer, List <Layer.LayerItem> bricks, int rotateSteps, bool forceKeepLastCenter)
        {
            // compute the default rotation angle, used if no bricks in the list is connected to something else
            float angle = MapData.Layer.CurrentRotationStep * rotateSteps;

            // now check if we can find any brick in the list which is connected to another
            // brick not in the list: in that case we don't care about the rotation step,
            // we rotate the group of brick such as it can connect with its next connexion point.
            // we do this first because maybe it will invalidate the flag sLastCenterIsValid

            // first we gather all the free connections or those linked with bricks outside of the list of brick
            FreeConnectionSet externalConnectionSet  = new FreeConnectionSet();            // all the connection connected to external bricks
            FreeConnectionSet availableConnectionSet = new FreeConnectionSet();            // all the possible connections that can be used to link to one external brick

            foreach (Layer.LayerItem item in bricks)
            {
                LayerBrick.Brick brick = item as LayerBrick.Brick;
                if (brick.HasConnectionPoint)
                {
                    foreach (LayerBrick.Brick.ConnectionPoint connection in brick.ConnectionPoints)
                    {
                        if (connection.IsFree)
                        {
                            availableConnectionSet.add(connection);
                        }
                        else if (!bricks.Contains(connection.ConnectionLink.mMyBrick))
                        {
                            availableConnectionSet.add(connection);
                            externalConnectionSet.add(connection);
                        }
                    }
                }
            }

            // get the biggest group of external connection among all the available types, and also get its type
            int chosenConnexionType = BrickLibrary.ConnectionType.DEFAULT;
            List <LayerBrick.Brick.ConnectionPoint> externalConnectionList = externalConnectionSet.getBiggestList(out chosenConnexionType);

            // check if there is any external connection on which we should rotate
            if (externalConnectionList.Count > 0)
            {
                // in that case we don't use the static center
                sLastCenterIsValid = false;

                // for now, without a lot of imagination, take the first connection of the list
                mOldConnectionPoint = externalConnectionList[0];

                // store the connection position
                mConnexionPosition = mOldConnectionPoint.PositionInStudWorldCoord;

                // get the fixed brick, the external brick on the other side of the chosen connection
                LayerBrick.Brick fixedBrick   = mOldConnectionPoint.ConnectedBrick;
                int fixedBrickConnectionIndex = mOldConnectionPoint.ConnectionLink.Index;

                // get the same list but for available connections
                List <LayerBrick.Brick.ConnectionPoint> availableConnectionList = availableConnectionSet.getListForType(chosenConnexionType);

                // check in which direction and how many connection we should jump
                bool rotateCW = (rotateSteps < 0);
                int  nbSteps  = Math.Abs(rotateSteps);

                // get the index of the chosen connection in the available connection list
                int index = availableConnectionList.IndexOf(mOldConnectionPoint);
                // start from it then count forward or backward a certain number of connections
                // depending on the number of steps and the rotation direction
                if (rotateCW)
                {
                    index -= (nbSteps % availableConnectionList.Count);
                    if (index < 0)
                    {
                        index += availableConnectionList.Count;
                    }
                }
                else
                {
                    index += (nbSteps % availableConnectionList.Count);
                    if (index >= availableConnectionList.Count)
                    {
                        index -= availableConnectionList.Count;
                    }
                }
                // finally get the new connection from the chosen index
                mNewConnectionPoint = availableConnectionList[index];

                // compute the angle to rotate
                LayerBrick.Brick newConnectedBrick = mNewConnectionPoint.mMyBrick;
                angle = AddConnectBrick.sGetOrientationOfConnectedBrick(fixedBrick, fixedBrickConnectionIndex,
                                                                        newConnectedBrick, mNewConnectionPoint.Index) - newConnectedBrick.Orientation;
            }

            // then call the normal constructor
            commonConstructor(layer, bricks, angle, forceKeepLastCenter);
        }