// Fill and stroke queries over flattened vector paths.
///|
fn subpath_winding_number(
point : @math.Vec2,
points : Array[@math.Vec2],
) -> Int {
if points.length() < 3 {
0
} else {
let mut winding = 0
for i = 0; i < points.length() - 1; i = i + 1 {
let current = points[i]
let next = points[i + 1]
if current.y <= point.y {
if next.y > point.y && next.sub(current).cross(point.sub(current)) > 0.0 {
winding = winding + 1
}
} else if next.y <= point.y &&
next.sub(current).cross(point.sub(current)) < 0.0 {
winding = winding - 1
}
}
winding
}
}
///|
pub fn path_contains_point(
path : Path,
point : @math.Vec2,
tolerance : Double,
) -> Bool {
let subpaths = flatten_path_internal(path, tolerance, 12)
let mut winding = 0
for i = 0; i < subpaths.length(); i = i + 1 {
let subpath = subpaths[i]
if subpath.closed {
winding = winding + subpath_winding_number(point, subpath.points)
}
}
winding != 0
}
///|
pub fn path_distance_to_point(
path : Path,
point : @math.Vec2,
tolerance : Double,
) -> Double {
let subpaths = flatten_path_internal(path, tolerance, 12)
let mut best_squared = 1000000000000000000.0
for i = 0; i < subpaths.length(); i = i + 1 {
let points = subpaths[i].points
for j = 0; j + 1 < points.length(); j = j + 1 {
let distance_squared = @math.distance_squared_to_segment(
point,
points[j],
points[j + 1],
)
if distance_squared < best_squared {
best_squared = distance_squared
}
}
}
if best_squared == 1000000000000000000.0 {
1000000000.0
} else {
best_squared.sqrt()
}
}
///|
pub fn geometry_contains_point(
geometry : ShapeGeometry,
point : @math.Vec2,
tolerance : Double,
) -> Bool {
path_contains_point(geometry.to_path(), point, tolerance)
}