Spaces:
Runtime error
Runtime error
DEVICE | |
int compute_winding_number(const Circle &circle, const Vector2f &pt) { | |
const auto &c = circle.center; | |
auto r = circle.radius; | |
// inside the circle: return 1, outside the circle: return 0 | |
if (distance_squared(c, pt) < r * r) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
DEVICE | |
int compute_winding_number(const Ellipse &ellipse, const Vector2f &pt) { | |
const auto &c = ellipse.center; | |
const auto &r = ellipse.radius; | |
// inside the ellipse: return 1, outside the ellipse: return 0 | |
if (square(c.x - pt.x) / square(r.x) + square(c.y - pt.y) / square(r.y) < 1) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
DEVICE | |
bool intersect(const AABB &box, const Vector2f &pt) { | |
if (pt.y < box.p_min.y || pt.y > box.p_max.y) { | |
return false; | |
} | |
if (pt.x > box.p_max.x) { | |
return false; | |
} | |
return true; | |
} | |
DEVICE | |
int compute_winding_number(const Path &path, const BVHNode *bvh_nodes, const Vector2f &pt) { | |
// Shoot a horizontal ray from pt to right, intersect with all curves of the path, | |
// count intersection | |
auto num_segments = path.num_base_points; | |
constexpr auto max_bvh_size = 128; | |
int bvh_stack[max_bvh_size]; | |
auto stack_size = 0; | |
auto winding_number = 0; | |
bvh_stack[stack_size++] = 2 * num_segments - 2; | |
while (stack_size > 0) { | |
const BVHNode &node = bvh_nodes[bvh_stack[--stack_size]]; | |
if (node.child1 < 0) { | |
// leaf | |
auto base_point_id = node.child0; | |
auto point_id = - node.child1 - 1; | |
assert(base_point_id < num_segments); | |
assert(point_id < path.num_points); | |
if (path.num_control_points[base_point_id] == 0) { | |
// Straight line | |
auto i0 = point_id; | |
auto i1 = (point_id + 1) % path.num_points; | |
auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]}; | |
auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]}; | |
// intersect p0 + t * (p1 - p0) with pt + t' * (1, 0) | |
// solve: | |
// pt.x + t' = v0.x + t * (v1.x - v0.x) | |
// pt.y = v0.y + t * (v1.y - v0.y) | |
if (p1.y != p0.y) { | |
auto t = (pt.y - p0.y) / (p1.y - p0.y); | |
if (t >= 0 && t <= 1) { | |
auto tp = p0.x - pt.x + t * (p1.x - p0.x); | |
if (tp >= 0) { | |
if (p1.y - p0.y > 0) { | |
winding_number += 1; | |
} else { | |
winding_number -= 1; | |
} | |
} | |
} | |
} | |
} else if (path.num_control_points[base_point_id] == 1) { | |
// Quadratic Bezier curve | |
auto i0 = point_id; | |
auto i1 = point_id + 1; | |
auto i2 = (point_id + 2) % path.num_points; | |
auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]}; | |
auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]}; | |
auto p2 = Vector2f{path.points[2 * i2], path.points[2 * i2 + 1]}; | |
// The curve is (1-t)^2p0 + 2(1-t)tp1 + t^2p2 | |
// = (p0-2p1+p2)t^2+(-2p0+2p1)t+p0 | |
// intersect with pt + t' * (1 0) | |
// solve | |
// pt.y = (p0-2p1+p2)t^2+(-2p0+2p1)t+p0 | |
float t[2]; | |
if (solve_quadratic(p0.y-2*p1.y+p2.y, | |
-2*p0.y+2*p1.y, | |
p0.y-pt.y, | |
&t[0], &t[1])) { | |
for (int j = 0; j < 2; j++) { | |
if (t[j] >= 0 && t[j] <= 1) { | |
auto tp = (p0.x-2*p1.x+p2.x)*t[j]*t[j] + | |
(-2*p0.x+2*p1.x)*t[j] + | |
p0.x-pt.x; | |
if (tp >= 0) { | |
if (2*(p0.y-2*p1.y+p2.y)*t[j]+(-2*p0.y+2*p1.y) > 0) { | |
winding_number += 1; | |
} else { | |
winding_number -= 1; | |
} | |
} | |
} | |
} | |
} | |
} else if (path.num_control_points[base_point_id] == 2) { | |
// Cubic Bezier curve | |
auto i0 = point_id; | |
auto i1 = point_id + 1; | |
auto i2 = point_id + 2; | |
auto i3 = (point_id + 3) % path.num_points; | |
auto p0 = Vector2f{path.points[2 * i0], path.points[2 * i0 + 1]}; | |
auto p1 = Vector2f{path.points[2 * i1], path.points[2 * i1 + 1]}; | |
auto p2 = Vector2f{path.points[2 * i2], path.points[2 * i2 + 1]}; | |
auto p3 = Vector2f{path.points[2 * i3], path.points[2 * i3 + 1]}; | |
// The curve is (1 - t)^3 p0 + 3 * (1 - t)^2 t p1 + 3 * (1 - t) t^2 p2 + t^3 p3 | |
// = (-p0+3p1-3p2+p3) t^3 + (3p0-6p1+3p2) t^2 + (-3p0+3p1) t + p0 | |
// intersect with pt + t' * (1 0) | |
// solve: | |
// pt.y = (-p0+3p1-3p2+p3) t^3 + (3p0-6p1+3p2) t^2 + (-3p0+3p1) t + p0 | |
double t[3]; | |
int num_sol = solve_cubic(double(-p0.y+3*p1.y-3*p2.y+p3.y), | |
double(3*p0.y-6*p1.y+3*p2.y), | |
double(-3*p0.y+3*p1.y), | |
double(p0.y-pt.y), | |
t); | |
for (int j = 0; j < num_sol; j++) { | |
if (t[j] >= 0 && t[j] <= 1) { | |
// t' = (-p0+3p1-3p2+p3) t^3 + (3p0-6p1+3p2) t^2 + (-3p0+3p1) t + p0 - pt.x | |
auto tp = (-p0.x+3*p1.x-3*p2.x+p3.x)*t[j]*t[j]*t[j]+ | |
(3*p0.x-6*p1.x+3*p2.x)*t[j]*t[j]+ | |
(-3*p0.x+3*p1.x)*t[j]+ | |
p0.x-pt.x; | |
if (tp > 0) { | |
if (3*(-p0.y+3*p1.y-3*p2.y+p3.y)*t[j]*t[j]+ | |
2*(3*p0.y-6*p1.y+3*p2.y)*t[j]+ | |
(-3*p0.y+3*p1.y) > 0) { | |
winding_number += 1; | |
} else { | |
winding_number -= 1; | |
} | |
} | |
} | |
} | |
} else { | |
assert(false); | |
} | |
} else { | |
assert(node.child0 >= 0 && node.child1 >= 0); | |
const AABB &b0 = bvh_nodes[node.child0].box; | |
if (intersect(b0, pt)) { | |
bvh_stack[stack_size++] = node.child0; | |
} | |
const AABB &b1 = bvh_nodes[node.child1].box; | |
if (intersect(b1, pt)) { | |
bvh_stack[stack_size++] = node.child1; | |
} | |
assert(stack_size <= max_bvh_size); | |
} | |
} | |
return winding_number; | |
} | |
DEVICE | |
int compute_winding_number(const Rect &rect, const Vector2f &pt) { | |
const auto &p_min = rect.p_min; | |
const auto &p_max = rect.p_max; | |
// inside the rectangle: return 1, outside the rectangle: return 0 | |
if (pt.x > p_min.x && pt.x < p_max.x && pt.y > p_min.y && pt.y < p_max.y) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
DEVICE | |
int compute_winding_number(const Shape &shape, const BVHNode *bvh_nodes, const Vector2f &pt) { | |
switch (shape.type) { | |
case ShapeType::Circle: | |
return compute_winding_number(*(const Circle *)shape.ptr, pt); | |
case ShapeType::Ellipse: | |
return compute_winding_number(*(const Ellipse *)shape.ptr, pt); | |
case ShapeType::Path: | |
return compute_winding_number(*(const Path *)shape.ptr, bvh_nodes, pt); | |
case ShapeType::Rect: | |
return compute_winding_number(*(const Rect *)shape.ptr, pt); | |
} | |
assert(false); | |
return 0; | |
} | |