use std::cell::RefCell;
use dom_struct::dom_struct;
use js::context::JSContext;
use js::rust::HandleObject;
use script_bindings::codegen::GenericBindings::DOMMatrixBinding::DOMMatrix2DInit;
use script_bindings::error::ErrorResult;
use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use script_bindings::str::DOMString;
use servo_canvas_traits::canvas::Path;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::Path2DMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::root::DomRoot;
use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
use crate::dom::globalscope::GlobalScope;
#[dom_struct]
pub(crate) struct Path2D {
reflector_: Reflector,
#[ignore_malloc_size_of = "Defined in kurbo."]
#[no_trace]
path: RefCell<Path>,
}
impl Path2D {
pub(crate) fn new() -> Path2D {
Self {
reflector_: Reflector::new(),
path: RefCell::new(Path::new()),
}
}
pub(crate) fn new_with_path(other: &Path2D) -> Path2D {
Self {
reflector_: Reflector::new(),
path: other.path.clone(),
}
}
pub(crate) fn new_with_str(path: &str) -> Path2D {
Self {
reflector_: Reflector::new(),
path: RefCell::new(Path::from_svg(path)),
}
}
pub(crate) fn segments(&self) -> Path {
self.path.borrow().clone()
}
pub(crate) fn is_path_empty(&self) -> bool {
self.path.borrow().0.is_empty()
}
fn add_path(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
if other.is_path_empty() {
return Ok(());
}
let matrix = dommatrix2dinit_to_matrix(transform)?;
if !matrix.m11.is_finite() ||
!matrix.m12.is_finite() ||
!matrix.m21.is_finite() ||
!matrix.m22.is_finite() ||
!matrix.m31.is_finite() ||
!matrix.m32.is_finite()
{
return Ok(());
}
let mut c = other.segments();
c.transform(matrix);
let mut path = self.path.borrow_mut();
let last_point = path.last_point();
path.0.extend(c.0);
if let Some(last_point) = last_point {
path.move_to(last_point.x, last_point.y);
}
Ok(())
}
}
impl Path2DMethods<crate::DomTypeHolder> for Path2D {
fn AddPath(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
self.add_path(other, transform)
}
fn ClosePath(&self) {
self.path.borrow_mut().close_path();
}
fn MoveTo(&self, x: f64, y: f64) {
self.path.borrow_mut().move_to(x, y);
}
fn LineTo(&self, x: f64, y: f64) {
self.path.borrow_mut().line_to(x, y);
}
fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
self.path.borrow_mut().quadratic_curve_to(cpx, cpy, x, y);
}
fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
self.path
.borrow_mut()
.bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
}
fn ArcTo(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) -> Fallible<()> {
self.path
.borrow_mut()
.arc_to(x1, y1, x2, y2, radius)
.map_err(|_| Error::IndexSize(None))
}
fn Rect(&self, x: f64, y: f64, w: f64, h: f64) {
self.path.borrow_mut().rect(x, y, w, h);
}
fn Arc(
&self,
x: f64,
y: f64,
radius: f64,
start_angle: f64,
end_angle: f64,
counterclockwise: bool,
) -> Fallible<()> {
self.path
.borrow_mut()
.arc(x, y, radius, start_angle, end_angle, counterclockwise)
.map_err(|_| Error::IndexSize(None))
}
fn Ellipse(
&self,
x: f64,
y: f64,
radius_x: f64,
radius_y: f64,
rotation_angle: f64,
start_angle: f64,
end_angle: f64,
counterclockwise: bool,
) -> Fallible<()> {
self.path
.borrow_mut()
.ellipse(
x,
y,
radius_x,
radius_y,
rotation_angle,
start_angle,
end_angle,
counterclockwise,
)
.map_err(|_| Error::IndexSize(None))
}
fn Constructor(
cx: &mut JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
) -> DomRoot<Path2D> {
reflect_dom_object_with_proto(cx, Box::new(Self::new()), global, proto)
}
fn Constructor_(
cx: &mut JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
other: &Path2D,
) -> DomRoot<Path2D> {
reflect_dom_object_with_proto(cx, Box::new(Self::new_with_path(other)), global, proto)
}
fn Constructor__(
cx: &mut JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
path_string: DOMString,
) -> DomRoot<Path2D> {
reflect_dom_object_with_proto(
cx,
Box::new(Self::new_with_str(&path_string.str())),
global,
proto,
)
}
}