public void SetUp() { chains = transform.GetComponentsInChildren <ChainLink>().ToList(); firstLink = chains[0]; LastLink = chains[chains.Count - 1]; length = chains.Count; }
/// <summary> /// Adds a ValueController to the ValueControllerChain. /// </summary> /// <param name="layer">The layer of the ValueController</param> /// <param name="valueController">The ValueController.</param> /// <param name="setterDelegate">The setter delegate.</param> /// <remarks>ValueController on the same layer are started simultaneously.</remarks> /// <exception cref="ArgumentNullException">Argument is null.</exception> public void AddValueController(int layer, ValueController <T> valueController, SetterDelegate <T> setterDelegate) { if (this.commonSetterDelegate == null && setterDelegate == null) { throw new ArgumentNullException("setterDelegate"); } var vcce = new ChainLink <T> { ValueController = valueController, SetterDelegate = setterDelegate }; if (layer > this.maxLayer) { this.maxLayer = layer; } if (!this.chainLinkLists.ContainsKey(layer)) { this.chainLinkLists[layer] = new List <ChainLink <T> >(); } this.chainLinkLists[layer].Add(vcce); }
static void Main() { AttachConsole(ATTACH_PARENT_PROCESS); ChainLink.InitializeResourceLocation(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
public static void Main(string[] args) { ChainLink left = new ChainLink(); ChainLink middle = new ChainLink(); ChainLink right = new ChainLink(); left.Append(middle); middle.Append(right); Console.WriteLine(left.LongerSide()); }
public void Append(ChainLink rightPart) { if (this.Right != null) { throw new InvalidOperationException("Link is already connected."); } this.Right = rightPart; rightPart.Left = this; }
private static IReadOnlyList <ChainLink> GetChains(ChainLink chain) { var list = new List <ChainLink> { chain }; foreach (var evolves_to in chain.EvolvesTo) { list.AddRange(GetChains(evolves_to)); } return(list); }
static void Main(string[] args) { new int[0].Select(x => { return (new ChainLink <int, IObject>(e => false, e => new CustomerRequest(), new ChainLink <int, IObject>(Singleton.Instance, e => new AlternativeRequest(e), new ChainLink <int, IObject>(e => false, e => new VoidRequest() ))).Transform(int.MinValue, new VoidRequest())); }) .OfType <IObject>(); new ChainLink <int>(e => false, e => { }, new ChainLink <int>(new Specification(), e => e.ToString() )).Process(int.MinValue, e => { }); var @object = new ChainLink <int, IObject>(e => false, e => new CustomerRequest(), new ChainLink <int, IObject>(Singleton.Instance, e => new AlternativeRequest(e), new ChainLink <int, IObject>(e => false, e => new VoidRequest()) )).Transform(int.MinValue, new VoidRequest()); // Customers requests var request1 = new CustomerRequest() { DesiredSpeed = 10 }; var request2 = new CustomerRequest() { DesiredSpeed = 30 }; var request3 = new CustomerRequest() { DesiredSpeed = 70 }; var requests = new CustomerRequest[] { request1, request2, request3 }; Selector baseSelector = new MB20Selector(); Selector mb40 = new MB40Selector(); Selector mb60 = new MB60Selector(); baseSelector.Successor = mb40; mb40.Successor = mb60; foreach (var request in requests) { baseSelector.ProcessRequest(request); Console.WriteLine("******************************"); } Console.ReadKey(); }
public Form1() { Form.CheckForIllegalCrossThreadCalls = false; InitializeComponent(); CurrentLink = new ChainLink(0.2); ColorPickerCache.Add(Color.FromArgb(238, 238, 238)); ColorPickerCache.Add(Color.FromArgb(0, 168, 254)); ColorPickerCache.Add(Color.FromArgb(68, 68, 68)); ColorPickerCache.Add(Color.FromArgb(220, 55, 55)); PathBox.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Linkies"); }
/// <summary> /// Initializes a new instance of the <see cref="WaitTimeList{TElement}"/> class. /// After creation the object is not added to the queue. /// </summary> /// <param name="queue">The queue.</param> /// <param name="cycle">The cycle.</param> /// <exception cref="ArgumentNullException">queue is a null reference.</exception> /// <remarks>After creation the object is not added to the queue.</remarks> public TODescriptor(WaitTimeList <TElement> queue, TimeSpan cycle) { myQueue = queue; if (queue == null) { throw new ArgumentNullException("queue in TODescriptor cannot be null"); } if (cycle.TotalMilliseconds == 0) { throw new ArgumentNullException("cycle in TODescriptor has to specify TimeSpan > 0 "); } lock (myQueue) myChainLink = new ChainLink(myQueue, (TElement)this, cycle); }
// Use this for initialization void Start() { links = new List <ChainLink>(); float length = fullLength; while (length > 0) { ChainLink l = new ChainLink(); l.length = Mathf.Min(segmentLength, length); l.position = (Vector2)transform.position + Vector2.down * length; links.Insert(0, l); length -= segmentLength; } }
public override void _Ready() { var width = (EndPosition.x - StartPosition.x) / (LinkCount + 1); var height = (EndPosition.y - StartPosition.y) / (LinkCount + 1); AddChild(linksContainer); AddChild(circleContainer); startAnchor = new ChainAnchor() { Position = StartPosition }; circleContainer.AddChild(startAnchor); PhysicsBody2D lastLink = startAnchor; for (int i = 0; i < LinkCount; ++i) { var link = new ChainLink() { Position = StartPosition + new Vector2(width + (i * width), height + (i * height)) }; linksContainer.AddChild(link); link.LinkToParent(lastLink, Softness, Bias); links.Add(link); var lineSprite = new SimpleLineSprite(); circleContainer.AddChild(lineSprite); lineSprite.PositionA = lastLink.Position; lineSprite.PositionB = link.Position; lineSprites.Add(lineSprite); lastLink = link; } endAnchor = new ChainAnchor() { Position = EndPosition }; circleContainer.AddChild(endAnchor); endAnchor.LinkTarget(lastLink, Softness, Bias); var lastLineSprite = new SimpleLineSprite(); linksContainer.AddChild(lastLineSprite); lastLineSprite.PositionA = lastLink.Position; lastLineSprite.PositionB = endAnchor.Position; lineSprites.Add(lastLineSprite); }
public override ChainLink FabricateChainLink(Order order) { ChainLink link = base.FabricateChainLink(order); if (link != null) { return(link); } switch (order.Type) { case ADDITION: link = order.LinkContainerObject.AddComponent <AdditionChanLink>(); break; case SUBTRACTION: link = order.LinkContainerObject.AddComponent <SubtractionChainLink>(); break; case MULTIPLICATION: link = order.LinkContainerObject.AddComponent <MultiplicationChainLink>(); break; case DIVISION: link = order.LinkContainerObject.AddComponent <DivisionChainLink>(); break; } #if UNITY_EDITOR if (link != null) { Rect newRect = link.LinkRect; newRect.position = order.Position; Vector2 size = new Vector2(100, 100); newRect.size = size; link.LinkRect = newRect; } #endif return(link); }
public async Task InsertOrMerge_ChainLinks() { var chainLink1 = new ChainLink { RowKey = "1" }; var chainLink2 = new ChainLink { RowKey = "2" }; var cloudTableMock = new Mock <CloudTable>(new Uri("http://127.0.0.1:10002/devstoreaccount1/screenSettings")); cloudTableMock.Setup(d => d.ExecuteBatchAsync(It.IsAny <TableBatchOperation>())); cloudTableMock.Setup(d => d.CreateIfNotExistsAsync()); var repo = new ChainLinkRepository(cloudTableMock.Object); await repo.UpdateChainLinks(new List <ChainLink> { chainLink1, chainLink2 }); cloudTableMock.Verify(m => m.CreateIfNotExistsAsync(), Times.Once); cloudTableMock.Verify(m => m.ExecuteBatchAsync(It.IsAny <TableBatchOperation>()), Times.Once); }
public static void Return_ChainLinksPublishedLastMonth_BasedOnInputDate() { var septemberChainLink = new ChainLink { Position = 1, PublishedDate = "Sat, 28 Sep 2019 08:23:52 +0000" }; var novemberChainLink = new ChainLink { Position = 4, PublishedDate = "Mon, 04 Nov 2019 08:23:52 +0000" }; var chainLinks = new List <ChainLink> { septemberChainLink, new ChainLink { Position = 2, PublishedDate = "Sat, 26 Oct 2019 08:23:52 +0000" }, new ChainLink { Position = 3, PublishedDate = "Sat, 26 Oct 2019 07:17:50 +0000" }, novemberChainLink, }; Assert.That(chainLinks.GetLastMonthsChainLinks(new DateTime(2019, 11, 02)), Is.EquivalentTo(chainLinks.Except(chainLinks.Where(l => l == septemberChainLink || l == novemberChainLink)))); }
/// <summary> /// Returns a chain link entry for the given chain link. /// </summary> private async Task <ChainLinkEntry> CreateChainLinkEntry(ChainLink chainLink) { var species = await PokemonSpeciesService.Upsert(chainLink.Species); var evolutionDetailEntries = await CreateEvolutionDetailEntries(chainLink.EvolutionDetails); var evolvesTo = new List <ChainLinkEntry>(); foreach (var to in chainLink.EvolvesTo) { // create successive links recursively var entry = await CreateChainLinkEntry(to); evolvesTo.Add(entry); } return(new ChainLinkEntry { IsBaby = chainLink.IsBaby, Species = species.ForEvolutionChain(), EvolutionDetails = evolutionDetailEntries.ToList(), EvolvesTo = evolvesTo }); }
public void Init(Chain chain, ChainLink previousLink, Vector2 startPos, bool isLastLink) { this.chain = chain; this.previousLink = previousLink; this.isLastLink = isLastLink; gameObject.transform.position = new Vector3(startPos.x * FPhysics.POINTS_TO_METERS, startPos.y * FPhysics.POINTS_TO_METERS, 0); gameObject.transform.parent = world.root.transform; if (this.previousLink != null) { sprite = new FSprite("ChainLink"); world.chainHolder.AddChild(sprite); //sprite.shader = FShader.Additive; //sprite.alpha = 0.5f; sprite.ListenForUpdate(HandleUpdate); } InitPhysics(); }
/// <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> /// 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); }
// Update is called once per frame void Update() { ChainLink.gravity = dangleGravity; ChainLink.pullback = danglePullback; ChainLink.damping = dangleDamping; if (up.Held() != down.Held()) { float scrollage = speed * Time.deltaTime; while (scrollage > 0) { if (up.Held()) { if (chainLength - scrollage <= minLength) { break; } if (scrollage < links[0].length) { links[0].length -= scrollage; scrollage = 0; } else { links.RemoveAt(0); } } else { if (chainLength + scrollage >= maxLength) { break; } if (links[0].length < segmentLength) { float spaceleft = segmentLength - links[0].length; links[0].length += Mathf.Min(spaceleft, scrollage) + Mathf.Epsilon; scrollage -= spaceleft; } else { ChainLink l = new ChainLink(); l.length = Mathf.Min(segmentLength, scrollage); l.position = transform.position; links.Insert(0, l); scrollage -= segmentLength; } } } } for (int _ = 0; _ < cycles; _++) { for (int i = 0; i < links.Count; i++) { links[i].target = ( i == 0 ? new Vector2(transform.position.x, transform.position.y) : links[i - 1].position ); links[i].Update((Time.deltaTime * dtMultiplier) / cycles); } } for (int i = 0; i < links.Count; i++) { Debug.DrawLine( (i == 0 ? transform.position : (Vector3)links[i - 1].position), links[i].position, (i % 2 == 0 ? Color.red : Color.green) ); } Rigidbody2D rig = endPoint.GetComponent <Rigidbody2D>(); if (rig) { rig.MovePosition(links[links.Count - 1].position); } else { endPoint.position = links[links.Count - 1].position; } }