// from https://cesiumjs.org/2013/05/09/Computing-the-horizon-occlusion-point/ public static Vector3 FromPoints(ref Vector3[] points, BBSphere boundingSphere) { if (points.Length < 1) { throw new ArgumentException("Your list of points must contain at least 2 points"); } // Bring coordinates to ellipsoid scaled coordinates for (int i = 0; i < points.Length; i++) { points[i].X = points[i].X * rX; points[i].Y = points[i].Y * rY; points[i].Z = points[i].Z * rZ; } boundingSphere.Center.X = boundingSphere.Center.X * rX; boundingSphere.Center.Y = boundingSphere.Center.Y * rY; boundingSphere.Center.Z = boundingSphere.Center.Z * rZ; Double maxMagnitude = double.NegativeInfinity; for (int i = 0; i < points.Length; i++) { //magnitudes.Add(ComputeMagnitude(points[i], boundingSphere.Center)); var magnitude = ComputeMagnitude(points[i], boundingSphere.Center); if (magnitude > maxMagnitude) { maxMagnitude = magnitude; } } return(Cartesian3D.MultiplyByScalar(boundingSphere.Center, maxMagnitude)); }
// Functions assumes ellipsoid scaled coordinates public static Double ComputeMagnitude(Vector3 point, Vector3 sphereCenter) { var magnitudeSquared = Cartesian3D.MagnitudeSquared(point); var magnitude = Math.Sqrt(magnitudeSquared); var direction = Cartesian3D.MultiplyByScalar(point, 1 / magnitude); magnitudeSquared = Math.Max(1.0, magnitudeSquared); magnitude = Math.Max(1.0, magnitude); var cosAlpha = DotProduct(direction, sphereCenter); var sinAlpha = Cartesian3D.Magnitude(CrossProduct(direction, sphereCenter)); var cosBeta = 1.0 / magnitude; var sinBeta = Math.Sqrt(magnitudeSquared - 1.0) * cosBeta; return(1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta)); }
/// <summary> /// Encodes vertec normal into two bytes /// </summary> /// <param name="vector"></param> /// <returns></returns> public static Vector2 OctEncode(Vector3 vector) { Vector2 result = new Vector2(0, 0); var rangeMax = 255; var EPSILON6 = 0.000001; var magSquared = Cartesian3D.MagnitudeSquared(vector); if (Math.Abs(magSquared - 1.0) > EPSILON6) { throw new ArithmeticException("vector must be normalized."); } result.X = vector.X / (Math.Abs(vector.X) + Math.Abs(vector.Y) + Math.Abs(vector.Z)); result.Y = vector.Y / (Math.Abs(vector.X) + Math.Abs(vector.Y) + Math.Abs(vector.Z)); if (vector.Z < 0) { var x = result.X; var y = result.Y; result.X = (1.0 - Math.Abs(y)) * SignNotZero(x); result.Y = (1.0 - Math.Abs(x)) * SignNotZero(y); } result.X = ToSNorm(result.X, rangeMax); result.Y = ToSNorm(result.Y, rangeMax); return(result); }
// Based on Ritter's algorithm public void FromPoints(Vector3[] points) // ECFC { nbPositions = points.Length; if (nbPositions < 2) { throw new ArgumentException("Your list of points must contain at least 2 points"); } for (int i = 0; i < nbPositions; i++) { var point = points[i]; // Store the points containing the smallest and largest component used for the naive approach if (point.X < minPointX.X) { minPointX = point; } if (point.Y < minPointY.Y) { minPointY = point; } if (point.Z < minPointZ.Z) { minPointZ = point; } if (point.X > maxPointX.X) { maxPointX = point; } if (point.Y > maxPointY.Y) { maxPointY = point; } if (point.Z > maxPointZ.Z) { maxPointZ = point; } } // Squared distance between each component min and max var xSpan = Cartesian3D.MagnitudeSquared(Cartesian3D.Subtract(maxPointX, minPointX)); var ySpan = Cartesian3D.MagnitudeSquared(Cartesian3D.Subtract(maxPointY, minPointY)); var zSpan = Cartesian3D.MagnitudeSquared(Cartesian3D.Subtract(maxPointZ, minPointZ)); var diameter1 = minPointX; var diameter2 = maxPointX; var maxSpan = xSpan; if (ySpan > maxSpan) { maxSpan = ySpan; diameter1 = minPointY; diameter2 = maxPointY; } if (zSpan > maxSpan) { maxSpan = zSpan; diameter1 = minPointZ; diameter2 = maxPointZ; } var ritterCenter = new Vector3() { X = (diameter1.X + diameter2.X) * 0.5, Y = (diameter1.Y + diameter2.Y) * 0.5, Z = (diameter1.Z + diameter2.Z) * 0.5 }; var radiusSquared = Cartesian3D.MagnitudeSquared(Cartesian3D.Subtract(diameter2, ritterCenter)); var ritterRadius = Math.Sqrt(radiusSquared); // Initial center and radius (naive) get min and max box var minBoxPt = new Vector3() { X = minPointX.X, Y = minPointY.Y, Z = minPointZ.Z }; var maxBoxPt = new Vector3() { X = maxPointX.X, Y = maxPointY.Y, Z = maxPointZ.Z }; var naiveCenter = Cartesian3D.MultiplyByScalar(Cartesian3D.Add(minBoxPt, maxBoxPt), 0.5); var naiveRadius = 0.0; for (int i = 0; i < nbPositions; i++) // foreach (var i in xrange(0, nbPositions)) { var currentP = points[i]; // Find the furthest point from the naive center to calculate the naive radius. var r = Cartesian3D.Magnitude(Cartesian3D.Subtract(currentP, naiveCenter)); if (r > naiveRadius) { naiveRadius = r; } // Make adjustments to the Ritter Sphere to include all points. var oldCenterToPointSquared = Cartesian3D.MagnitudeSquared(Cartesian3D.Subtract(currentP, ritterCenter)); if (oldCenterToPointSquared > radiusSquared) { var oldCenterToPoint = Math.Sqrt(oldCenterToPointSquared); ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; // Calculate center of new Ritter sphere var oldToNew = oldCenterToPoint - ritterRadius; ritterCenter = new Vector3() { X = (ritterRadius * ritterCenter.X + oldToNew * currentP.X) / oldCenterToPoint, Y = (ritterRadius * ritterCenter.Y + oldToNew * currentP.Y) / oldCenterToPoint, Z = (ritterRadius * ritterCenter.Z + oldToNew * currentP.Z) / oldCenterToPoint }; } } // Keep the naive sphere if smaller if (naiveRadius < ritterRadius) { Radius = (float)ritterRadius; Center = ritterCenter; } else { Radius = (float)naiveRadius; Center = naiveCenter; } }