/// <summary> /// Helper to do a geodesic pan. /// </summary> private void GeodesicPan(Vector3D p1, Vector3D p2) { Mobius pan = new Mobius(); Isometry inverse = m_isometry.Inverse(); p1 = inverse.Apply(p1); p2 = inverse.Apply(p2); pan.Geodesic(m_geometry, p1, p2); m_isometry.Mobius *= pan; }
/// <summary> /// Transforms us into a new macro based on a different click location. /// </summary> public Macro Transform(Cell clickedCell, Vector3D clickedPoint, Puzzle puzzle, bool mouseMotionReflected) { Macro m = this.CloneAllButTwists(); m.SetupMobius(clickedCell, clickedPoint, puzzle, mouseMotionReflected); // Did we have an odd number of view reflections? bool viewReflected = this.ViewReflected ^ m.ViewReflected; Isometry iso1 = new Isometry(m.Mobius, null); Isometry iso2 = new Isometry(this.Mobius, null); if (viewReflected) { iso1 = Isometry.ReflectX() * iso1; } Isometry combined = iso1.Inverse() * iso2; foreach (SingleTwist t in this.m_twists) { // Find the transformed twist data. // NOTE: We choose the one which will be closest to the origin after transformation, // which hopefully won't lead to performance problems. // I initially just used the first TwistDataForStateCalcs list item, // but that led to issues because sometimes it would get transformed // to very near the disk boundary. We'd have run out of cells to // find the correct closest, and the transformed macros got all messed up. TwistData tdOriginal = t.IdentifiedTwistData.TwistDataForStateCalcs .OrderBy(td => combined.Apply(td.Center).MagSquared()) .First(); Vector3D newCenter = combined.Apply(tdOriginal.Center); TwistData tdNew = puzzle.ClosestTwistingCircles(newCenter); SingleTwist tClone = t.Clone(); tClone.IdentifiedTwistData = tdNew.IdentifiedTwistData; // If the reverse state of our transformed twist // has changed, we may need to reverse the new twist. bool reverse = tdOriginal.Reverse ^ tdNew.Reverse; if (reverse ^ viewReflected) // NOTE: Very similar to code in Renderer. { tClone.ReverseTwist(); } m.m_twists.Add(tClone); } return(m); }
private static Isometry SetupIsometry(Cell clickedCell, Vector3D clickedPoint, Puzzle puzzle) { int p = puzzle.Config.P; Geometry g = puzzle.Config.Geometry; Isometry cellIsometry = clickedCell.Isometry.Clone(); // Take out reflections. // ZZZ - Figuring out how to deal with these reflected isometries was a bit painful to figure out. // I wish I had just taken more care to not have any cell isometries with reflections. // Maybe I can rework that to be different at some point. if (cellIsometry.Reflection != null) { cellIsometry = Isometry.ReflectX() * cellIsometry; } // Round to nearest vertex. Vector3D centered = cellIsometry.Apply(clickedPoint); double angle = Euclidean2D.AngleToCounterClock(centered, new Vector3D(1, 0)); double angleFromZeroToP = p * angle / (2 * Math.PI); angleFromZeroToP = Math.Round(angleFromZeroToP, 0); if (p == (int)angleFromZeroToP) { angleFromZeroToP = 0; } angle = 2 * Math.PI * angleFromZeroToP / p; // This will take vertex to canonical position. Mobius rotation = new Mobius(); rotation.Isometry(g, angle, new Complex()); Isometry rotIsometry = new Isometry(rotation, null); return(rotIsometry * cellIsometry); }
/// <summary> /// Apply an isometry to us. /// </summary> public void Transform(Isometry isometry) { foreach (Segment s in this.Segments) { s.Transform(isometry); } Center = isometry.Apply(Center); }
public static void DrawPolygonSolid(Polygon p, Isometry isometry, Color color) { GL.Color3(color); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); GL.Begin(BeginMode.TriangleFan); { Vector3D center = isometry.Apply(p.Center); GL.Vertex2(center.X, center.Y); Vector3D[] edgePoints = p.EdgePoints; for (int i = 0; i < edgePoints.Length; i++) { Vector3D draw = isometry.Apply(edgePoints[i]); GL.Vertex2(draw.X, draw.Y); } } GL.End(); }
public static void DrawElements(HyperbolicModel model, Vector3D[] textureCoords, Vector3D[] textureVerts, int[] elements, Isometry mouseIsometry, double textureScale) { ////////////////////// ZZZ - Use VBOs GL.Begin(BeginMode.Triangles); { double factor = textureScale; int skipped = 0; for (int i = 0; i < elements.Length; i++) { int idx = elements[i]; // In Poincare model. GL.TexCoord2((textureCoords[idx].X * factor + 1) / 2, (textureCoords[idx].Y * factor + 1) / 2); Complex transformed = textureVerts[idx].ToComplex(); if (mouseIsometry != null) { transformed = mouseIsometry.Apply(transformed); } switch (model) { case HyperbolicModel.Poincare: { Vertex(transformed); break; } case HyperbolicModel.Klein: { Vector3D temp = Vector3D.FromComplex(transformed); Vertex(PoincareToKlein(temp)); break; } case HyperbolicModel.Pseudosphere: { Mobius m = new Mobius(); m.UpperHalfPlane(); Complex u = m.Apply(transformed); double x = u.Real; double y = u.Imaginary; double max = 1 * System.Math.PI; double min = -1 * System.Math.PI; if (0 == i % 3 && (x < min - 1 || x > max + 1 || y < 0)) { skipped = 1; continue; } if (skipped > 0 && skipped < 3) { skipped++; continue; } skipped = 0; GL.TexCoord2((textureCoords[idx].X * factor + 1) / 2, (textureCoords[idx].Y * factor + 1) / 2); // Pseudosphere Func <double, Complex> tractrix = new Func <double, Complex>( (t) => { return(new Complex(t - Math.Tanh(t), 1.0 / Math.Cosh(t))); }); //Vector3D temp1 = Vector3D.FromComplex( u ); if (x < min) { x = min; } if (x > max) { x = max; } if (y < 1) { y = 1; } Vector3D temp1 = new Vector3D(x, y); double logy = Math.Log(temp1.Y); Complex tract = tractrix(logy); Vector3D temp2 = new Vector3D( Math.Cos(temp1.X) * tract.Imaginary, Math.Sin(temp1.X) * tract.Imaginary, tract.Real); GL.Vertex3(temp2.X, temp2.Y, temp2.Z); //temp1 = m.Inverse().Apply( temp1 ); //GL.Vertex3( temp1.X, temp1.Y, temp1.Z ); //Vertex( temp1 ); break; } case HyperbolicModel.Hyperboloid: { Vector3D hyper = Sterographic.PlaneToHyperboloid(Vector3D.FromComplex(transformed)); // Hyperboloid GL.Vertex3(hyper.X, hyper.Y, hyper.Z); break; } default: { System.Diagnostics.Debug.Assert(false); break; } } /* // PETALS * int petals = 7; * double newMag = transformed.Magnitude * ( 1 + 0.5 * Math.Sin( transformed.Phase * petals ) ); * double newPhase = transformed.Phase + ( -0.2 * newMag * Math.Pow( Math.Sin( newMag * 3 ), 1 ) * Math.Cos( transformed.Phase * petals ) ); * transformed = Complex.FromPolarCoordinates( newMag, newPhase ); * * Vertex( transformed ); * */ //double mag = System.Math.Pow( transformed.Magnitude, 3 ) / transformed.Magnitude; // nice //double mag = System.Math.Pow( transformed.Magnitude - 3, 2 ) + .0; // looks spherical //double mag = transformed.Magnitude + 0.1* System.Math.Sin( transformed.Magnitude * 15 ); // Fun warping (20 is cool too) //Vertex( transformed * mag ); /*double xmag = 1; * double ymag = transformed.Imaginary + 0.1 * System.Math.Sin( transformed.Imaginary * 15 ); * xmag = System.Math.Abs( xmag ); * ymag = System.Math.Abs( ymag ); * Vertex( new Complex( transformed.Real * xmag, transformed.Imaginary * ymag ) ); */ //Vertex( 2 / System.Math.PI * Complex.Log( ( 1 + transformed ) / ( 1 - transformed ) ) ); // Band model //Vertex( Complex.Pow( transformed, 3 ) / transformed.Magnitude ); // Spikey // Spiral //Complex band = 2 / System.Math.PI * Complex.Log( ( 1 + transformed ) / ( 1 - transformed ) ); //band = new Complex( band.Real, band.Imaginary + 0.3 * System.Math.Sin( band.Real * 2 ) ); //band = new Complex( band.Real * .5, band.Imaginary ); //band += new Complex( 0, .5 ); //Vertex( band ); /* * double x = band.Real; * double y = band.Imaginary; * * double r = System.Math.Exp( x ); * double theta = 3*( x + y/1.75 ); */ //Vertex( new Complex( r * System.Math.Sin( theta ), r * System.Math.Cos( theta ) ) ); // Spiral } } GL.End(); }
public override void Transform(Isometry i) { base.Transform(i); CenterNE = i.Apply(CenterNE); }