// returns distance, 1e20 if nohit public double intersect(ref Ray r) { // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0 Vec op = p.Sub(ref r.o); double b = op.Dot(ref r.d); double det = b * b - op.Dot(ref op) + sqRad; double eps = 1e-4; if (det < 0) { return(1e20); } else { double dets = MathSqrt(det); if (b - dets > eps) { return(b - dets); } else if (b + dets > eps) { return(b + dets); } else { return(1e20); } } }
/// <summary> /// Construct ellipse from two conjugate diameters, and set /// Axis0 to the major axis and Axis1 to the minor axis. /// The algorithm was constructed from first principles. /// Also computes the squared lengths of the major and minor /// half axis. /// </summary> public static __et__ FromConjugateDiameters(__vt__ center, /*# if (d == 3) { */ __vt__ normal, /*# } */ __vt__ a, __vt__ b, out double major2, out double minor2) { var ab = Vec.Dot(a, b); double a2 = a.LengthSquared, b2 = b.LengthSquared; if (ab.IsTiny()) { if (a2 >= b2) { major2 = a2; minor2 = b2; return(new __et__(center, /*# if (d == 3) { */ normal, /*# } */ a, b)); } else { major2 = b2; minor2 = a2; return(new __et__(center, /*# if (d == 3) { */ normal, /*# } */ b, a)); } } else { var t = 0.5 * Fun.Atan2(2 * ab, a2 - b2); double ct = Fun.Cos(t), st = Fun.Sin(t); __vt__ v0 = a * ct + b * st, v1 = b * ct - a * st; a2 = v0.LengthSquared; b2 = v1.LengthSquared; if (a2 >= b2) { major2 = a2; minor2 = b2; return(new __et__(center, /*# if (d == 3) { */ normal, /*# } */ v0, v1)); } else { major2 = b2; minor2 = a2; return(new __et__(center, /*# if (d == 3) { */ normal, /*# } */ v1, v0)); } } }
public void Dot() { var p1 = new Vec(1, 2, 3); Assert.AreEqual(14, p1.Dot(new Vec(1, 2, 3))); Assert.AreEqual(0, p1.DotCross(new Vec(4, 5, 6), new Vec(4, 5, 6))); }
/// <summary> /// Enumerates all vertex indexes at which /// both outgoing edges meet at an angle that /// is less than the given threshold. /// </summary> public static IEnumerable <int> GetSpikes( this Polygon3d self, double toleranceInDegrees = 0.1) { if (toleranceInDegrees < 0.0) { throw new ArgumentOutOfRangeException(); } var threshold = Conversion.RadiansFromDegrees(toleranceInDegrees).Cos(); var edges = self.GetEdgeArray(); edges.Apply(e => e.Normalized); var ec = edges.Length; if (Vec.Dot(-edges[ec - 1], edges[0]) > threshold) { yield return(0); } for (int i = 1; i < ec; i++) { if (Vec.Dot(-edges[i - 1], edges[i]) > threshold) { yield return(i); } } }
public void TransformToPlane(Plane newPlane) { double eps = 1e-7; if (!PipeDataUtil.Equals(Vec.Dot(newPlane.Z, Plane.Z), 1, eps)) { throw new InvalidOperationException("Cannot transform arc to a new plane"); } if (Vec.Difference(newPlane.Origin, Plane.Origin).Length > eps) { throw new InvalidOperationException("The origins of the planes do not match"); } double angle = Vec.AngleBetween(Plane.X, newPlane.X); if (angle < eps) { return; } if (Vec.Dot(Vec.Cross(Plane.X, newPlane.X), Plane.Z) < 0) { angle *= -1; } Plane = new Plane(Plane.Origin, Plane.X.RotateAbout(Plane.Z, angle), Plane.Y.RotateAbout(Plane.Z, angle)); StartAngle -= angle; EndAngle -= angle; }
public override bool Intersect(Ray ray, Intersection isec) { var a = 1; var b = 2 * ray.Direction.Dot(ray.Origin - Center); var c = Center.Dot(Center) + ray.Origin.Dot(ray.Origin) - 2 * (Center.Dot(ray.Origin)) - Radius * Radius; var d = b * b - 4 * a * c; if (d < 0) { return(false); } var sqrtd = (Prec)Math.Sqrt(d); var t1 = (-b - sqrtd) / 2; //(2*a); var t2 = (-b + sqrtd) / 2; //(2*a); var t = t1; if (t1 < 0) { if (t2 < 0) { return(false); } t = t2; } else { if (t2 > 0 && t2 < t1) { t = t2; } } var p = ray.Origin + ray.Direction * t; var n = (p - Center); n.Normalize(); isec.Set(t, n, Material); return(true); }
public static double ComputeUnscaledFormFactor( this Polygon3d polygon, V3d p, V3d n, double eps = 1e-6) { var vc = polygon.PointCount; V3d[] cpa = new V3d[vc + 1]; var cc = 0; var pb = polygon[0] - p; var hb = Vec.Dot(pb, n); bool hbp = hb > eps, hbn = hb < -eps; if (hb >= -eps) { cpa[cc++] = pb; } var p0 = pb; var h0 = hb; var h0p = hbp; var h0n = hbn; for (int vi = 1; vi < vc; vi++) { var p1 = polygon[vi] - p; var h1 = Vec.Dot(p1, n); bool h1p = h1 > eps, h1n = h1 < -eps; if (h0p && h1n || h0n && h1p) { cpa[cc++] = p0 + (p1 - p0) * (h0 / (h0 - h1)); } if (h1 >= -eps) { cpa[cc++] = p1; } p0 = p1; h0 = h1; h0p = h1p; h0n = h1n; } if (h0p && hbn || h0n && hbp) { cpa[cc++] = p0 + (pb - p0) * (h0 / (h0 - hb)); } var cpr = cpa.Map(cc, v => v.Length); var cv = Vec.Cross(cpa[0], cpa[cc - 1]); double ff = Vec.Dot(n, cv) * Fun.AcosClamped(Vec.Dot(cpa[0], cpa[cc - 1]) / (cpr[0] * cpr[cc - 1])) / cv.Length; for (int ci = 0; ci < cc - 1; ci++) { cv = Vec.Cross(cpa[ci + 1], cpa[ci]); ff += Vec.Dot(n, cv) * Fun.AcosClamped(Vec.Dot(cpa[ci + 1], cpa[ci]) / (cpr[ci + 1] * cpr[ci])) / cv.Length; } return(ff); }
private static __mnnt__ Mgs(__mnnt__ m) { //# for (int j = 1; j < n; j++) { //# for (int k = 0; k < j; k++) { m.C__j__ -= (Vec.Dot(m.C__k__, m.C__j__) / Vec.Dot(m.C__k__, m.C__k__)) * m.C__k__; //# } //# } return(m); }
public static IEnumerable <Pt> ClipPolygonToLine(Pt a, Pt b, IEnumerable <Pt> pts) { Vec N = (b - a).Perp(); double D = -N.Dot((Vec)a); bool first = true; Pt S = new Pt(), F = new Pt(); // compiler is unable to prove these don't need initialization here foreach (Pt P in pts) { double v1, v2; v2 = N.Dot((Vec)P) + D; if (first) { S = P; F = P; first = false; } else { v1 = N.Dot((Vec)S) + D; if (v1 * v2 < 0) { yield return(S + (P - S) * v1 / (v1 - v2)); } S = P; } if (v2 >= 0) { yield return(P); } } if (!first) { double v1, v2; v2 = N.Dot((Vec)F) + D; v1 = N.Dot((Vec)S) + D; if (v1 * v2 < 0) { yield return(S + (F - S) * v1 / (v1 - v2)); } } }
// https://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another public static Rot3f HalfWayVec(V3f from, V3f into) { if (from.Dot(into).ApproximateEquals(-1)) { return(new Rot3f(0, from.AxisAlignedNormal())); } else { V3f half = Vec.Normalized(from + into); QuaternionF q = new QuaternionF(Vec.Dot(from, half), Vec.Cross(from, half)); return(new Rot3f(q.Normalized)); } }
// https://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another public static __rot3t__ HalfWayVec(__v3t__ from, __v3t__ into) { if (from.Dot(into).ApproximateEquals(-1)) { return(new __rot3t__(0, from.AxisAlignedNormal())); } else { __v3t__ half = Vec.Normalized(from + into); __quatt__ q = new __quatt__(Vec.Dot(from, half), Vec.Cross(from, half)); return(new __rot3t__(q.Normalized)); } }
private static __mnnt__ MgsNorm(__mnnt__ m) { m.C0 = m.C0.Normalized; //# for (int j = 1; j < n; j++) { //# for (int k = 0; k < j; k++) { m.C__j__ -= (Vec.Dot(m.C__k__, m.C__j__) / Vec.Dot(m.C__k__, m.C__k__)) * m.C__k__; //# } m.C__j__ = m.C__j__.Normalized; //# } return(m); }
public static Rot3f HalfWayQuat(V3f from, V3f into) { var d = Vec.Dot(from, into); if (d.ApproximateEquals(-1)) { return(new Rot3f(0, from.AxisAlignedNormal())); } else { QuaternionF q = new QuaternionF(d + 1, Vec.Cross(from, into)); return(new Rot3f(q.Normalized)); } }
public static __rot3t__ HalfWayQuat(__v3t__ from, __v3t__ into) { var d = Vec.Dot(from, into); if (d.ApproximateEquals(-1)) { return(new __rot3t__(0, from.AxisAlignedNormal())); } else { __quatt__ q = new __quatt__(d + 1, Vec.Cross(from, into)); return(new __rot3t__(q.Normalized)); } }
public void ValueType_Vec() { var p1 = new Vec(1, 0, 0); var p2 = new Vec(0, 1, 0); Assert.IsFalse(p1.IsEqual(p2, 0.99, 0.1)); Assert.IsTrue(p1.IsEqual(p2, 1.01, 0.1)); Assert.IsTrue(p1.IsEqual(p2, 0.99, Math.PI / 2)); Assert.IsTrue(p1.IsNormal(p2, 0.1)); Assert.IsFalse(p1.IsOpposite(p2, 0.1)); Assert.IsTrue(p1.IsOpposite(p2, Math.PI / 2)); Assert.IsFalse(p1.IsParallel(p2, 0.1)); Assert.IsTrue(p1.IsParallel(p2, Math.PI / 2)); p1 = new Vec(1, 2, 3); p2 = new Vec(4, 5, 6); Assert.AreEqual(14, p1.SquareMagnitude()); Assert.AreEqual(Math.Sqrt(14), p1.Magnitude()); p2 = p1; p2.Add(new Vec(1, 2, 3)); Assert.AreEqual(new Vec(2, 4, 6), p2); Assert.AreEqual(new Vec(2, 4, 6), p1.Added(new Vec(1, 2, 3))); p2 = new Vec(1, 2, 3); p2.Subtract(new Vec(3, 2, 1)); Assert.AreEqual(new Vec(-2, 0, 2), p2); Assert.AreEqual(new Vec(-2, 0, 2), p1.Subtracted(new Vec(3, 2, 1))); p2 = new Vec(1, 2, 3); p2.Cross(new Vec(3, 2, 1)); Assert.AreEqual(new Vec(-4, 8, -4), p2); Assert.AreEqual(new Vec(-4, 8, -4), p1.Crossed(new Vec(3, 2, 1))); Assert.AreEqual(Math.Sqrt(96), p1.CrossMagnitude(new Vec(3, 2, 1))); Assert.AreEqual(96, p1.CrossSquareMagnitude(new Vec(3, 2, 1))); p2 = new Vec(1, 2, 3); p2.CrossCross(new Vec(1, 2, 3), new Vec(4, 5, 6)); Assert.AreEqual(new Vec(-24, -6, 12), p2); Assert.AreEqual(new Vec(-24, -6, 12), p1.CrossCrossed(new Vec(1, 2, 3), new Vec(4, 5, 6))); p2 = new Vec(1, 2, 3); p2.Divide(2); Assert.AreEqual(new Vec(0.5, 1, 1.5), p2); Assert.AreEqual(new Vec(0.5, 1, 1.5), p1.Divided(2)); Assert.AreEqual(14, p1.Dot(new Vec(1, 2, 3))); Assert.AreEqual(0, p1.DotCross(new Vec(4, 5, 6), new Vec(4, 5, 6))); p2 = new Vec(1, 2, 3); p2.Multiply(2); Assert.AreEqual(new Vec(2, 4, 6), p2); Assert.AreEqual(new Vec(2, 4, 6), p1.Multiplied(2)); p2 = new Vec(1, 2, 3); p2.Scale(2); Assert.AreEqual(new Vec(2, 4, 6), p2); Assert.AreEqual(new Vec(2, 4, 6), p1.Scaled(2)); p2 = new Vec(1, 2, 3); p2.Normalize(); Assert.IsTrue(p2.IsEqual(new Vec(0.26726, 0.53452, 0.80178), 0.00001, 0.00001)); Assert.IsTrue(p1.Normalized().IsEqual(new Vec(0.26726, 0.53452, 0.80178), 0.00001, 0.00001)); p2 = new Vec(1, 2, 3); p2.Reverse(); Assert.AreEqual(new Vec(-1, -2, -3), p2); Assert.AreEqual(new Vec(-1, -2, -3), p1.Reversed()); p2.SetLinearForm(new Vec(1, 2, 3), new Vec(4, 5, 6)); Assert.AreEqual(new Vec(5, 7, 9), p2); p2.SetLinearForm(2, new Vec(1, 2, 3), new Vec(4, 5, 6)); Assert.AreEqual(new Vec(6, 9, 12), p2); p2.SetLinearForm(2, new Vec(1, 2, 3), 3, new Vec(4, 5, 6)); Assert.AreEqual(new Vec(14, 19, 24), p2); p2.SetLinearForm(2, new Vec(1, 2, 3), 3, new Vec(4, 5, 6), new Vec(7, 8, 9)); Assert.AreEqual(new Vec(21, 27, 33), p2); p2.SetLinearForm(2, new Vec(1, 2, 3), 3, new Vec(4, 5, 6), 4, new Vec(7, 8, 9)); Assert.AreEqual(new Vec(42, 51, 60), p2); p2.SetLinearForm(2, new Vec(1, 2, 3), 3, new Vec(4, 5, 6), 4, new Vec(7, 8, 9), new Vec(10, 11, 12)); Assert.AreEqual(new Vec(52, 62, 72), p2); p2 = new Vec(2, 0, 0); p2.Mirror(new Vec(0, 1, 0)); Assert.AreEqual(new Vec(-2, 0, 0), p2); Assert.AreEqual(new Vec(2, 0, 0), p2.Mirrored(new Vec(0, 1, 0))); var m2 = new Ax1(new Pnt(-1, 2, -3), new Dir(-1, 0, 0)); p2 = new Vec(2, 1, 3); Assert.AreEqual(new Vec(2, -1, -3), p2.Mirrored(m2)); p2.Mirror(m2); Assert.AreEqual(new Vec(2, -1, -3), p2); var a2 = new Ax2(new Pnt(-1, 2, -3), new Dir(-1, 0, 0)); p2 = new Vec(2, 1, 3); Assert.AreEqual("-2,1,3", p2.Mirrored(a2).ToString()); p2.Mirror(a2); Assert.AreEqual("-2,1,3", p2.ToString()); p2 = new Vec(2, 1, 3); Assert.IsTrue(new Vec(2, 3, -1).IsEqual(p2.Rotated(m2, Math.PI / 2), 0.0001, 0.0001)); p2.Rotate(m2, Math.PI / 2); Assert.IsTrue(new Vec(2, 3, -1).IsEqual(p2, 0.0001, 0.0001)); //TestContext.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", gp2.x, gp2.y, gp2.z)); Trsf t1 = new Trsf(); t1.SetRotation(Ax1.OZ, Math.PI / 2); p2 = new Vec(4, 5, 6); Assert.AreEqual("-5,4,6", p2.Transformed(t1).ToString()); p2.Transform(t1); Assert.AreEqual("-5,4,6", p2.ToString()); }
/// <summary> /// Creates a plane from 3 independent points. /// A normalized normal vector is computed and stored. /// </summary> public Plane3d(V3d p0, V3d p1, V3d p2) { Normal = Vec.Cross(p1 - p0, p2 - p0).Normalized; Distance = Vec.Dot(Normal, p0); }
/// <summary> /// Creates plane from normal vector and point. /// IMPORTANT: The normal has to be normalized in order for all methods /// to work correctly, however if only relative height computations /// using the <see cref="Height"/> method are necessary, the normal /// vector need not be normalized. /// </summary> public Plane3d(V3d normalizedNormal, V3d point) { Normal = normalizedNormal; Distance = Vec.Dot(normalizedNormal, point); }
/// <summary> /// The signed height of the supplied point over the plane. /// </summary> public double Height(V3d p) => Vec.Dot(Normal, p - Point);
public static unsafe Vec radiance(ref Ray r, int depth, ref RandomLCG rand) { double t; // distance to intersection Sphere *obj = intersect(ref r, out t); if (obj == null) { return(Vec_Zero); // if miss, return black } else { int newDepth = depth + 1; bool isMaxDepth = newDepth > 100; // Russian roulette for path termination bool isUseRR = newDepth > 5; bool isRR = isUseRR && rand.NextNumber() < obj->maxC; if (isMaxDepth || (isUseRR && !isRR)) { return(obj->e); } else { Vec f = (isUseRR && isRR) ? obj->cc : obj->c; var tmp1 = r.d.Mul(t); Vec x = r.o.Add(ref tmp1); Vec n = (x.Sub(ref obj->p)).Norm(); Vec nl = n.Dot(ref r.d) < 0 ? n : n.Mul(-1); if (obj->refl == MatType.DIFF) { // Ideal DIFFUSE reflection double r1 = 2 * M_PI * rand.NextNumber(); double r2 = rand.NextNumber(); double r2s = MathSqrt(r2); Vec w = nl; Vec wo = w.x <-0.1 || w.x> 0.1 ? Vec_YAxis : Vec_XAxis; Vec u = (wo.Cross(ref w)).Norm(); Vec v = w.Cross(ref u); var tmp2 = v.Mul(MathSin(r1)).Mul(r2s); var tmp3 = w.Mul(MathSqrt(1 - r2)); Vec d = (u.Mul(MathCos(r1)).Mul(r2s).Add(ref tmp2).Add(ref tmp3)).Norm(); var tmp4 = new Ray(ref x, ref d); var tmp5 = radiance(ref tmp4, newDepth, ref rand); var tmp6 = f.Mul(ref tmp5); return(obj->e.Add(ref tmp6)); } else if (obj->refl == MatType.SPEC) // Ideal SPECULAR reflection { var tmp8 = n.Mul(2 * (n.Dot(ref r.d))); var tmp9 = r.d.Sub(ref tmp8); var tmp7 = new Ray(ref x, ref tmp9); var tmp10 = radiance(ref tmp7, newDepth, ref rand); var tmp11 = f.Mul(ref tmp10); return(obj->e.Add(ref tmp11)); } else { // Ideal dielectric REFRACTION var tmp100 = n.Mul(2 * (n.Dot(ref r.d))); var tmp101 = r.d.Sub(ref tmp100); Ray reflRay = new Ray(ref x, ref tmp101); bool into = n.Dot(ref nl) > 0; // Ray from outside going in? double nc = 1; double nt = 1.5; double nnt = into ? nc / nt : nt / nc; double ddn = r.d.Dot(ref nl); double cos2t = 1 - nnt * nnt * (1 - ddn * ddn); if (cos2t < 0) // Total internal reflection { var tmp12 = radiance(ref reflRay, newDepth, ref rand); var tmp13 = f.Mul(ref tmp12); return(obj->e.Add(ref tmp13)); } else { var tmp14 = n.Mul((into ? 1 : -1) * (ddn * nnt + MathSqrt(cos2t))); Vec tdir = (r.d.Mul(nnt).Sub(ref tmp14)).Norm(); double a = nt - nc; double b = nt + nc; double R0 = (a * a) / (b * b); double c = 1 - (into ? -ddn : tdir.Dot(ref n)); double Re = R0 + (1 - R0) * c * c * c * c * c; double Tr = 1 - Re; double P = .25 + .5 * Re; double RP = Re / P; double TP = Tr / (1 - P); Vec result; if (newDepth > 2) { // Russian roulette and splitting for selecting reflection and/or refraction if (rand.NextNumber() < P) { result = radiance(ref reflRay, newDepth, ref rand).Mul(RP); } else { var tmp15 = new Ray(ref x, ref tdir); result = radiance(ref tmp15, newDepth, ref rand).Mul(TP); } } else { var tmp16 = new Ray(ref x, ref tdir); var tmp17 = radiance(ref tmp16, newDepth, ref rand).Mul(Tr); result = radiance(ref reflRay, newDepth, ref rand).Mul(Re).Add(ref tmp17); } var tmp18 = f.Mul(ref result); return(obj->e.Add(ref tmp18)); } } } } }
/// <summary> /// Creates plane from point and normal vector. IMPORTANT: The /// supplied vector has to be normalized in order for all methods /// to work correctly, however if only relative height computations /// using the <see cref="Height"/> method are necessary, the normal /// vector need not be normalized. /// </summary> public Plane2d(V2d normalizedNormal, V2d point) { Normal = normalizedNormal; Distance = Vec.Dot(normalizedNormal, point); }
/// <summary> /// The signed height of the supplied point over the plane. /// </summary> public double Height(V3d p) => Vec.Dot(Normal, p) - Distance;
public void RasterTest(uint raster) { int iraster = (int)raster; V3fCoder coder = new V3fCoder(raster); uint step = 1; bool large = false; if (iraster > 52) { step = 7; large = true; } if (iraster > 591) { step = 63; } if (iraster > 6688) { step = 2039; } int bits = (int)Fun.Ceiling(Fun.Log2(coder.Count)); Test.Begin("normal coder raster {0} ({1} bits)", iraster, bits); /* * Console.WriteLine(" raster = {0}", m_raster); * Console.WriteLine(" rasterMul2Sub1 = {0}", m_r2Sub1); * Console.WriteLine(" doubleRaster = {0}", m_doubleRaster); * Console.WriteLine(" invDoubleRaster = {0}", m_invDoubleRaster); * Console.WriteLine(" edgeBasis = {0}", m_edgeBasis); * Console.WriteLine(" cornerBasis = {0}", m_cornerBasis); */ Test.Begin("testing {0} of {1} codes", 1 + ((long)coder.Count - 1) / (long)step, coder.Count); for (uint code = 0; code < coder.Count; code += step) { V3f dir = coder.Decode(code); uint newCode = coder.Encode(dir); Test.IsTrue(code == newCode); } Test.End(); double minDot = 1.0; float eps = Constant <float> .PositiveTinyValue; float[] factorTable = { 1.0f - eps, 1.0f, 1.0f + eps }; for (int sign = -1; sign < 2; sign += 2) { for (int axis = 0; axis < 3; axis++) { float factor = factorTable[axis]; for (int xi = -2 * iraster; xi <= 2 * iraster; xi++) { if (large && (xi > 3 - 2 * iraster) && (xi < -3)) { continue; } if (large && (xi < 2 * iraster - 3) && (xi > +3)) { continue; } double x = (double)xi * factor / (2 * iraster); #if (!V3FCODER_NO_WARP) x = V3fCoder.SphericalOfBox(x); #endif for (int yi = -2 * iraster; yi <= 2 * iraster; yi++) { if (large && (yi > 3 - 2 * iraster) && (yi < -3)) { continue; } if (large && (yi < 2 * iraster - 3) && (yi > +3)) { continue; } double y = (double)yi * factor / (2 * iraster); #if (!V3FCODER_NO_WARP) y = V3fCoder.SphericalOfBox(y); #endif V3f n = new V3f(0, 0, 0); // init to make compiler h. n[axis] = sign; n[(axis + 1) % 3] = (float)x; n[(axis + 2) % 3] = (float)y; n.Normalize(); uint code = coder.Encode(n); V3f newN = coder.Decode(code); double newDot = Vec.Dot(n, newN); if (newDot < minDot) { minDot = newDot; } } } } } double maxErr = System.Math.Acos(minDot) * 180.0 / System.Math.PI; Report.Line("maximal error {0:g4} degrees", maxErr); Test.End(); }