private static FloatRgb Mix(FloatRgb a, FloatRgb b, double weight) { var output = new FloatRgb { R = Arithmetic.Mix(a.R, b.R, weight), G = Arithmetic.Mix(a.G, b.G, weight), B = Arithmetic.Mix(a.B, b.B, weight) }; return(output); }
public override unsafe SignedDistance SignedDistance(Vector2 origin, ref double param) { var qa = _p[0] - origin; var ab = _p[1] - _p[0]; var br = _p[0] + _p[2] - _p[1] - _p[1]; var coefficient = stackalloc[] { Vector2.Dot(br, br), 3 * Vector2.Dot(ab, br), 2 * Vector2.Dot(ab, ab) + Vector2.Dot(qa, br), Vector2.Dot(qa, ab) }; var t = stackalloc double[3]; var solutions = Equations.SolveCubic(t, coefficient); var minDistance = Arithmetic.NonZeroSign(Vector2.Cross(ab, qa)) * qa.Length(); // distance from A param = -Vector2.Dot(qa, ab) / Vector2.Dot(ab, ab); { var distance = Arithmetic.NonZeroSign(Vector2.Cross(_p[2] - _p[1], _p[2] - origin)) * (_p[2] - origin).Length(); // distance from B if (Math.Abs(distance) < Math.Abs(minDistance)) { minDistance = distance; param = Vector2.Dot(origin - _p[1], _p[2] - _p[1]) / Vector2.Dot(_p[2] - _p[1], _p[2] - _p[1]); } } for (var i = 0; i < solutions; ++i) { if (t[i] > 0 && t[i] < 1) { var endpoint = _p[0] + 2 * t[i] * ab + t[i] * t[i] * br; var distance = Arithmetic.NonZeroSign(Vector2.Cross(_p[2] - _p[0], endpoint - origin)) * (endpoint - origin).Length(); if (Math.Abs(distance) <= Math.Abs(minDistance)) { minDistance = distance; param = t[i]; } } } if (param >= 0 && param <= 1) { return(new SignedDistance(minDistance, 0)); } if (param < .5) { return(new SignedDistance(minDistance, Math.Abs(Vector2.Dot(ab.Normalize(), qa.Normalize())))); } return(new SignedDistance(minDistance, Math.Abs(Vector2.Dot((_p[2] - _p[1]).Normalize(), (_p[2] - origin).Normalize())))); }
public static void RenderSdf(Bitmap <float> output, Bitmap <FloatRgb> sdf, double pxRange) { int w = output.Width, h = output.Height; pxRange *= (double)(w + h) / (sdf.Width + sdf.Height); for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { var s = Sample(sdf, new Vector2((x + .5) / w, (y + .5) / h)); output[x, y] = DistVal(Arithmetic.Median(s.R, s.G, s.B), pxRange); } } }
private static float Sample(Bitmap <float> bitmap, Vector2 pos) { int w = bitmap.Width, h = bitmap.Height; var x = pos.X * w - .5; var y = pos.Y * h - .5; var l = (int)Math.Floor(x); var b = (int)Math.Floor(y); var r = l + 1; var t = b + 1; var lr = x - l; var bt = y - b; l = Math.Clamp(l, 0, w - 1); r = Math.Clamp(r, 0, w - 1); b = Math.Clamp(b, 0, h - 1); t = Math.Clamp(t, 0, h - 1); return(Arithmetic.Mix(Arithmetic.Mix(bitmap[l, b], bitmap[r, b], lr), Arithmetic.Mix(bitmap[l, t], bitmap[r, t], lr), bt)); }
public override Vector2 Direction(double param) { var tangent = Arithmetic.Mix(Arithmetic.Mix(_p[1] - _p[0], _p[2] - _p[1], param), Arithmetic.Mix(_p[2] - _p[1], _p[3] - _p[2], param), param); if (!tangent) { if (param == 0) { return(_p[2] - _p[0]); } if (param == 1) { return(_p[3] - _p[1]); } } return(tangent); }
public override SignedDistance SignedDistance(Vector2 origin, ref double param) { var aq = origin - _p[0]; var ab = _p[1] - _p[0]; param = Vector2.Dot(aq, ab) / Vector2.Dot(ab, ab); var eq = _p[param > 0.5 ? 1 : 0] - origin; var endpointDistance = eq.Length(); if (param > 0 && param < 1) { var orthoDistance = Vector2.Dot(ab.GetOrthonormal(), aq); if (Math.Abs(orthoDistance) < endpointDistance) { return(new SignedDistance(orthoDistance, 0)); } } return(new SignedDistance(Arithmetic.NonZeroSign(Vector2.Cross(aq, ab)) * endpointDistance, Math.Abs(Vector2.Dot(ab.Normalize(), eq.Normalize())))); }
public override void SplitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new CubicSegment(Color, _p[0], _p[0] == _p[1] ? _p[0] : Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), 1.0 / 3.0), Point(1.0 / 3.0)); part2 = new CubicSegment(Color, Point(1.0 / 3.0), Arithmetic.Mix( Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), 1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 1.0 / 3.0), 1.0 / 3.0), 2.0 / 3.0), Arithmetic.Mix( Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 2.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), 2.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), 2.0 / 3.0), 1.0 / 3.0), Point(2.0 / 3.0)); part3 = new CubicSegment(Color, Point(2.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), 2.0 / 3.0), _p[2] == _p[3] ? _p[3] : Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), _p[3]); }
public override SignedDistance SignedDistance(Vector2 origin, ref double param) { var qa = _p[0] - origin; var ab = _p[1] - _p[0]; var br = _p[2] - _p[1] - ab; var as_ = _p[3] - _p[2] - (_p[2] - _p[1]) - br; var epDir = Direction(0); var minDistance = Arithmetic.NonZeroSign(Vector2.Cross(epDir, qa)) * qa.Length(); // distance from A param = -Vector2.Dot(qa, epDir) / Vector2.Dot(epDir, epDir); { epDir = Direction(1); var distance = Arithmetic.NonZeroSign(Vector2.Cross(epDir, _p[3] - origin)) * (_p[3] - origin).Length(); // distance from B if (Math.Abs(distance) < Math.Abs(minDistance)) { minDistance = distance; param = Vector2.Dot(origin + epDir - _p[3], epDir) / Vector2.Dot(epDir, epDir); } } // Iterative minimum distance search for (var i = 0; i <= MsdfgenCubicSearchStarts; ++i) { var t = (double)i / MsdfgenCubicSearchStarts; for (var step = 0;; ++step) { var qpt = Point(t) - origin; var distance = Arithmetic.NonZeroSign(Vector2.Cross(Direction(t), qpt)) * qpt.Length(); if (Math.Abs(distance) < Math.Abs(minDistance)) { minDistance = distance; param = t; } if (step == MsdfgenCubicSearchSteps) { break; } // Improve t var d1 = 3 * as_ * t * t + 6 * br * t + 3 * ab; var d2 = 6 * as_ * t + 6 * br; t -= Vector2.Dot(qpt, d1) / (Vector2.Dot(d1, d1) + Vector2.Dot(qpt, d2)); if (t < 0 || t > 1) { break; } } } if (param >= 0 && param <= 1) { return(new SignedDistance(minDistance, 0)); } if (param < .5) { return(new SignedDistance(minDistance, Math.Abs(Vector2.Dot(Direction(0).Normalize(), qa.Normalize())))); } return(new SignedDistance(minDistance, Math.Abs(Vector2.Dot(Direction(1).Normalize(), (_p[3] - origin).Normalize())))); }
public override void SplitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new QuadraticSegment(Color, _p[0], Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Point(1.0 / 3.0)); part2 = new QuadraticSegment(Color, Point(1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 5.0 / 9.0), Arithmetic.Mix(_p[1], _p[2], 4.0 / 9.0), 0.5), Point(2 / 3.0)); part3 = new QuadraticSegment(Color, Point(2.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), _p[2]); }
public override Vector2 Direction(double param) { return(Arithmetic.Mix(_p[1] - _p[0], _p[2] - _p[1], param)); }
public override Vector2 Point(double param) { return(Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], param), Arithmetic.Mix(_p[1], _p[2], param), param)); }