public Isometry ( Geometry g, double angle, System.Numerics.Complex P ) : void | ||
g | Geometry | |
angle | double | |
P | System.Numerics.Complex | |
리턴 | void |
/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// factor can be used to only go some fraction of the distance from p1 to p2. /// </summary> public void Geodesic(Geometry g, Complex p1, Complex p2, double factor = 1.0) { Mobius t = new Mobius(); t.Isometry(g, 0, p1 * -1); Complex p2t = t.Apply(p2); // Only implemented for hyperbolic so far. if (factor != 1.0 && g == Geometry.Hyperbolic) { double newMag = DonHatch.h2eNorm(DonHatch.e2hNorm(p2t.Magnitude) * factor); Vector3D temp = Vector3D.FromComplex(p2t); temp.Normalize(); temp *= newMag; p2t = temp.ToComplex(); } Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry(g, 0, p1 * -1); m2.Isometry(g, 0, p2t); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
/// <summary> /// Allow a hyperbolic transformation using an absolute offset. /// offset is specified in the respective geometry. /// </summary> public void Hyperbolic2(Geometry g, Complex fixedPlus, Complex point, double offset) { // To the origin. Mobius m = new Mobius(); m.Isometry(g, 0, fixedPlus * -1); double eRadius = m.Apply(point).Magnitude; double scale = 1; switch (g) { case Geometry.Spherical: double sRadius = Spherical2D.e2sNorm(eRadius); sRadius += offset; scale = Spherical2D.s2eNorm(sRadius) / eRadius; break; case Geometry.Euclidean: scale = (eRadius + offset) / eRadius; break; case Geometry.Hyperbolic: double hRadius = DonHatch.e2hNorm(eRadius); hRadius += offset; scale = DonHatch.h2eNorm(hRadius) / eRadius; break; } Hyperbolic(g, fixedPlus, scale); }
public static Mobius CreateFromIsometry(Geometry g, double angle, Complex P) { Mobius m = new Mobius(); m.Isometry(g, angle, P); return(m); }
/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// </summary> public void Geodesic(Geometry g, Complex p1, Complex p2) { Mobius t = new Mobius(); t.Isometry(g, 0, p1 * -1); Complex p2t = t.Apply(p2); Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry(g, 0, p1 * -1); m2.Isometry(g, 0, p2t); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
public void Elliptic(Geometry g, Complex fixedPlus, double angle) { // To the origin. Mobius origin = new Mobius(); origin.Isometry(g, 0, fixedPlus * -1); // Rotate. Mobius rotate = new Mobius(); rotate.Isometry(g, angle, new Complex()); // Conjugate. this = origin.Inverse() * rotate * origin; }
public void Hyperbolic(Geometry g, Complex fixedPlus, double scale) { // To the origin. Mobius m1 = new Mobius(); m1.Isometry(g, 0, fixedPlus * -1); // Scale. Mobius m2 = new Mobius(); m2.A = scale; m2.B = m2.C = 0; m2.D = 1; // Back. //Mobius m3 = m1.Inverse(); // Doesn't work well if fixedPlus is on disk boundary. Mobius m3 = new Mobius(); m3.Isometry(g, 0, fixedPlus); // Compose them (multiply in reverse order). this = m3 * m2 * m1; }
public void Elliptic( Geometry g, Complex fixedPlus, double angle ) { // To the origin. Mobius origin = new Mobius(); origin.Isometry( g, 0, fixedPlus * -1 ); // Rotate. Mobius rotate = new Mobius(); rotate.Isometry( g, angle, new Complex() ); // Conjugate. this = origin.Inverse() * rotate * origin; }
/// <summary> /// A Mobius that will put our simplex into a face centered orientation. /// Meant to be used with H3Models.TransformInBall2 or H3Models.TransformHelper /// </summary> private static Mobius FCOrientMobius( Sphere cellSphereInBall ) { Mobius m = new Mobius(); double d = cellSphereInBall.Center.Abs() - cellSphereInBall.Radius; m.Isometry( Geometry.Hyperbolic, 0, new Vector3D( 0, d ) ); return m; }
/// <summary> /// Subdivides a segment from p1->p2 with the two endpoints not on the origin, in the respective geometry. /// </summary> private static Vector3D[] SubdivideSegmentInGeometry( Vector3D p1, Vector3D p2, int divisions, Geometry g ) { // Handle this specially, so we can keep things 3D if needed. if( g == Geometry.Euclidean ) { Segment seg = Segment.Line( p1, p2 ); return seg.Subdivide( divisions ); } Mobius p1ToOrigin = new Mobius(); p1ToOrigin.Isometry( g, 0, -p1 ); Mobius inverse = p1ToOrigin.Inverse(); Vector3D newP2 = p1ToOrigin.Apply( p2 ); Segment radial = Segment.Line( new Vector3D(), newP2 ); Vector3D[] temp = SubdivideRadialInGeometry( radial, divisions, g ); List<Vector3D> result = new List<Vector3D>(); foreach( Vector3D v in temp ) result.Add( inverse.Apply( v ) ); return result.ToArray(); }
private static void HyperidealSquares() { Mobius rot = new Mobius(); rot.Isometry( Geometry.Spherical, Math.PI / 4, new Vector3D() ); List<Segment> segs = new List<Segment>(); int[] qs = new int[] { 5, -1 }; foreach( int q in qs ) { TilingConfig config = new TilingConfig( 4, q, 1 ); Tile t = Tiling.CreateBaseTile( config ); List<Segment> polySegs = t.Boundary.Segments; polySegs = polySegs.Select( s => { s.Transform( rot ); return s; } ).ToList(); segs.AddRange( polySegs ); } Vector3D v1 = new Vector3D(1,0); v1.RotateXY( Math.PI/6 ); Vector3D v2 = v1; v2.Y *= -1; Vector3D cen; double rad; H3Models.Ball.OrthogonalCircle( v1, v2, out cen, out rad ); Segment seg = Segment.Arc( v1, v2, cen, false ); rot.Isometry( Geometry.Spherical, Math.PI / 2, new Vector3D() ); for( int i = 0; i < 4; i++ ) { seg.Transform( rot ); segs.Add( seg.Clone() ); } SVG.WriteSegments( "output1.svg", segs ); System.Func<Segment, Segment> PoincareToKlein = s => { return Segment.Line( HyperbolicModels.PoincareToKlein( s.P1 ), HyperbolicModels.PoincareToKlein( s.P2 ) ); }; segs = segs.Select( s => PoincareToKlein( s ) ).ToList(); Vector3D v0 = new Vector3D( v1.X, v1.X ); Vector3D v3 = v0; v3.Y *= -1; Segment seg1 = Segment.Line( v0, v1 ), seg2 = Segment.Line( v2, v3 ); Segment seg3 = Segment.Line( new Vector3D( 1, 1 ), new Vector3D( 1, -1 ) ); for( int i = 0; i < 4; i++ ) { seg1.Transform( rot ); seg2.Transform( rot ); seg3.Transform( rot ); segs.Add( seg1.Clone() ); segs.Add( seg2.Clone() ); segs.Add( seg3.Clone() ); } SVG.WriteSegments( "output2.svg", segs ); }
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 ) return SimplexCalcs.MirrorsSpherical( p, q, r ); 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. Sphere[] result = surfaces.Select( s => moveToBall ? H3Models.UHSToBall( s ) : s ).ToArray(); cellCenter = moveToBall ? H3Models.UHSToBall( cellCenter ) : cellCenter; return result; }
public static Mobius VertexCenteredMobius( int p, int q ) { double angle = Math.PI / q; if( Utils.Even( q ) ) angle *= 2; Vector3D offset = new Vector3D( -1 * Geometry2D.GetNormalizedCircumRadius( p, q ), 0, 0 ); offset.RotateXY( angle ); Mobius m = new Mobius(); m.Isometry( Geometry2D.GetGeometry( p, q ), angle, offset.ToComplex() ); return m; }
/// <summary> /// Calculate the hyperbolic midpoint of an edge. /// Only works for non-ideal edges at the moment. /// </summary> public static Vector3D Midpoint( H3.Cell.Edge edge ) { // Special case if edge has endpoint on origin. // XXX - Really this should be special case anytime edge goes through origin. Vector3D e1 = edge.Start; Vector3D e2 = edge.End; if( e1.IsOrigin || e2.IsOrigin ) { if( e2.IsOrigin ) Utils.Swap<Vector3D>( ref e1, ref e2 ); return HalfTo( e2 ); } // No doubt there is a much better way, but // work in H2 slice transformed to xy plane, with e1 on x-axis. double angle = e1.AngleTo( e2 ); // always <= 180 e1 = new Vector3D( e1.Abs(), 0 ); e2 = new Vector3D( e2.Abs(), 0 ); e2.RotateXY( angle ); // Mobius that will move e1 to origin. Mobius m = new Mobius(); m.Isometry( Geometry.Hyperbolic, 0, -e1 ); e2 = m.Apply( e2 ); Vector3D midOnPlane = HalfTo( e2 ); midOnPlane= m.Inverse().Apply( midOnPlane ); double midAngle = e1.AngleTo( midOnPlane ); Vector3D mid = edge.Start; mid.RotateAboutAxis( edge.Start.Cross( edge.End ), midAngle ); mid.Normalize( midOnPlane.Abs() ); return mid; }
/// <summary> /// Allow a hyperbolic transformation using an absolute offset. /// offset is specified in the respective geometry. /// </summary> public void Hyperbolic2( Geometry g, Complex fixedPlus, Complex point, double offset ) { // To the origin. Mobius m = new Mobius(); m.Isometry( g, 0, fixedPlus * -1 ); double eRadius = m.Apply( point ).Magnitude; double scale = 1; switch( g ) { case Geometry.Spherical: double sRadius = Spherical2D.e2sNorm( eRadius ); sRadius += offset; scale = Spherical2D.s2eNorm( sRadius ) / eRadius; break; case Geometry.Euclidean: scale = (eRadius + offset) / eRadius; break; case Geometry.Hyperbolic: double hRadius = DonHatch.e2hNorm( eRadius ); hRadius += offset; scale = DonHatch.h2eNorm( hRadius ) / eRadius; break; } Hyperbolic( g, fixedPlus, scale ); }
/// <summary> /// Using this to move the view around in interesting ways. /// </summary> private Vector3D ApplyTransformation( Vector3D v, double t = 0.0 ) { //v.RotateXY( Math.PI / 4 + 0.01 ); bool applyNone = true; if( applyNone ) return v; Mobius m0 = new Mobius(), m1 = new Mobius(), m2 = new Mobius(), m3 = new Mobius(); Sphere unitSphere = new Sphere(); // self-similar scale for 437 //v*= 4.259171776329806; double s = 6.5; v *= s; v += new Vector3D( s/3, -s/3 ); v = unitSphere.ReflectPoint( v ); v.RotateXY( Math.PI/6 ); //v /= 3; //v.RotateXY( Math.PI ); //v.RotateXY( Math.PI/2 ); return v; //v.Y = v.Y / Math.Cos( Math.PI / 6 ); // 637 repeatable //return v; // 12,12,12 m0.Isometry( Geometry.Hyperbolic, 0, new Complex( .0, .0 ) ); m1 = Mobius.Identity(); m2 = Mobius.Identity(); m3 = Mobius.Identity(); v = (m0 * m1 * m2 * m3).Apply( v ); return v; // i64 m0.Isometry( Geometry.Hyperbolic, 0, new Complex( .5, .5 ) ); m1.UpperHalfPlane(); m2 = Mobius.Scale( 1.333333 ); m3.Isometry( Geometry.Euclidean, 0, new Vector3D( 0, -1.1 ) ); v = (m1 * m2 * m3).Apply( v ); return v; // 464 // NOTE: Also, don't apply rotations during simplex generation. m1.UpperHalfPlane(); m2 = Mobius.Scale( 1.3 ); m3.Isometry( Geometry.Euclidean, 0, new Vector3D( 1.55, -1.1 ) ); v = ( m1 * m2 * m3 ).Apply( v ); return v; // iii m1.Isometry( Geometry.Hyperbolic, 0, new Complex( 0, Math.Sqrt( 2 ) - 1 ) ); m2.Isometry( Geometry.Euclidean, -Math.PI / 4, 0 ); m3 = Mobius.Scale( 5 ); //v = ( m1 * m2 * m3 ).Apply( v ); // Vertical Line /*v = unitSphere.ReflectPoint( v ); m1.MapPoints( new Vector3D(-1,0), new Vector3D(), new Vector3D( 1, 0 ) ); m2 = Mobius.Scale( .5 ); v = (m1*m2).Apply( v ); */ /* m1 = Mobius.Scale( 0.175 ); v = unitSphere.ReflectPoint( v ); v = m1.Apply( v ); * */ // Inversion //v = unitSphere.ReflectPoint( v ); //return v; /*Mobius m1 = new Mobius(), m2 = new Mobius(), m3 = new Mobius(); m1.Isometry( Geometry.Spherical, 0, new Complex( 0, 1 ) ); m2.Isometry( Geometry.Euclidean, 0, new Complex( 0, -1 ) ); m3 = Mobius.Scale( 0.5 ); v = (m1 * m3 * m2).Apply( v );*/ //Mobius m = new Mobius(); //m.Isometry( Geometry.Hyperbolic, 0, new Complex( -0.88, 0 ) ); //m.Isometry( Geometry.Hyperbolic, 0, new Complex( 0, Math.Sqrt(2) - 1 ) ); //m = Mobius.Scale( 0.17 ); //m.Isometry( Geometry.Spherical, 0, new Complex( 0, 3.0 ) ); //v = m.Apply( v ); // 63i, 73i m1 = Mobius.Scale( 6.0 ); // Scale {3,i} to unit disk. m1 = Mobius.Scale( 1.0 / 0.14062592996431983 ); // 73i (constant is abs of midpoint of {3,7} tiling, if we want to calc later for other tilings). m2.MapPoints( Infinity.InfinityVector, new Vector3D( 1, 0 ), new Vector3D() ); // swap interior/exterior m3.UpperHalfPlane(); v *= 2.9; // iii /*m1.MapPoints( new Vector3D(), new Vector3D(1,0), new Vector3D( Math.Sqrt( 2 ) - 1, 0 ) ); m2.Isometry( Geometry.Euclidean, -Math.PI / 4, 0 ); m3 = Mobius.Scale( 0.75 );*/ Mobius m = m3 * m2 * m1; v = m.Inverse().Apply( v ); // Strange that we have to do inverse here. return v; }
public static Sphere GeodesicOffset( Sphere s, double offset, bool ball = true ) { Sphere offsetSphere; if( ball ) { // Geodesic offset (ball). { // Hyperbolic honeycomb double mag = s.Center.Abs() - s.Radius; mag = s.IsPlane ? DonHatch.h2eNorm( offset ) : DonHatch.h2eNorm( DonHatch.e2hNorm( mag ) - offset ); Vector3D closestPointToOrigin = s.IsPlane ? s.Normal : s.Center; closestPointToOrigin.Normalize(); closestPointToOrigin *= mag; offsetSphere = H3Models.Ball.OrthogonalSphereInterior( closestPointToOrigin ); // There are multiple ultraparallel spheres. // This experiments with picking others. Mobius m = new Mobius(); m.Isometry( Geometry.Hyperbolic, 0, new Vector3D( 0, -0.2 ) ); //H3Models.TransformInBall2( offsetSphere, m ); } { // Spherical honeycomb //offset *= -1; double mag = -s.Center.Abs() + s.Radius; Spherical2D.s2eNorm( Spherical2D.e2sNorm( mag ) + offset ); offsetSphere = s.Clone(); offsetSphere.Radius += offset*10; } } else { // Geodesic offset (UHS). // XXX - not scaled right. offsetSphere = s.Clone(); offsetSphere.Radius += offset; } return offsetSphere; }
/// <summary> /// Same as above, but works with all geometries. /// </summary> public static Circle EquidistantOffset( Geometry g, Segment seg, double offset ) { Mobius m = new Mobius(); Vector3D direction; if( seg.Type == SegmentType.Line ) { direction = seg.P2 - seg.P1; direction.RotateXY( Math.PI / 2 ); } else { direction = seg.Circle.Center; } direction.Normalize(); m.Isometry( g, 0, direction * offset ); // Transform 3 points on segment. Vector3D p1 = m.Apply( seg.P1 ); Vector3D p2 = m.Apply( seg.Midpoint ); Vector3D p3 = m.Apply( seg.P2 ); return new Circle( p1, p2, p3 ); }
/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// </summary> public void Geodesic( Geometry g, Complex p1, Complex p2 ) { Mobius t = new Mobius(); t.Isometry( g, 0, p1 * -1 ); Complex p2t = t.Apply( p2 ); Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry( g, 0, p1 * -1 ); m2.Isometry( g, 0, p2t ); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
public Vector3D Transform( Vector3D v ) { v.RotateAboutAxis( new Vector3D( 1, 0 ), Math.PI / 2 ); Mobius m = new Mobius(); m.Isometry( Geometry.Hyperbolic, 0, new Complex( 0, 0.6 ) ); v = H3Models.TransformHelper( v, m ); v.RotateAboutAxis( new Vector3D( 1, 0 ), -Math.PI / 2 ); return v; }
public void Hyperbolic( Geometry g, Complex fixedPlus, double scale ) { // To the origin. Mobius m1 = new Mobius(); m1.Isometry( g, 0, fixedPlus * -1 ); // Scale. Mobius m2 = new Mobius(); m2.A = scale; m2.B = m2.C = 0; m2.D = 1; // Back. //Mobius m3 = m1.Inverse(); // Doesn't work well if fixedPlus is on disk boundary. Mobius m3 = new Mobius(); m3.Isometry( g, 0, fixedPlus ); // Compose them (multiply in reverse order). this = m3 * m2 * m1; }
/// <summary> /// This Mobius transform will center the tiling on an edge. /// </summary> public Mobius EdgeMobius() { Geometry g = Geometry2D.GetGeometry( this.P, this.Q ); Polygon poly = new Polygon(); poly.CreateRegular( this.P, this.Q ); Segment seg = poly.Segments[0]; Vector3D offset = seg.Midpoint; double angle = Math.PI / this.P; offset.RotateXY( -angle ); Mobius m = new Mobius(); m.Isometry( g, -angle, -offset ); return m; }
public static Mobius CreateFromIsometry( Geometry g, double angle, Complex P ) { Mobius m = new Mobius(); m.Isometry( g, angle, P ); return m; }
public static Mobius FCOrientMobius( int p, int q ) { Vector3D p1, p2, p3; Segment seg = null; TilePoints( p, q, out p1, out p2, out p3, out seg ); Geometry cellGeometry = Geometry2D.GetGeometry( p, q ); p1.RotateXY( Math.PI / 2 ); // XXX - repeated from above. Implement a better way to sequence transformations. Mobius m = new Mobius(); m.Isometry( cellGeometry, 0, -p1 ); return m; }