public void PointOfAngleTest() { var zero = new Position(0, 0, null, null); var p = zero.PointOfAngle(Molecule.TETRAHEDRON_SITE, Math.PI * 5 / 6); Assert.Less(p.X, zero.X); Assert.AreEqual(2, p.TetrahedronPart(zero)); }
public void BoundPositionTest(Molecule molecule, int boundNr, double xExp, double yExp) { var poz = molecule.BoundPosition(boundNr); var poz_expected = new Position(xExp, yExp); AssertExtensions.AreAlmostEqual(poz_expected, poz); }
/// <summary> /// Circle equation: /// x = s.X + r*cos(t) /// y = s.Y + r*sin(t) /// Segment equation: /// x = p1.X + k*(p2.X-p1.X) /// y = p1.Y + k*(p2.Y-p1.Y) /// where k in [0, 1], t in [0, 360) /// => /// t = acos((x - s.X)/r OR t = asin((y - s.Y)/r /// k = (x - p1.X)/(p2.X-p1.X) OR k = (y - p1.Y)/(p2.Y-p1.Y) /// </summary> public static List<Position> CircleAndSegmentIntersections(Position p1, Position p2, double r, Position s, bool includeEnds) { List<Position> ret = new List<Position>(); if (p1.X != p2.X) { /* y = ax + b (s.x - x)^2 + (s.y - y)^2 = r^2 => (s.x-x)^2 + (s.y - b - ax) ^2 = r^2 => (1 + a^2)x^2- 2[(s.y-b)a + s.x] x + s.x^2 + (s.y-b)^2 - r^2 = 0 */ double a = (p2.Y - p1.Y) / (p2.X - p1.X); double b = p1.Y - a * p1.X; double[] xs = SolveQuadraticalEquation( 1 + a * a, -2 * ((s.Y - b) * a + s.X), s.X * s.X + (s.Y - b) * (s.Y - b) - r * r ); foreach (double x in xs) { double k = (x - p1.X) / (p2.X - p1.X); if (includeEnds && 0 <= k && k <= 1 || !includeEnds && 0 < k && k < 1) { ret.Add(new Position(x, a * x + b)); } } } else { /* x = a (s.x - x)^2 + (s.y - y)^2 = r^2 => (s.X - a)^2 + (s.Y - y)^2 = r^2 => y^2 + -2*s.Y*y + s.Y^2 + (s.X - a)^2 - r^2 = 0 */ double a = p1.X; double[] ys = SolveQuadraticalEquation( 1, -2 * s.Y, s.Y * s.Y + (s.X - a) * (s.X - a) - r * r ); foreach (double y in ys) { double k = (y - p1.Y) / (p2.Y - p1.Y); if (includeEnds && 0 <= k && k <= 1 || !includeEnds && 0 < k && k < 1) { ret.Add(new Position(a, y)); } } } return ret; }
public void TetrahedronPartTest() { Position zero = new Position(3.45, 7.93, null, null); Position p = new Position(3.45, 10, null, null); Assert.AreEqual(1, p.TetrahedronPart(zero)); p.X = -100; p.Y = 8; Assert.AreEqual(2, p.TetrahedronPart(zero)); p.X = 100; p.Y = 1; Assert.AreEqual(5, p.TetrahedronPart(zero)); }
public void PointOnBorderOfTetrahedronPartTest() { Position zero = new Position(3.45, 7.93, null, null); Double r = 10; var p = zero.PointOnBorderOfTetrahedronPart(r, 3); var p_expected = new Position(zero.X - r, zero.Y, null, null); AssertExtensions.AreAlmostEqual(p_expected, p); p = zero.PointOnBorderOfTetrahedronPart(r, 5); p_expected = new Position(zero.X + r / 2, zero.Y - r * Math.Sqrt(3) / 2, null, null); AssertExtensions.AreAlmostEqual(p_expected, p); }
public void CircleAndSegmentIntersectionsTest() { var positions = Intersections.CircleAndSegmentIntersections(new Position(6, 6), new Position(6, 20), 10, new Position(0, 0), false); var pExp = new Position(6, 8); AssertExtensions.AreAlmostEqual(pExp, positions[0]); positions = Intersections.CircleAndSegmentIntersections(new Position(0, -8), new Position(12, -8), 10, new Position(0, 0), false); pExp = new Position(6, -8); AssertExtensions.AreAlmostEqual(pExp, positions[0]); positions = Intersections.CircleAndSegmentIntersections(new Position(0, -7.8), new Position(12, -8.2), 10, new Position(0, 0), false); pExp = new Position(6, -8); AssertExtensions.AreAlmostEqual(pExp, positions[0]); }
public static void DoElasticCollisionOfTwoBalls(double mass1, Position b1, double mass2, Position b2) { // Avoid division by zero below in computing new normal velocities // Doing a collision where both balls have no mass makes no sense anyway if (mass1 <= 0.0 || mass2 <= 0.0) return; // Compute unit normal and unit tangent vectors V v_n = b2 - b1; // v_n = normal vec. - a vector normal to the collision surface V v_un = v_n.UnitVector(); // unit normal vector V v_ut = new V(-v_un.Y, v_un.X); // unit tangent vector // Compute scalar projections of velocities onto v_un and v_ut double v1n = v_un * b1.Direction; // Dot product double v1t = v_ut * b1.Direction; double v2n = v_un * b2.Direction; double v2t = v_ut * b2.Direction; // Compute new tangential velocities double v1tPrime = v1t; // Note: in reality, the tangential velocities do not change after the collision double v2tPrime = v2t; // Compute new normal velocities using one-dimensional elastic collision equations in the normal direction // Division by zero avoided. See early return above. double v1nPrime = (v1n * (mass1 - mass2) + 2.0 * mass2 * v2n) / (mass1 + mass2); double v2nPrime = (v2n * (mass2 - mass1) + 2.0 * mass1 * v1n) / (mass1 + mass2); // Compute new normal and tangential velocity vectors V v_v1nPrime = v1nPrime * v_un; // Multiplication by a scalar V v_v1tPrime = v1tPrime * v_ut; V v_v2nPrime = v2nPrime * v_un; V v_v2tPrime = v2tPrime * v_ut; // Set new velocities in x and y coordinates b1.Direction.X = v_v1nPrime.X + v_v1tPrime.X; b1.Direction.Y = v_v1nPrime.Y + v_v1tPrime.Y; b2.Direction.X = v_v2nPrime.X + v_v2tPrime.X; b2.Direction.Y = v_v2nPrime.Y + v_v2tPrime.Y; }
public void Init(Habitat habitat, InitMoleculeType type) { this.habitat = habitat; if (type == InitMoleculeType.Center) { Position = new Position(0, 0, new V(0, 0), this); AttachedIteration = 0; } else { Position = Position.NextRandomPosition( habitat.CondensationCenter.Position.X, habitat.CondensationCenter.Position.Y, habitat.Radius, this.DefaultSpeed, this, type == InitMoleculeType.Additional); } }
public static void AreAlmostEqual(Position expected, Position actual) { AssertExtensions.AreAlmostEqual(expected.X, actual.X, "X"); AssertExtensions.AreAlmostEqual(expected.Y, actual.Y, "Y"); }
/// <summary> /// Pod jakim jest kątem do dodaniej osi OX /// </summary> /// <param name="zero">Początek układu współrzędnych</param> /// <returns></returns> public double Angle(Position zero) { double angle; double x_rel = X - zero.X; double y_rel = Y - zero.Y; if (x_rel == 0) angle = y_rel > 0 ? Math.PI / 2 : 3 * Math.PI / 2; else { switch (Quadrant(zero)) { case 1: angle = Math.Atan(y_rel / x_rel); break; case 4: angle = Math.Atan(y_rel / x_rel) + 2 * Math.PI; break; default: angle = Math.Atan(y_rel / x_rel) + Math.PI; break; } } angle = (angle + 2 * Math.PI) % (Math.PI * 2); return angle; }
/// <summary> /// Do której szóstki układu współrzędych należy punkt numerowane 0 - 5 /// </summary> /// <param name="zero">Początek układu współrzędnych</param> /// <returns></returns> public int TetrahedronPart(Position zero) { double angle = Angle(zero); return (int)(angle / (Math.PI / 3)); }
/// <summary> /// Do której ćwiartki układu współrzędych należy punkt numerowane 1 - 4 /// </summary> /// <param name="zero">Początek układu współrzędnych</param> /// <returns></returns> public int Quadrant(Position zero) { if (X - zero.X >= 0) if (Y - zero.Y >= 0) return 1; else return 4; else if (Y - zero.Y >= 0) return 2; else return 3; }
public void BumpWalls() { V d = new V(Direction.X, Direction.Y); Position expectedPosition = new Position(this.X + Direction.X, this.Y + Direction.Y); while (d.Speed > 0) { List<Position> crossing = Intersections.CircleAndSegmentIntersections(this, expectedPosition, HabitatRadius, HabitatCondensationCenter.Position, false); if (crossing.Count == 0) break; Position crossPosition = crossing.First<Position>(); double alpha = (4 * Math.PI / 9) * random.NextDouble() - (2 * Math.PI / 9); double angle = HabitatCondensationCenter.Position.Angle(crossPosition); double dMoved = (crossPosition - this).Speed; expectedPosition = crossPosition.PointOfAngle(dMoved, angle + alpha); this.X = crossPosition.X; this.Y = crossPosition.Y; double oldSpeed = Direction.Speed; Direction = expectedPosition - crossPosition; Direction.Speed = oldSpeed; d.Speed -= dMoved; } Direction.Speed = EvaluateSpeed(); this.X = expectedPosition.X; this.Y = expectedPosition.Y; }