use web_sys::{CanvasWindingRule, Path2d};
use super::device::WebDeviceContext;
use crate::{
error::GraphicsError,
geometry::FRect,
path::{FillType, PathBackend},
Float,
};
#[derive(Clone, Debug)]
pub struct WebPath {
path: Path2d,
fill_type: FillType,
points: Vec<(Float, Float)>,
}
impl PathBackend for WebPath {
type DeviceContextType = WebDeviceContext;
fn new(_context: Option<&Self::DeviceContextType>) -> Result<Self, GraphicsError> {
Path2d::new()
.map(|path| Self {
path,
fill_type: FillType::Winding,
points: Vec::new(),
})
.map_err(|err| GraphicsError::CreationFailed(err.as_string().unwrap_or(stringify!(Path2d).to_string())))
}
fn get_fill_type(&self) -> FillType {
self.fill_type
}
fn set_fill_type(&mut self, fill_type: FillType) {
self.fill_type = fill_type;
}
fn begin(&mut self) {
let path = Path2d::new();
if let Ok(path) = path {
self.path = path;
self.points.clear();
}
}
fn close(&mut self) {
self.path.close_path();
}
fn move_to(&mut self, x: Float, y: Float) {
self.path.move_to(x, y);
self.points.push((x, y));
}
fn line_to(&mut self, x: Float, y: Float) {
self.path.line_to(x, y);
self.points.push((x, y));
}
fn arc_to(&mut self, x1: Float, y1: Float, x2: Float, y2: Float, radius: Float) {
self.path.arc_to(x1, y1, x2, y2, radius).ok();
self.points.push((x1, y1));
self.points.push((x2, y2));
}
fn bezier_curve_to(&mut self, cpx1: Float, cpy1: Float, cpx2: Float, cpy2: Float, x: Float, y: Float) {
self.path.bezier_curve_to(cpx1, cpy1, cpx2, cpy2, x, y);
self.points.push((cpx1, cpy1));
self.points.push((cpx2, cpy2));
self.points.push((x, y));
}
fn quad_curve_to(&mut self, cpx: Float, cpy: Float, x: Float, y: Float) {
self.path.quadratic_curve_to(cpx, cpy, x, y);
self.points.push((cpx, cpy));
self.points.push((x, y));
}
fn add_arc(&mut self, x: Float, y: Float, radius: Float, start_angle: Float, end_angle: Float, clockwise: bool) {
self.path.arc_with_anticlockwise(x, y, radius, start_angle, end_angle, clockwise).ok();
self.points.push((x - radius, y - radius));
self.points.push((x + radius, y + radius));
}
fn add_rect(&mut self, x: Float, y: Float, width: Float, height: Float) {
self.path.rect(x, y, width, height);
self.points.push((x, y));
self.points.push((x + width, y + height));
}
fn add_circle(&mut self, x: Float, y: Float, radius: Float) {
self.path.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).ok();
self.points.push((x - radius, y - radius));
self.points.push((x + radius, y + radius));
}
fn add_ellipse(&mut self, x: Float, y: Float, width: Float, height: Float) {
let radius_x = width / 2.0;
let radius_y = height / 2.0;
let (x, y) = (x + radius_x, y + radius_y);
self.path.ellipse(x, y, radius_x, radius_y, 0.0, 0.0, 2.0 * std::f64::consts::PI).ok();
self.points.push((x - radius_x, y - radius_y));
self.points.push((x + radius_x, y + radius_y));
}
fn add_rounded_rect(&mut self, x: Float, y: Float, width: Float, height: Float, radius: Float) {
self.path.round_rect_with_f64(x, y, width, height, radius).ok();
self.points.push((x, y));
self.points.push((x + width, y + height));
}
fn bounds(&self) -> FRect {
let mut min_x = Float::INFINITY;
let mut min_y = Float::INFINITY;
let mut max_x = Float::NEG_INFINITY;
let mut max_y = Float::NEG_INFINITY;
for (x, y) in &self.points {
min_x = min_x.min(*x);
min_y = min_y.min(*y);
max_x = max_x.max(*x);
max_y = max_y.max(*y);
}
FRect::new(min_x, min_y, max_x - min_x, max_y - min_y)
}
fn is_empty(&self) -> bool {
false
}
}
impl WebPath {
pub(super) fn path(&self) -> &Path2d {
&self.path
}
}
impl From<FillType> for CanvasWindingRule {
fn from(fill_type: FillType) -> Self {
match fill_type {
FillType::Winding => CanvasWindingRule::Nonzero,
FillType::EvenOdd => CanvasWindingRule::Evenodd,
}
}
}