/// <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; } } } } }
/// <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); } }
/// <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(); }
/// <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); }
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); }
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); } }
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); } } }
/// <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(); }
/// <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); } }
/// <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); } } }
public void remove(LayerBrick.Brick.ConnectionPoint connection) { mFreeConnections[connection.Type].Remove(connection); }
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; } }
public ConnectionAndIndex(LayerBrick.Brick.ConnectionPoint connection, int index) { mConnection = connection; mIndexInTheGroup = index; }
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); }