public static Sphere[] Mirrors(int p, int q, int r, ref Vector3D cellCenter, bool moveToBall = true, double scaling = -1)
{
Geometry g = Util.GetGeometry(p, q, r);
if (g == Geometry.Spherical)
{
// These are in the ball model.
Sphere[] result = SimplexCalcs.MirrorsSpherical(p, q, r);
return(result);
}
else if (g == Geometry.Euclidean)
{
return(SimplexCalcs.MirrorsEuclidean());
}
// This is a rotation we'll apply to the mirrors at the end.
// This is to try to make our image outputs have vertical bi-lateral symmetry and the most consistent in all cases.
// NOTE: + is CW, not CCW. (Because the way I did things, our images have been reflected vertically, and I'm too lazy to go change this.)
double rotation = Math.PI / 2;
// Some construction points we need.
Vector3D p1, p2, p3;
Segment seg = null;
TilePoints(p, q, out p1, out p2, out p3, out seg);
//
// Construct in UHS
//
Geometry cellGeometry = Geometry2D.GetGeometry(p, q);
Vector3D center = new Vector3D();
double radius = 0;
if (cellGeometry == Geometry.Spherical)
{
// Finite or Infinite r
// Spherical trig
double halfSide = Geometry2D.GetTrianglePSide(q, p);
double mag = Math.Sin(halfSide) / Math.Cos(Util.PiOverNSafe(r));
mag = Math.Asin(mag);
// e.g. 43j
//mag *= 0.95;
// Move mag to p1.
mag = Spherical2D.s2eNorm(mag);
H3Models.Ball.DupinCyclideSphere(p1, mag, Geometry.Spherical, out center, out radius);
}
else if (cellGeometry == Geometry.Euclidean)
{
center = p1;
radius = p1.Dist(p2) / Math.Cos(Util.PiOverNSafe(r));
}
else if (cellGeometry == Geometry.Hyperbolic)
{
if (Infinite(p) && Infinite(q) && FiniteOrInfinite(r))
{
//double iiiCellRadius = 2 - Math.Sqrt( 2 );
//Circle3D iiiCircle = new Circle3D() { Center = new Vector3D( 1 - iiiCellRadius, 0, 0 ), Radius = iiiCellRadius };
//radius = iiiCellRadius; // infinite r
//center = new Vector3D( 1 - radius, 0, 0 );
// For finite r, it was easier to calculate cell facet in a more symmetric position,
// then move into position with the other mirrors via a Mobius transformation.
double rTemp = 1 / (Math.Cos(Util.PiOverNSafe(r)) + 1);
Mobius m = new Mobius();
m.Isometry(Geometry.Hyperbolic, -Math.PI / 4, new Vector3D(0, Math.Sqrt(2) - 1));
Vector3D c1 = m.Apply(new Vector3D(1 - 2 * rTemp, 0, 0));
Vector3D c2 = c1;
c2.Y *= -1;
Vector3D c3 = new Vector3D(1, 0);
Circle3D c = new Circle3D(c1, c2, c3);
radius = c.Radius;
center = c.Center;
}
else if (Infinite(p) && Finite(q) && FiniteOrInfinite(r))
{
// http://www.wolframalpha.com/input/?i=r%2Bx+%3D+1%2C+sin%28pi%2Fp%29+%3D+r%2Fx%2C+solve+for+r
// radius = 2 * Math.Sqrt( 3 ) - 3; // Appolonian gasket wiki page
//radius = Math.Sin( Math.PI / q ) / ( Math.Sin( Math.PI / q ) + 1 );
//center = new Vector3D( 1 - radius, 0, 0 );
// For finite r, it was easier to calculate cell facet in a more symmetric position,
// then move into position with the other mirrors via a Mobius transformation.
double rTemp = 1 / (Math.Cos(Util.PiOverNSafe(r)) + 1);
Mobius m = new Mobius();
m.Isometry(Geometry.Hyperbolic, 0, p2);
Vector3D findingAngle = m.Inverse().Apply(new Vector3D(1, 0));
double angle = Math.Atan2(findingAngle.Y, findingAngle.X);
m.Isometry(Geometry.Hyperbolic, angle, p2);
Vector3D c1 = m.Apply(new Vector3D(1 - 2 * rTemp, 0, 0));
Vector3D c2 = c1;
c2.Y *= -1;
Vector3D c3 = new Vector3D(1, 0);
Circle3D c = new Circle3D(c1, c2, c3);
radius = c.Radius;
center = c.Center;
}
else if (Finite(p) && Infinite(q) && FiniteOrInfinite(r))
{
radius = p2.Abs(); // infinite r
radius = DonHatch.asinh(Math.Sinh(DonHatch.e2hNorm(p2.Abs())) / Math.Cos(Util.PiOverNSafe(r))); // hyperbolic trig
// 4j3
//m_jOffset = radius * 0.02;
//radius += m_jOffset ;
radius = DonHatch.h2eNorm(radius);
center = new Vector3D();
rotation *= -1;
}
else if (/*Finite( p ) &&*/ Finite(q))
{
// Infinite r
//double mag = Geometry2D.GetTrianglePSide( q, p );
// Finite or Infinite r
double halfSide = Geometry2D.GetTrianglePSide(q, p);
double mag = DonHatch.asinh(Math.Sinh(halfSide) / Math.Cos(Util.PiOverNSafe(r))); // hyperbolic trig
H3Models.Ball.DupinCyclideSphere(p1, DonHatch.h2eNorm(mag), out center, out radius);
}
else
{
throw new System.NotImplementedException();
}
}
Sphere cellBoundary = new Sphere()
{
Center = center,
Radius = radius
};
Sphere[] interior = InteriorMirrors(p, q);
Sphere[] surfaces = new Sphere[] { cellBoundary, interior[0], interior[1], interior[2] };
// Apply rotations.
bool applyRotations = true;
if (applyRotations)
{
foreach (Sphere s in surfaces)
{
RotateSphere(s, rotation);
}
p1.RotateXY(rotation);
}
// Apply scaling
bool applyScaling = scaling != -1;
if (applyScaling)
{
//double scale = 1.0/0.34390660467269524;
//scale = 0.58643550768408892;
foreach (Sphere s in surfaces)
{
Sphere.ScaleSphere(s, scaling);
}
}
bool facetCentered = false;
if (facetCentered)
{
PrepForFacetCentering(p, q, surfaces, ref cellCenter);
}
// Move to ball if needed.
if (moveToBall)
{
surfaces = MoveToBall(surfaces, ref cellCenter);
}
return(surfaces);
}