public void Transfer(float radians, Gear target) { var ratio = this.NumberOfTeeth / (float)target.NumberOfTeeth; var targetRadians = radians * ratio; target.Spin(targetRadians * target.Direction); // Solve colisions due to small inaccuracies // 1. Find tooth closest to target gear var targetPosition2D = new Vector2(target.Position.X, target.Position.Z); var myClosestTooth = GetToothClosestToPoint(targetPosition2D); // 2. Find tooth from target gear to us var myClosestToothCom = myClosestTooth.CenterOfMassTransformed(); var targetClosestTooth = target.GetToothClosestToPoint(myClosestToothCom); // 3. Check if they overlap if (Direction == 1) { if (LineMath.PenetrationOfAIntoB(myClosestTooth.CounterClockwiseLine.OutlineTransformed, targetClosestTooth.ClockwiseLine.OutlineTransformed, out var point)) { var adjustment = (MathHelper.TwoPi / target.NumberOfTeeth) / 300.0f; target.Spin(adjustment * target.Direction); } else if (LineMath.PenetrationOfAIntoB(myClosestTooth.ClockwiseLine.OutlineTransformed, targetClosestTooth.CounterClockwiseLine.OutlineTransformed, out var point2)) { var adjustment = -(MathHelper.TwoPi / target.NumberOfTeeth) / 300.0f; target.Spin(adjustment * target.Direction); } } // TODO: code path for when we rotate in the other direction }
public LineMath PerpendicularAtAPointOnLine(Vector3 point) { LineMath lm = new LineMath(); // TODO Check if it on the line lm.p1 = point; if (IsNaNorIsInfinity(m)) { var x = point.x + 1; var z = point.z; lm.p2 = new Vector3(x, point.y, z); return(lm); } else if (m != 0) { var perpM = -1 / m; var perpB = point.z - point.x * perpM; var x = point.x + 5; var z = perpM * x + perpB; lm.p2 = new Vector3(x, point.y, z); return(lm); } else { var x = point.x; var z = point.z + 1; lm.p2 = new Vector3(x, point.y, z); return(lm); } }
public void Transfer(Gear to, float step) { var intersects = false; var intersectionPoint = Vector2.Zero; for (var f = 0; f < this.Lines.Count; f++) { for (var t = 0; t < to.Lines.Count; t++) { var fromLine = this.Lines[f]; var toLine = to.Lines[t]; if (LineMath.Intersection(fromLine.OutlineTransformed, toLine.OutlineTransformed, out intersectionPoint)) { intersects = true; goto outside; } } } outside: if (intersects) { var ratio = this.Teeth / (float)to.Teeth; to.Spin(step * ratio); } }
public void Update(GameTime gameTime, KeyboardState keyboard) { if (keyboard.IsKeyDown(Keys.Space)) { LineList[0].Spin(0.75); //LineList[1].Spin(-0.75); } else { //LineList[0].Spin(0); //LineList[1].Spin(0); } for (var i = 0; i < LineList.Count; i++) { LineList[i].Update(gameTime); } var shortest = LineMath.ShortestDistance(LineList[0].OutlineTransformed[0], LineList[0].OutlineTransformed[1], LineList[1].OutlineTransformed[0]); var shortestLine = LineMath.ShortestDistance(LineList[0].OutlineTransformed.ToArray(), LineList[1].OutlineTransformed.ToArray()); Indicators.Clear(); //Indicators.Add(new Shape(Device, Color.Green, PointToCross(LineList[1].OutlineTransformed[0], 0.2f), false)); //Indicators.Add(new Shape(Device, Color.Pink, PointToCross(LineList[0].OutlineTransformed[0], 0.2f), false)); //Indicators.Add(new Shape(Device, Color.Purple, PointToCross(LineList[0].OutlineTransformed[1], 0.2f), false)); //Indicators.Add(new Shape(Device, Color.Orange, PointToCross(shortest, 0.4f), false)); //Indicators.Add(new Shape(Device, Color.Yellow, PointToCross(shortestLine[0], 0.04f), false)); //Indicators.Add(new Shape(Device, Color.Yellow, PointToCross(shortestLine[1], 0.04f), false)); if (LineMath.Intersection(LineList[0].OutlineTransformed.ToArray(), LineList[1].OutlineTransformed.ToArray(), out var intersectionPoint)) { Indicators.Add(new Shape(Device, Color.Yellow, PointToCross(intersectionPoint, 0.1f), false)); // Proportional increase in angular velocity so that things get unstuck var d = LineMath.PenetrationOfAIntoB(LineList[0].OutlineTransformed.ToArray(), intersectionPoint); var dRatio = d / LineList[0].Length; LineList[1].Spin(-LineList[0].AngularVelocity * (1.0 + dRatio)); Console.WriteLine($"Penetration {d:F4}, L0: {LineList[0].AngularVelocity:F4}, L1: {LineList[1].AngularVelocity:F4}"); // TODO: what if things intersect and the velocity of line 0 is 0, or quickly decreases? } }
private double?Gear(Line a, Line b, float amount) { // TODO leading on leading should add speed, while trailing on trailing should reduce speed if (LineMath.Intersection(a.OutlineTransformed.ToArray(), b.OutlineTransformed.ToArray(), out var intersectionPoint)) { Indicators.Add(new Shape(Device, Color.Yellow, PointToCross(intersectionPoint, 0.1f), false)); // Proportional increase in angular velocity so that things get unstuck var d = LineMath.PenetrationOfAIntoB(a.OutlineTransformed.ToArray(), intersectionPoint); var dRatio = d / a.Length; var spin = -a.AngularVelocity * (1.0 + dRatio); return(amount); } return(null); }
public Vector3 Intersect(LineMath secondLine) { if (IsNaNorIsInfinity(m) && IsNaNorIsInfinity(secondLine.m)) // x=a1 and x=a2 { // check if they are the same line, or parallel and have no intersection. if (p1.x == secondLine.p1.x) { // Same line, return either end of first line, or start of the second line return(p2); } else { return(new Vector3(float.NaN, float.NaN, float.NaN)); } } else if (IsNaNorIsInfinity(m)) { return(new Vector3(p1.x, p1.y, p1.x * secondLine.m + secondLine.b)); } else if (IsNaNorIsInfinity(secondLine.m)) { return(new Vector3(secondLine.p1.x, secondLine.p1.y, secondLine.p1.x * m + b)); } else if (m == secondLine.m) { if (b == secondLine.b) { // Same line, return either end of first line, or start of the second line return(p2); } else { return(new Vector3(float.NaN, float.NaN, float.NaN)); } } var intersectionX = (secondLine.b - b) / (m - secondLine.m); var intersectionZ = m * intersectionX + b; Vector3 intersection = new Vector3(intersectionX, p1.y, intersectionZ); return(intersection); }
/// <summary> /// Returns the distance to the closest line on the rout /// </summary> /// <param name="p"></param> public virtual float DistanceTo(Vector2 p) { Box2 b = BoundingBox(); if ((p.X > b.Right || p.X < b.Left) && (p.Y > b.Top || p.Y < b.Bottom)) { return(float.PositiveInfinity); } float closest = float.PositiveInfinity; for (int i = 0; i < Points.Count; i++) { float l = LineMath.Length(Points[i], p); if (closest > l) { closest = l; closestPoint = i; } } if (closest < width / 2) { closestIsPoint = true; return(closest); } closestIsPoint = false; closest = float.PositiveInfinity; for (int i = 0; i < (Points.Count - 1); i++) { float newValue = LineMath.DistanceTo(Points[i], Points[i + 1], p); if (newValue < closest) { closest = newValue; closestPoint = i; } } return(closest); }
// To recognize a scribble we require the simplified line to reverse // direction at least three times. There are separate criteria for // erasing a shape currently being drawn and for erasing existing // shapes. // // The key difference between an "erase scribble" and a "cancel // scribble" is that an erase scribble starts out as such, while // a cancel scribble indicates that the user changed his mind, so // the line will not appear to be a scribble at the beginning. // The difference is detected by timestamps. For example, the // following diagram represents an "erase" operation and a "cancel" // operation. Assume the input points are evenly spaced in time, // and that the dots represent points where the input reversed // direction. // // Input points .......................... // Reversals (erase) . . . . . . // Reversals (cancel) . . . . // // So, a scribble is considered an erasure if it satisfies t0 < t1, // where t0 is the time between mouse-down and the first reversal, // and t1 is the time between the first and third reversals. A cancel // operation satisfies t0 > t1 instead. // // Both kinds of scribble need to satisfy the formula LL*c > CHA, // where c is a constant factor in pixels, LL is the drawn line // length and CHA is the area of the Convex Hull that outlines the // drawn figure. This formula basically detects that the user // is convering the same ground repeatedly; if the pen reverses // direction repeatedly but goes to new places each time, it's not // considered an erasure scribble. For a cancel scribble, LL is // computed starting from the first reversal. IEnumerable <Shape> RecognizeScribbleForEraseOrCancel(DragState state, out bool cancel, out List <PointT> simplifiedSS) { cancel = false; simplifiedSS = null; var tolerance = state._inputTransform.Transform(new VectorT(0, 10)).Length(); var simplifiedMP = LineMath.SimplifyPolyline( state.UnfilteredMousePoints.Select(p => p.Point), tolerance); List <int> reversals = FindReversals(simplifiedMP, 3); if (reversals.Count >= 3) { simplifiedSS = simplifiedMP.Select(p => state._inputTransform.Transform(p)).ToList(); // 3 reversals confirmed. Now decide: erase or cancel? int[] timeStampsMs = FindTimeStamps(state.UnfilteredMousePoints, simplifiedMP); int t0 = timeStampsMs[reversals[0]], t1 = timeStampsMs[reversals[2]] - t0; cancel = t0 > t1 + 500; // Now test the formula LL*c > CHA as explained above IListSource <PointT> simplifiedMP_ = cancel ? simplifiedMP.Slice(reversals[0]) : simplifiedMP.AsListSource(); float LL = simplifiedMP_.AdjacentPairs().Sum(pair => pair.A.Sub(pair.B).Length()); var hull = PointMath.ComputeConvexHull(simplifiedMP); float CHA = PolygonMath.PolygonArea(hull); if (LL * EraseNubWidth > CHA) { // Erasure confirmed. if (cancel) { return(EmptyList <Shape> .Value); } // Figure out which shapes to erase. To do this, we compute for // each shape the amount of the scribble that overlaps that shape. var simplifiedSS_ = simplifiedSS; return(_doc.Shapes.Where(s => ShouldErase(s, simplifiedSS_)).ToList()); } } return(null); }
void UpdateBegun() { var blockPoses = LineMath.IntegerPoints(lastMousePosition, mousePosition); if (blockPoses.Count > 0) { Vector2Int lastBlockPos = Vector2Int.zero; foreach (var blockPos in blockPoses) { Direction direction = BlockDirection(lastBlockPos, blockPos); int xStep = 0; int yStep = 0; switch (direction) { case Direction.East: xStep = 1; break; case Direction.South: yStep = -1; break; case Direction.West: xStep = -1; break; case Direction.North: yStep = 1; break; } int x = blockPos.x; int y = blockPos.y; List <Block> blocksToMove = new List <Block> (); for (var block = board.GetDisplayBlock(x, y); block != null && block != begunBlock; x += xStep, y += yStep, block = board.GetDisplayBlock(x, y)) { blocksToMove.Add(block); } foreach (var block in blocksToMove) { block.DisplayX += xStep; block.DisplayY += yStep; } lastBlockPos = blockPos; } begunBlock.DisplayX = lastBlockPos.x; begunBlock.DisplayY = lastBlockPos.y; var blockMoved = true; while (blockMoved) { blockMoved = false; foreach (var block in board.GetBlocks()) { if (block != begunBlock) { if (block.DisplayX != block.X) { var dist = block.X - block.DisplayX; var desired = block.DisplayX + dist / Mathf.Abs(dist); if (board.GetDisplayBlock(desired, block.DisplayY) == null) { block.DisplayX = desired; blockMoved = true; } } if (block.DisplayY != block.Y) { var dist = block.Y - block.DisplayY; var desired = block.DisplayY + dist / Mathf.Abs(dist); if (board.GetDisplayBlock(block.DisplayX, desired) == null) { block.DisplayY = desired; blockMoved = true; } } } } } } if (Input.GetMouseButtonUp(0)) { if (board.WithinBoundary(begunBlock.DisplayX, begunBlock.DisplayY)) { var blocksToRemove = new List <Block> (); foreach (var block in board.GetBlocks()) { if (!board.WithinBoundary(block.DisplayX, block.DisplayY)) { blocksToRemove.Add(block); } else { if (block.DisplayX != block.X || block.DisplayY != block.Y) { block.OnFire = false; block.OnWarning = false; block.NoFireCount = 0; block.OnWarningCount = 0; } block.SetAllPosition(block.DisplayX, block.DisplayY); } } foreach (var block in blocksToRemove) { board.RemoveBlock(block); block.gameObject.SetActive(false); Destroy(block.gameObject); } soundManager.PlayDrop(); } else { board.RemoveBlock(begunBlock); prepareArea.AddBlock(begunBlock); foreach (var block in board.GetBlocks()) { block.SetAllPosition(block.X, block.Y); } } begunBlock = null; state = State.Idle; } }