private static void Transform(Mesh mesh) { Sphere sphere = new Sphere(); sphere.Radius = 0.1; for (int i = 0; i < mesh.Triangles.Count; i++) { mesh.Triangles[i] = new Mesh.Triangle( sphere.ReflectPoint(mesh.Triangles[i].a), sphere.ReflectPoint(mesh.Triangles[i].b), sphere.ReflectPoint(mesh.Triangles[i].c)); } }
public void Reflect(Sphere sphere) { for (int i = 0; i < 4; i++) { Verts[i] = sphere.ReflectPoint(Verts[i]); } CalcFaces(); }
/// <summary> /// Reflect ourselves about another sphere. /// </summary> public void Reflect(Sphere sphere) { // An interior point used to calculate whether we get inverted. Vector3D interiorPoint; if (IsPlane) { Debug.Assert(!this.Normal.IsOrigin); interiorPoint = this.Offset - this.Normal; } else { // We don't want it to be the center, because that will reflect to infinity. interiorPoint = (this.Center + new Vector3D(this.Radius / 2, 0)); } if (Invert) { interiorPoint = ReflectPoint(interiorPoint); } Debug.Assert(IsPointInside(interiorPoint)); interiorPoint = sphere.ReflectPoint(interiorPoint); Debug.Assert(!interiorPoint.DNE); if (this.Equals(sphere)) { if (IsPlane) { //this.Center = -this.Center; // Same as inverting, but we need to do it this way because of Pov-Ray this.Invert = !this.Invert; } else { this.Invert = !this.Invert; } Debug.Assert(this.IsPointInside(interiorPoint)); return; } // Both planes? if (IsPlane && sphere.IsPlane) { // XXX - not general, but I know the planes I'll be dealing with go through the origin. //if( !sphere.Offset.IsOrigin ) // throw new System.NotImplementedException(); /*Vector3D p1 = this.Normal.Cross( sphere.Normal ); * if( !p1.Normalize() ) * { * this.Center *= -1; * return; * } * * Vector3D p2 = p1.Cross( this.Normal ); * p2.Normalize(); * p1 = sphere.ReflectPoint( p1 ); * p2 = sphere.ReflectPoint( p2 ); * Vector3D newNormal = p2.Cross( p1 ); * if( !newNormal.Normalize() ) * throw new System.Exception( "Reflection impl" ); * this.Center = newNormal;*/ // Reflect the normal relative to the plane (conjugate with sphere.Offset). Vector3D newNormal = this.Normal + sphere.Offset; newNormal = sphere.ReflectPoint(newNormal); newNormal -= sphere.Offset; newNormal.Normalize(); this.Center = newNormal; // Calc the new offset (so far we have considered planes through origin). this.Offset = sphere.ReflectPoint(this.Offset); //Debug.Assert( Offset.IsOrigin ); // XXX - should handle more generality. Debug.Assert(this.IsPointInside(interiorPoint)); return; } // We are a plane and reflecting in a sphere. if (IsPlane) { // Think of 2D case here (circle and line)... Vector3D projected = Euclidean3D.ProjectOntoPlane(this.Normal, this.Offset, sphere.Center); Vector3D p = sphere.ReflectPoint(projected); if (Infinity.IsInfinite(p)) { // This can happen if we go through sphere.Center. // This reflection does not change our orientation (does not invert us). return; } Center = sphere.Center + (p - sphere.Center) / 2; Radius = Center.Dist(sphere.Center); // Did this invert us? if (!this.IsPointInside(interiorPoint)) { Invert = !Invert; } return; } // Is mirror a plane? if (sphere.IsPlane) { Vector3D projected = Euclidean3D.ProjectOntoPlane(sphere.Normal, sphere.Offset, Center); Vector3D diff = Center - projected; Center -= 2 * diff; // Radius remains unchanged. // NOTE: This does not invert us. Debug.Assert(this.IsPointInside(interiorPoint)); return; } // // Now sphere reflecting in a sphere. // // Reflecting to a plane? if (IsPointOn(sphere.Center)) { // Concentric spheres? if (Center == sphere.Center) { throw new System.Exception(); } // Center Vector3D center = Center - sphere.Center; // Offset Vector3D direction = center; direction.Normalize(); Vector3D offset = direction * Radius * 2; offset = sphere.ReflectPoint(offset); // We are a line now. Center = center; //Offset = offset; // Not working?? Caused issues in old generation code for 435. Radius = double.PositiveInfinity; // Did this invert us? if (!this.IsPointInside(interiorPoint)) { this.Invert = !this.Invert; } Debug.Assert(this.IsPointInside(interiorPoint)); return; } // XXX - Could try to share code below with Circle class. // NOTE: We can't just reflect the center. // See http://mathworld.wolfram.com/Inversion.html double a = Radius; double k = sphere.Radius; Vector3D v = Center - sphere.Center; double s = k * k / (v.MagSquared() - a * a); Center = sphere.Center + v * s; Radius = Math.Abs(s) * a; // Did this invert us? if (!this.IsPointInside(interiorPoint)) { Invert = !Invert; } }
private static void ReflectEdgesRecursive(Sphere[] simplex, Edge[] edges, Settings settings, HashSet <Edge> completedEdges) { if (0 == edges.Length) { return; } HashSet <Edge> newEdges = new HashSet <Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Edge edge in edges) { //foreach( Sphere mirror in simplex ) for (int m = 0; m < simplex.Length; m++) { Sphere mirror = simplex[m]; if (completedEdges.Count > settings.MaxEdges) { throw new System.Exception("Maxing out edges - will result in uneven filling."); } Vector3D r1 = mirror.ReflectPoint(edge.Start); Vector3D r2 = mirror.ReflectPoint(edge.End); Edge newEdge = new Edge(r1, r2); newEdge.CopyDepthsFrom(edge); if (!EdgeOk(newEdge, settings)) { continue; } // This tracks reflections across the cell facets. newEdge.Depths[m]++; // Edge color. // This also controls resolution of the edges, and can have a big effect on file size. // Make the threshold cutoff black, or the background color. double percentWhite = 1; if (settings.ThreshType == EdgeThreshType.Length) { percentWhite = (r1.Dist(r2) - settings.Threshold) / 0.015; } else { double closestToOrigin = Math.Min(r1.Abs(), r2.Abs()); // Mainly ranges from 0 to 1 if (closestToOrigin < 0.9) { percentWhite = 1.0; } else { percentWhite = 1.0 - Math.Pow(closestToOrigin - 0.9, 1.3) / 0.1; } } if (percentWhite < 0) { percentWhite = 0; } if (percentWhite > 1) { percentWhite = 1; } //newEdge.Color = new Vector3D( percentWhite, percentWhite, percentWhite ); newEdge.Color = m_background; newEdge.Color.Z = 0.1 + 0.9 * percentWhite; if (completedEdges.Add(newEdge)) { // Haven't seen this edge yet, so // we'll need to recurse on it. newEdges.Add(newEdge); } } } ReflectEdgesRecursive(simplex, newEdges.ToArray(), settings, completedEdges); }
/// <summary> /// Reflect ourselves about another sphere. /// </summary> public void Reflect( Sphere sphere ) { // An interior point used to calculate whether we get inverted. Vector3D interiorPoint; if( IsPlane ) { Debug.Assert( !this.Normal.IsOrigin ); interiorPoint = -this.Normal; } else { // We don't want it to be the center, because that will reflect to infinity. interiorPoint = ( this.Center + new Vector3D( this.Radius / 2, 0 ) ); } if( Invert ) interiorPoint = ReflectPoint( interiorPoint ); Debug.Assert( IsPointInside( interiorPoint ) ); interiorPoint = sphere.ReflectPoint( interiorPoint ); Debug.Assert( !interiorPoint.DNE ); if( this.Equals( sphere ) ) { if( IsPlane ) { //this.Center = -this.Center; // Same as inverting, but we need to do it this way because of Pov-Ray this.Invert = !this.Invert; } else this.Invert = !this.Invert; Debug.Assert( this.IsPointInside( interiorPoint ) ); return; } // Both planes? if( IsPlane && sphere.IsPlane ) { // XXX - not general, but I know the planes I'll be dealing with go through the origin. //if( !sphere.Offset.IsOrigin ) // throw new System.NotImplementedException(); /*Vector3D p1 = this.Normal.Cross( sphere.Normal ); if( !p1.Normalize() ) { this.Center *= -1; return; } Vector3D p2 = p1.Cross( this.Normal ); p2.Normalize(); p1 = sphere.ReflectPoint( p1 ); p2 = sphere.ReflectPoint( p2 ); Vector3D newNormal = p2.Cross( p1 ); if( !newNormal.Normalize() ) throw new System.Exception( "Reflection impl" ); this.Center = newNormal;*/ // Reflect the normal relative to the plane (conjugate with sphere.Offset). Vector3D newNormal = this.Normal + sphere.Offset; newNormal = sphere.ReflectPoint( newNormal ); newNormal -= sphere.Offset; newNormal.Normalize(); this.Center = newNormal; // Calc the new offset (so far we have considered planes through origin). this.Offset = sphere.ReflectPoint( this.Offset ); //Debug.Assert( Offset.IsOrigin ); // XXX - should handle more generality. Debug.Assert( this.IsPointInside( interiorPoint ) ); return; } // We are a plane and reflecting in a sphere. if( IsPlane ) { // Think of 2D case here (circle and line)... Vector3D projected = Euclidean3D.ProjectOntoPlane( this.Normal, this.Offset, sphere.Center ); Vector3D p = sphere.ReflectPoint( projected ); if( Infinity.IsInfinite( p ) ) { // This can happen if we go through sphere.Center. // This reflection does not change our orientation (does not invert us). return; } Center = sphere.Center + ( p - sphere.Center ) / 2; Radius = Center.Dist( sphere.Center ); // Did this invert us? if( !this.IsPointInside( interiorPoint ) ) Invert = !Invert; return; } // Is mirror a plane? if( sphere.IsPlane ) { Vector3D projected = Euclidean3D.ProjectOntoPlane( sphere.Normal, sphere.Offset, Center ); Vector3D diff = Center - projected; Center -= 2 * diff; // Radius remains unchanged. // NOTE: This does not invert us. Debug.Assert( this.IsPointInside( interiorPoint ) ); return; } // // Now sphere reflecting in a sphere. // // Reflecting to a plane? if( IsPointOn( sphere.Center ) ) { // Concentric spheres? if( Center == sphere.Center ) throw new System.Exception(); // Center Vector3D center = Center - sphere.Center; // Offset Vector3D direction = center; direction.Normalize(); Vector3D offset = direction * Radius * 2; offset = sphere.ReflectPoint( offset ); // We are a line now. Center = center; //Offset = offset; // Not working?? Caused issues in old generation code for 435. Radius = double.PositiveInfinity; // Did this invert us? if( !this.IsPointInside( interiorPoint ) ) this.Invert = !this.Invert; Debug.Assert( this.IsPointInside( interiorPoint ) ); return; } // XXX - Could try to share code below with Circle class. // NOTE: We can't just reflect the center. // See http://mathworld.wolfram.com/Inversion.html double a = Radius; double k = sphere.Radius; Vector3D v = Center - sphere.Center; double s = k * k / ( v.MagSquared() - a * a ); Center = sphere.Center + v * s; Radius = Math.Abs( s ) * a; // Did this invert us? if( !this.IsPointInside( interiorPoint ) ) Invert = !Invert; }
/// <summary> /// Given 2 points in the interior of the ball, calculate the center and radius of the orthogonal circle. /// One point may optionally be on the boundary, but one shoudl be in the interior. /// If both points are on the boundary, we'll fall back on our other method. /// </summary> public static void OrthogonalCircleInterior( Vector3D v1, Vector3D v2, out Circle3D circle ) { if( Tolerance.Equal( v1.Abs(), 1 ) && Tolerance.Equal( v2.Abs(), 1 ) ) { circle = OrthogonalCircle( v1, v2 ); return; } // http://www.math.washington.edu/~king/coursedir/m445w06/ortho/01-07-ortho-to3.html // http://www.youtube.com/watch?v=Bkvo09KE1zo Vector3D interior = Tolerance.Equal( v1.Abs(), 1 ) ? v2 : v1; Sphere ball = new Sphere(); Vector3D reflected = ball.ReflectPoint( interior ); circle = new Circle3D( reflected, v1, v2 ); }
/// <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; }