use super::ColumnSampledContour;
use super::distance_field::*;
use super::daub_brush_distance_field::*;
use super::marching_squares::*;
use super::sampled_contour::*;
use crate::bezier::*;
use crate::bezier::path::*;
use crate::geo::*;
pub trait DaubBrush {
type DaubDistanceField;
fn create_daub(&self, centered_at: impl Coordinate + Coordinate2D, radius: f64) -> Option<(Self::DaubDistanceField, ContourPosition)>;
}
pub fn brush_stroke_daubs_from_curve<'a, TBrush, TCurve>(distance_field: &'a TBrush, curve: &'a TCurve, step: f64, max_error: f64) -> (impl 'a + Iterator<Item=(TBrush::DaubDistanceField, ContourPosition)>, Coord2)
where
TCurve: BezierCurve,
TCurve::Point: Coordinate + Coordinate3D,
TBrush: 'a + DaubBrush,
{
let start_point = curve.start_point();
let (cp1, cp2) = curve.control_points();
let end_point = curve.end_point();
let curve2d = Curve::from_points(Coord2(start_point.x(), start_point.y()), (Coord2(cp1.x(), cp1.y()), Coord2(cp2.x(), cp2.y())), Coord2(end_point.x(), end_point.y()));
let radius = Curve::from_points(start_point.z(), (cp1.z(), cp2.z()), end_point.z());
let bounds = curve2d.bounding_box::<Bounds<_>>();
let radius_bounds = radius.bounding_box::<Bounds<_>>();
let radius_max = radius_bounds.max().max(0.0);
let radius_max = radius_max.ceil() + 1.0;
let offset = bounds.min();
let offset = Coord2(offset.x() - radius_max - 1.0, offset.y() - radius_max - 1.0);
let curve2d = Curve::from_points(Coord2(start_point.x(), start_point.y())-offset, (Coord2(cp1.x(), cp1.y())-offset, Coord2(cp2.x(), cp2.y())-offset), Coord2(end_point.x(), end_point.y())-offset);
let iterator = walk_curve_evenly_map(curve2d, step, max_error,
move |curve_section| {
let t_mid = curve_section.t_for_t(0.5);
let pos = curve2d.point_at_pos(t_mid);
let radius = radius.point_at_pos(t_mid);
(pos, radius)
}).flat_map(move |(pos, radius)| {
distance_field.create_daub(pos, radius)
});
(iterator, offset)
}
pub fn brush_stroke_daubs_from_path<'a, TBrush, TPath>(distance_field: &'a TBrush, path: &'a TPath, step: f64, max_error: f64) -> (impl 'a + Iterator<Item=(TBrush::DaubDistanceField, ContourPosition)>, Coord2)
where
TPath: BezierPath,
TPath::Point: Coordinate + Coordinate3D,
TBrush: 'a + DaubBrush,
{
let mut curves = vec![];
for path_curve in path.to_curves::<Curve<_>>() {
let (start_point, (cp1, cp2), end_point) = path_curve.all_points();
let curve2d = Curve::from_points(Coord2(start_point.x(), start_point.y()), (Coord2(cp1.x(), cp1.y()), Coord2(cp2.x(), cp2.y())), Coord2(end_point.x(), end_point.y()));
let radius = Curve::from_points(start_point.z(), (cp1.z(), cp2.z()), end_point.z());
curves.push((curve2d, radius));
}
let (bounds, radius_bounds) = if curves.len() > 0 {
let mut bounds = curves[0].0.bounding_box::<Bounds<_>>();
let mut radius_bounds = curves[0].1.bounding_box::<Bounds<_>>();
for (curve, radius) in curves.iter().skip(1) {
bounds = bounds.union_bounds(curve.bounding_box());
radius_bounds = radius_bounds.union_bounds(radius.bounding_box());
}
(bounds, radius_bounds)
} else {
(Bounds::empty(), Bounds::empty())
};
let radius_max = radius_bounds.max().max(0.0);
let radius_max = radius_max.ceil() + 1.0;
let offset = bounds.min();
let offset = Coord2(offset.x() - radius_max - 1.0, offset.y() - radius_max - 1.0);
let iterator = curves.into_iter()
.enumerate()
.flat_map(move |(idx, (curve2d, radius))| {
let (start_point, (cp1, cp2), end_point) = curve2d.all_points();
let curve2d_offset = Curve::from_points(start_point-offset, (cp1-offset, cp2-offset), end_point-offset);
let skip = if idx == 0 { 0 } else { 1 };
walk_curve_evenly_map(curve2d_offset, step, max_error, move |curve_section| {
let (t_min, t_max) = curve_section.original_curve_t_values();
let t_mid = (t_min+t_max)/2.0;
let pos = curve2d_offset.point_at_pos(t_mid);
let radius = radius.point_at_pos(t_mid);
(pos, radius)
}).skip(skip)
.flat_map(move |(pos, radius)| {
distance_field.create_daub(pos, radius)
})
});
(iterator, offset)
}
pub fn brush_stroke_from_curve<TPath, TBrushCurve, TBrush>(distance_field: &TBrush, curve: &TBrushCurve, step: f64, max_error: f64) -> Vec<TPath>
where
TPath: BezierPathFactory,
TPath::Point: Coordinate + Coordinate2D,
TBrushCurve: BezierCurve,
TBrushCurve::Point: Coordinate + Coordinate3D,
TBrush: DaubBrush,
TBrush::DaubDistanceField: SampledSignedDistanceField,
{
let (daubs, offset) = brush_stroke_daubs_from_curve(distance_field, curve, step, max_error);
let distance_field = DaubBrushDistanceField::from_daubs(daubs);
let mut paths = trace_paths_from_distance_field::<TPath>(&distance_field, max_error);
let offset = TPath::Point::from_components(&[offset.x(), offset.y()]);
paths.iter_mut().for_each(|path| *path = path.with_offset(offset));
paths
}
pub fn brush_stroke_from_path<TPath, TBrushPath, TBrush>(distance_field: &TBrush, path: &TBrushPath, step: f64, max_error: f64) -> Vec<TPath>
where
TPath: BezierPathFactory,
TPath::Point: Coordinate + Coordinate2D,
TBrushPath: BezierPath,
TBrushPath::Point: Coordinate + Coordinate3D,
TBrush: DaubBrush,
TBrush::DaubDistanceField: SampledSignedDistanceField,
{
let (daubs, offset) = brush_stroke_daubs_from_path(distance_field, path, step, max_error);
let distance_field = DaubBrushDistanceField::from_daubs(daubs);
let mut paths = trace_paths_from_distance_field::<TPath>(&distance_field, max_error);
let offset = TPath::Point::from_components(&[offset.x(), offset.y()]);
paths.iter_mut().for_each(|path| *path = path.with_offset(offset));
paths
}
pub fn brush_stroke_from_path_intercepts<TPath, TBrushPath, TBrush>(distance_field: &TBrush, path: &TBrushPath, step: f64, max_error: f64) -> Vec<TPath>
where
TPath: BezierPathFactory,
TPath::Point: Coordinate + Coordinate2D,
TBrushPath: BezierPath,
TBrushPath::Point: Coordinate + Coordinate3D,
TBrush: DaubBrush,
TBrush::DaubDistanceField: SampledSignedDistanceField,
<TBrush::DaubDistanceField as SampledSignedDistanceField>::Contour: ColumnSampledContour,
{
let (daubs, offset) = brush_stroke_daubs_from_path(distance_field, path, step, max_error);
let distance_field = DaubBrushDistanceField::from_daubs(daubs);
let mut paths = trace_paths_from_intercepts::<TPath>(&distance_field, max_error);
let offset = TPath::Point::from_components(&[offset.x(), offset.y()]);
paths.iter_mut().for_each(|path| *path = path.with_offset(offset));
paths
}