Except for all the things that don't quite work, shading works perfectly.
namespace IPDF
{
+vector<Real> SolveQuadratic(const Real & a, const Real & b, const Real & c, const Real & min, const Real & max)
+{
+ vector<Real> roots; roots.reserve(2);
+ if (a == 0 && b != 0)
+ {
+ roots.push_back(-c/b);
+ return roots;
+ }
+ Real disc(b*b - Real(4)*a*c);
+ if (disc < 0)
+ {
+ return roots;
+ }
+ else if (disc == 0)
+ {
+ Real x(-b/Real(2)*a);
+ if (x >= min && x <= max)
+ roots.push_back(x);
+ return roots;
+ }
+
+ Real x0((-b - Sqrt(b*b - Real(4)*a*c))/(Real(2)*a));
+ Real x1((-b + Sqrt(b*b - Real(4)*a*c))/(Real(2)*a));
+ if (x0 > x1)
+ {
+ Real tmp(x0);
+ x0 = x1;
+ x1 = tmp;
+ }
+ if (x0 >= min && x0 <= max)
+ roots.push_back(x0);
+ if (x1 >= min && x1 <= max)
+ roots.push_back(x1);
+ return roots;
+}
+
+/**
+ * Finds the root (if it exists) in a monotonicly in(de)creasing segment of a Cubic
+ */
+
+static void CubicSolveSegment(vector<Real> & roots, const Real & a, const Real & b, const Real & c, const Real & d, Real & tl, Real & tu, const Real & delta)
+{
+ Real l = a*tl*tl*tl + b*tl*tl + c*tl + d;
+ Real u = a*tu*tu*tu + b*tu*tu + c*tu + d;
+ if ((l < 0 && u < 0) || (l > 0 && u > 0))
+ return;
+
+ bool negative = (u < l); // lower point > 0, upper point < 0
+ while (tu - tl > delta)
+ {
+ Real t(tu+tl);
+ t /= 2;
+ Real m = a*t*t*t + b*t*t + c*t + d;
+ if (m > 0)
+ {
+ if (negative)
+ tl = t;
+ else
+ tu = t;
+ }
+ else if (negative)
+ {
+ tu = t;
+ }
+ else
+ {
+ tl = t;
+ }
+ //Debug("Delta is %f (%f - %f -> %f)", tu-tl, tu, tl, t);
+ }
+ roots.push_back(tl);
+}
+vector<Real> SolveCubic(const Real & a, const Real & b, const Real & c, const Real & d, const Real & min, const Real & max, const Real & delta)
+{
+ vector<Real> roots; roots.reserve(3);
+ Real tu(max);
+ Real tl(min);
+ vector<Real> turns(SolveQuadratic(a*3, b*2, c));
+ //Debug("%u turning points", turns.size());
+ for (unsigned i = 1; i < turns.size(); ++i)
+ {
+ tu = turns[i];
+ CubicSolveSegment(roots, a, b, c, d, tl, tu,delta);
+ tl = turns[i];
+ }
+ tu = max;
+ CubicSolveSegment(roots, a, b, c, d, tl, tu,delta);
+ return roots;
+ /*
+ Real maxi(100);
+ Real prevRes(d);
+ for(int i = 0; i <= 100; ++i)
+ {
+ Real x(i);
+ x /= maxi;
+ Real y = a*(x*x*x) + b*(x*x) + c*x + d;
+ if (((y < Real(0)) && (prevRes > Real(0))) || ((y > Real(0)) && (prevRes < Real(0))))
+ {
+ //Debug("Found root of %fx^3 + %fx^2 + %fx + %f at %f (%f)", a, b, c, d, x, y);
+ roots.push_back(x);
+ }
+ prevRes = y;
+ }
+ return roots;
+ */
+}
+
/**
* Factorial
* Use dynamic programming / recursion
//Debug("No real roots");
return pair<Real, Real>(0,1);
}
- pair<Real, Real> tsols = SolveQuadratic(a, b, c);
- if (tsols.first > 1) tsols.first = 1;
- if (tsols.first < 0) tsols.first = 0;
- if (tsols.second > 1) tsols.second = 1;
- if (tsols.second < 0) tsols.second = 0;
- return tsols;
+ vector<Real> tsols = SolveQuadratic(a, b, c);
+ if (tsols.size() == 1)
+ return pair<Real,Real>(tsols[0], tsols[0]);
+ else if (tsols.size() == 0)
+ return pair<Real, Real>(0,1);
+
+ return pair<Real,Real>(tsols[0], tsols[1]);
+
}
inline bool CompRealByPtr(const Real * a, const Real * b)
return result;
}
+vector<Real> Bezier::SolveXParam(const Real & x) const
+{
+ Real d(x0 - x);
+ Real c((x1 - x0)*Real(3));
+ Real b((x2 - x1)*Real(3) - c);
+ Real a(x3 -x0 - c - b);
+ vector<Real> results(SolveCubic(a, b, c, d));
+ for (unsigned i = 0; i < results.size(); ++i)
+ {
+ Vec2 p;
+ Evaluate(p.x, p.y, results[i]);
+ }
+ return results;
+}
+
+
+vector<Real> Bezier::SolveYParam(const Real & y) const
+{
+ Real d(y0 - y);
+ Real c((y1 - y0)*Real(3));
+ Real b((y2 - y1)*Real(3) - c);
+ Real a(y3 -y0 - c - b);
+ vector<Real> results(SolveCubic(a, b, c, d));
+ for (unsigned i = 0; i < results.size(); ++i)
+ {
+ Vec2 p;
+ Evaluate(p.x, p.y, results[i]);
+ }
+ return results;
+}
+
+vector<Vec2> Bezier::Evaluate(const vector<Real> & u) const
+{
+ vector<Vec2> result(u.size());
+ for (unsigned i = 0; i < u.size(); ++i)
+ {
+ Evaluate(result[i].x, result[i].y, u[i]);
+ }
+ return result;
+}
+
/**
* Get Bounds Rectangle of Bezier
*/
extern Real Bernstein(int k, int n, const Real & u);
extern std::pair<Real,Real> BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3);
- inline std::pair<Real,Real> SolveQuadratic(const Real & a, const Real & b, const Real & c)
- {
- Real x0((-b + Sqrt(b*b - Real(4)*a*c))/(Real(2)*a));
- Real x1((-b - Sqrt(b*b - Real(4)*a*c))/(Real(2)*a));
- return std::pair<Real,Real>(x0,x1);
- }
-
- inline std::vector<Real> SolveCubic(const Real & a, const Real & b, const Real & c, const Real & d)
- {
- // This is going to be a big one...
- // See http://en.wikipedia.org/wiki/Cubic_function#General_formula_for_roots
-
- std::vector<Real> roots;
- // delta = 18abcd - 4 b^3 d + b^2 c^2 - 4ac^3 - 27 a^2 d^2
-
-#if 0
- Real discriminant = Real(18) * a * b * c * d - Real(4) * (b * b * b) * d
- + (b * b) * (c * c) - Real(4) * a * (c * c * c)
- - Real(27) * (a * a) * (d * d);
-
- Debug("Trying to solve %fx^3 + %fx^2 + %fx + %f (Discriminant: %f)", a,b,c,d, discriminant);
- // discriminant > 0 => 3 distinct, real roots.
- // discriminant = 0 => a multiple root (1 or 2 real roots)
- // discriminant < 0 => 1 real root, 2 complex conjugate roots
-
- Real delta0 = (b*b) - Real(3) * a * c;
- Real delta1 = Real(2) * (b * b * b) - Real(9) * a * b * c + Real(27) * (a * a) * d;
+ extern std::vector<Real> SolveQuadratic(const Real & a, const Real & b, const Real & c, const Real & min = 0, const Real & max = 1);
-
- Real C = pow((delta1 + Sqrt((delta1 * delta1) - 4 * (delta0 * delta0 * delta0)) ) / Real(2), 1/3);
-
- if (false && discriminant < 0)
- {
- Real real_root = (Real(-1) / (Real(3) * a)) * (b + C + delta0 / C);
-
- roots.push_back(real_root);
-
- return roots;
-
- }
-#endif
- ////HACK: We know any roots we care about will be between 0 and 1, so...
- Real maxi(100);
- Real prevRes(d);
- for(int i = 0; i <= 100; ++i)
- {
- Real x(i);
- x /= maxi;
- Real y = a*(x*x*x) + b*(x*x) + c*x + d;
- if (((y < Real(0)) && (prevRes > Real(0))) || ((y > Real(0)) && (prevRes < Real(0))))
- {
- Debug("Found root of %fx^3 + %fx^2 + %fx + %f at %f (%f)", a, b, c, d, x, y);
- roots.push_back(x);
- }
- prevRes = y;
- }
- return roots;
-
- }
+ extern std::vector<Real> SolveCubic(const Real & a, const Real & b, const Real & c, const Real & d, const Real & min = 0, const Real & max = 1, const Real & delta = 1e-4);
/** A _cubic_ bezier. **/
struct Bezier
x = x0*coeff[0] + x1*coeff[1] + x2*coeff[2] + x3*coeff[3];
y = y0*coeff[0] + y1*coeff[1] + y2*coeff[2] + y3*coeff[3];
}
+ std::vector<Vec2> Evaluate(const std::vector<Real> & u) const;
+
+ std::vector<Real> SolveXParam(const Real & x) const;
+ std::vector<Real> SolveYParam(const Real & x) const;
+
+ // Get points with same X
+ inline std::vector<Vec2> SolveX(const Real & x) const
+ {
+ return Evaluate(SolveXParam(x));
+ }
+ // Get points with same Y
+ inline std::vector<Vec2> SolveY(const Real & y) const
+ {
+ return Evaluate(SolveYParam(y));
+ }
bool operator==(const Bezier & equ) const
{
Bezier data = bezier.ToRelative(bounds); // Relative
if (data.ToAbsolute(bounds) != bezier)
{
- Error("%s != %s", data.ToAbsolute(Rect(0,0,1,1)).Str().c_str(),
+ Warn("%s != %s", data.ToAbsolute(Rect(0,0,1,1)).Str().c_str(),
bezier.Str().c_str());
- Fatal("ToAbsolute on ToRelative does not give original Bezier");
+ Warn("ToAbsolute on ToRelative does not give original Bezier");
}
unsigned index = AddBezierData(data);
return Add(BEZIER, bounds, index);
clock_gettime(CLOCK_MONOTONIC_RAW, &real_clock_start);
real_clock_now = real_clock_start;
double frames = 0;
- double data_rate = 1; // period between data output to stdout (if <= 0 there will be no output)
+ double data_rate = 0; // period between data output to stdout (if <= 0 there will be no output)
uint64_t data_points = 0;
setbuf(stdout, NULL);
while (scr.PumpEvents())
#include "objectrenderer.h"
#include "view.h"
#include <vector>
+#include <queue>
using namespace std;
return result;
}
-pair<int64_t, int64_t> ObjectRenderer::CPUPointLocation(const pair<Real, Real> & point, const View & view, const CPURenderTarget & target)
+ObjectRenderer::PixelPoint ObjectRenderer::CPUPointLocation(const Vec2 & point, const View & view, const CPURenderTarget & target)
{
// hack...
- Rect result = view.TransformToViewCoords(Rect(point.first, point.second,1,1));
+ Rect result = view.TransformToViewCoords(Rect(point.x, point.y,1,1));
int64_t x = result.x*target.w;
int64_t y = result.y*target.h;
- return pair<int64_t, int64_t>(x,y);
+ return PixelPoint(x,y);
}
for (int64_t j = 1; j <= blen; ++j)
{
control.Evaluate(x[j % 2],y[j % 2], invblen*j);
- ObjectRenderer::RenderLineOnCPU((int64_t)Double(x[0]),(int64_t)Double(y[0]), (int64_t)Double(x[1]),(int64_t)Double(y[1]), target);
+ ObjectRenderer::RenderLineOnCPU((int64_t)Double(x[0]),(int64_t)Double(y[0]), (int64_t)Double(x[1]),(int64_t)Double(y[1]), target, Colour(0,0,0,!view.PerformingShading()));
}
/*
Rect bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
PixelBounds pix_bounds(bounds);
- pix_bounds.x-=1;
- pix_bounds.w+=2;
- pix_bounds.y-=1;
- pix_bounds.h+=2;
const Path & path = objects.paths[objects.data_indices[m_indexes[i]]];
if (path.m_fill.a == 0 || !view.PerformingShading())
continue;
-
-
- pair<int64_t,int64_t> top(CPUPointLocation(path.m_top, view, target));
- pair<int64_t,int64_t> bottom(CPUPointLocation(path.m_bottom, view, target));
- pair<int64_t,int64_t> left(CPUPointLocation(path.m_left, view, target));
- pair<int64_t,int64_t> right(CPUPointLocation(path.m_right, view, target));
- FloodFillOnCPU(top.first, top.second+1, pix_bounds, target, path.m_fill);
- FloodFillOnCPU(bottom.first, bottom.second-1, pix_bounds, target, path.m_fill);
- FloodFillOnCPU(left.first+1, left.second, pix_bounds, target, path.m_fill);
- FloodFillOnCPU(right.first-1, right.second, pix_bounds, target, path.m_fill);
- if (view.ShowingObjectBounds())
+ for (unsigned f = 0; f < path.m_fill_points.size(); ++f)
{
- Colour c(0,0,1,1);
- RenderLineOnCPU(top.first, top.second, bottom.first, bottom.second, target, c);
- RenderLineOnCPU(left.first, left.second, right.first, right.second, target, c);
- ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target, c);
- ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, c);
- ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target, c);
- ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, c);
+ PixelPoint fill_point(CPUPointLocation(path.m_fill_points[f], view, target));
+ FloodFillOnCPU(fill_point.first, fill_point.second, pix_bounds, target, path.m_fill);
}
+ /*if (true)//(view.ShowingObjectBounds())
+ {
+
+ PixelPoint start(CPUPointLocation((path.m_top+path.m_left+path.m_right+path.m_bottom)/4, view, target));
+ for (unsigned f = 0; f < path.m_fill_points.size(); ++f)
+ {
+ PixelPoint end(CPUPointLocation(path.m_fill_points[f], view, target));
+ RenderLineOnCPU(start.first, start.second, end.first, end.second, target, Colour(0,0,1,1));
+ }
+ }
+ */
}
}
void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill)
{
- if (x < 0 || x < bounds.x || x > bounds.x+bounds.w || x >= target.w)
- return;
- if (y < 0 || y < bounds.y || y > bounds.y+bounds.h || y >= target.h)
- return;
-
- if (GetColour(target, x, y) != Colour(1,1,1,1))
+ if (fill == Colour(1,1,1,1))
return;
+ queue<PixelPoint > traverse;
+ traverse.push(PixelPoint(x,y));
+ // now with 100% less stack overflows!
+ while (traverse.size() > 0)
+ {
+ PixelPoint cur(traverse.front());
+ traverse.pop();
+ if (cur.first < 0 || cur.first < bounds.x || cur.first >= bounds.x+bounds.w || cur.first >= target.w ||
+ cur.second < 0 || cur.second < bounds.y || cur.second >= bounds.y+bounds.h || cur.second >= target.h)
+ continue;
+ if (GetColour(target, cur.first, cur.second) != Colour(1,1,1,1))
+ continue;
+ SetColour(target, cur.first, cur.second, fill);
- SetColour(target, x, y, fill);
- FloodFillOnCPU(x-1, y, bounds, target, fill);
- FloodFillOnCPU(x+1, y, bounds, target, fill);
- FloodFillOnCPU(x,y-1,bounds,target,fill);
- FloodFillOnCPU(x,y+1,bounds,target,fill);
-
+
+ traverse.push(PixelPoint(cur.first+1, cur.second));
+ traverse.push(PixelPoint(cur.first-1, cur.second));
+ traverse.push(PixelPoint(cur.first, cur.second-1));
+ traverse.push(PixelPoint(cur.first, cur.second+1));
+ }
}
}
int64_t x; int64_t y; int64_t w; int64_t h;
PixelBounds(const Rect & bounds) : x(Double(bounds.x)), y(Double(bounds.y)), w(Double(bounds.w)), h(Double(bounds.h)) {}
};
+
+ typedef std::pair<int64_t, int64_t> PixelPoint;
static Rect CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target);
- static std::pair<int64_t, int64_t> CPUPointLocation(const std::pair<Real, Real> & point, const View & view, const CPURenderTarget & target);
+ static PixelPoint CPUPointLocation(const Vec2 & point, const View & view, const CPURenderTarget & target);
static void SaveBMP(const CPURenderTarget & target, const char * filename);
ymax = (objb.y+objb.h);
bottom = i;
}
+
+ // find fill points
+ Vec2 pt;
+ // left
+ pt = Vec2(objb.x, objb.y+objb.h/Real(2));
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // right
+ pt = Vec2(objb.x+objb.w, objb.y+objb.h/Real(2));
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // bottom
+ pt = Vec2(objb.x+objb.w/Real(2), objb.y+objb.h);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // top
+ pt = Vec2(objb.x+objb.w/Real(2), objb.y);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+
+ // topleft
+ pt = Vec2(objb.x, objb.y);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // topright
+ pt = Vec2(objb.x+objb.w, objb.y);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // bottom left
+ pt = Vec2(objb.x, objb.y+objb.h);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ // bottom right
+ pt = Vec2(objb.x+objb.w, objb.y);
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+
+ // mid
+ pt = Vec2(objb.x+objb.w/Real(2), objb.y+objb.h/Real(2));
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+
+
}
// Get actual turning point coords of the 4 edge case beziers
m_bottom = objects.beziers[objects.data_indices[bottom]].ToAbsolute(objects.bounds[bottom]).GetBottom();
m_left = objects.beziers[objects.data_indices[left]].ToAbsolute(objects.bounds[left]).GetLeft();
m_right = objects.beziers[objects.data_indices[right]].ToAbsolute(objects.bounds[right]).GetRight();
- /*Debug("Top: %f, %f", m_top.first, m_top.second);
- Debug("Bottom: %f, %f", m_bottom.first, m_bottom.second);
- Debug("Left: %f, %f", m_left.first, m_left.second);
- Debug("Right: %f, %f", m_right.first, m_right.second);
- Debug("Left - Right: %f, %f", m_right.first - m_left.first, m_right.second - m_left.second);
- Debug("Top - Bottom: %f, %f", m_top.first - m_bottom.first, m_top.second - m_bottom.second);
- */
+
+ Vec2 pt = (m_top + m_bottom)/2;
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ pt = (m_left + m_right)/2;
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+ pt = (m_left + m_right + m_top + m_bottom)/4;
+ if (PointInside(objects, pt))
+ m_fill_points.push_back(pt);
+
+}
+
+
+bool Path::PointInside(const Objects & objects, const Vec2 & pt, bool debug) const
+{
+ vector<Vec2> x_ints;
+ vector<Vec2> y_ints;
+ for (unsigned i = m_start; i <= m_end; ++i)
+ {
+ Bezier bez(objects.beziers[objects.data_indices[i]].ToAbsolute(objects.bounds[i]));
+ vector<Vec2> xi(bez.SolveX(pt.x));
+ vector<Vec2> yi(bez.SolveY(pt.y));
+ x_ints.insert(x_ints.end(), xi.begin(), xi.end());
+ y_ints.insert(y_ints.end(), yi.begin(), yi.end());
+ }
+ //Debug("Solved for intersections");
+ unsigned bigger = 0;
+ unsigned smaller = 0;
+ for (unsigned i = 0; i < x_ints.size(); ++i)
+ {
+ if (debug)
+ Debug("X Intersection %u at %f,%f vs %f,%f", i,x_ints[i].x, x_ints[i].y, pt.x, pt.y);
+ if (x_ints[i].y >= pt.y)
+ {
+
+ ++bigger;
+ }
+ }
+ smaller = x_ints.size() - bigger;
+ if (debug)
+ {
+ Debug("%u horizontal, %u bigger, %u smaller", x_ints.size(), bigger, smaller);
+ }
+ if (smaller % 2 == 0 || bigger % 2 == 0)
+ return false;
+
+ bigger = 0;
+ smaller = 0;
+
+ for (unsigned i = 0; i < y_ints.size(); ++i)
+ {
+ if (debug)
+ Debug("Y Intersection %u at %f,%f vs %f,%f", i,x_ints[i].x, x_ints[i].y, pt.x, pt.y);
+ if (y_ints[i].x >= pt.x)
+ {
+
+ ++bigger;
+ }
+ }
+ smaller = y_ints.size() - bigger;
+ if (debug)
+ {
+ Debug("%u vertical, %u bigger, %u smaller", y_ints.size(), bigger, smaller);
+ }
+ if (smaller % 2 == 0 || bigger % 2 == 0)
+ return false;
+
+
+ return true;
}
Rect Path::SolveBounds(const Objects & objects) const
{
- return Rect(m_left.first, m_top.second, m_right.first-m_left.first, m_bottom.second-m_top.second);
+ return Rect(m_left.x, m_top.y, m_right.x-m_left.x, m_bottom.y-m_top.y);
}
}
Rect SolveBounds(const Objects & objects) const;
-
+ // Is point inside shape?
+ bool PointInside(const Objects & objects, const Vec2 & pt, bool debug=false) const;
unsigned m_start; // First bounding Bezier index
unsigned m_end; // Last (inclusive) '' ''
unsigned m_index; // index into Objects array
- std::pair<Real,Real> m_top;
- std::pair<Real,Real> m_bottom;
- std::pair<Real,Real> m_left;
- std::pair<Real,Real> m_right;
+ Vec2 m_top;
+ Vec2 m_bottom;
+ Vec2 m_left;
+ Vec2 m_right;
+
+ std::vector<Vec2> m_fill_points;
Colour m_fill; // colour to fill with
};
Real y;
Vec2() : x(0), y(0) {}
Vec2(Real _x, Real _y) : x(_x), y(_y) {}
+ Vec2(const std::pair<Real, Real> & p) : x(p.first), y(p.second) {}
bool operator==(const Vec2& other) const { return (x == other.x) && (y == other.y); }
bool operator!=(const Vec2& other) const { return !(*this == other); }
{
m_screen.RenderPixels(0,0,width, height, m_cpu_rendering_pixels); //TODO: Make this work :(
// Debug for great victory (do something similar for GPU and compare?)
- ObjectRenderer::SaveBMP({m_cpu_rendering_pixels, width, height}, "cpu_rendering_last_frame.bmp");
+ //ObjectRenderer::SaveBMP({m_cpu_rendering_pixels, width, height}, "cpu_rendering_last_frame.bmp");
}
m_cached_display.UnBind(); // resets render target to the screen
m_cached_display.Blit(); // blit FrameBuffer to screen