use std::f64::consts::PI;
use core_graphics::{
geometry::CGRect,
path::{CGMutablePath, CGPath},
};
use os_ver::if_greater_than;
use super::device::CoreGraphicsDeviceContext;
use crate::{
error::GraphicsError,
geometry::FRect,
path::{FillType, PathBackend},
Float,
};
#[derive(Clone, Debug)]
pub struct CoreGraphicsPath {
path: CGMutablePath,
fill_type: FillType,
}
impl PathBackend for CoreGraphicsPath {
type DeviceContextType = CoreGraphicsDeviceContext;
fn new(_context: Option<&Self::DeviceContextType>) -> Result<Self, GraphicsError> {
Ok(Self {
path: CGMutablePath::new(),
fill_type: FillType::Winding,
})
}
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) {
self.path = CGMutablePath::new();
}
fn close(&mut self) {
self.path.close_subpath();
}
fn move_to(&mut self, x: Float, y: Float) {
if x.is_nan() || y.is_nan() {
return;
}
self.path.move_to_point(None, x, y);
}
fn line_to(&mut self, x: Float, y: Float) {
if x.is_nan() || y.is_nan() {
return;
}
self.path.add_line_to_point(None, x, y);
}
fn arc_to(&mut self, x1: Float, y1: Float, x2: Float, y2: Float, radius: Float) {
if x1.is_nan() || y1.is_nan() || x2.is_nan() || y2.is_nan() || radius.is_nan() {
return;
}
self.path.add_arc_to_point(None, x1, y1, x2, y2, radius);
}
fn bezier_curve_to(&mut self, cpx1: Float, cpy1: Float, cpx2: Float, cpy2: Float, x: Float, y: Float) {
if cpx1.is_nan() || cpy1.is_nan() || cpx2.is_nan() || cpy2.is_nan() || x.is_nan() || y.is_nan() {
return;
}
self.path.add_curve_to_point(None, cpx1, cpy1, cpx2, cpy2, x, y);
}
fn quad_curve_to(&mut self, cpx: Float, cpy: Float, x: Float, y: Float) {
if cpx.is_nan() || cpy.is_nan() || x.is_nan() || y.is_nan() {
return;
}
self.path.add_quad_curve_to_point(None, cpx, cpy, x, y);
}
fn add_arc(&mut self, x: Float, y: Float, radius: Float, start_angle: Float, end_angle: Float, clockwise: bool) {
if x.is_nan() || y.is_nan() || radius.is_nan() || start_angle.is_nan() || end_angle.is_nan() {
return;
}
self.path.add_arc(None, x, y, radius, start_angle, end_angle, clockwise);
}
fn add_rect(&mut self, x: Float, y: Float, width: Float, height: Float) {
if x.is_nan() || y.is_nan() || width.is_nan() || height.is_nan() {
return;
}
self.path.add_rect(None, CGRect::new(x, y, width, height));
}
fn add_circle(&mut self, x: Float, y: Float, radius: Float) {
if x.is_nan() || y.is_nan() || radius.is_nan() {
return;
}
self.path.add_ellipse_in_rect(None, CGRect::new(x - radius, y - radius, radius * 2.0, radius * 2.0));
}
fn add_ellipse(&mut self, x: Float, y: Float, width: Float, height: Float) {
if x.is_nan() || y.is_nan() || width.is_nan() || height.is_nan() {
return;
}
self.path.add_ellipse_in_rect(None, CGRect::new(x, y, width, height));
}
fn add_rounded_rect(&mut self, x: Float, y: Float, width: Float, height: Float, radius: Float) {
if x.is_nan() || y.is_nan() || width.is_nan() || height.is_nan() || radius.is_nan() {
return;
}
#[cfg(target_os = "macos")]
if_greater_than! {(10, 9) => {
self.path.add_rounded_rect(None, CGRect::new(x, y, width, height), radius, radius);
} else {
self.path.move_to_point(None, x + radius, y);
self.path.add_arc_to_point(None, x + width, y, x + width, y + radius, radius);
self.path.add_arc_to_point(None, x + width, y + height, x + width - radius, y + height, radius);
self.path.add_arc_to_point(None, x, y + height, x, y + height - radius, radius);
self.path.add_arc_to_point(None, x, y, x + radius, y, radius);
}};
#[cfg(target_os = "ios")]
if_greater_than! {(7) => {
self.path.add_rounded_rect(None, CGRect::new(x, y, width, height), radius, radius);
} else {
self.path.move_to_point(None, x + radius, y);
self.path.add_arc_to_point(None, x + width, y, x + width, y + radius, radius);
self.path.add_arc_to_point(None, x + width, y + height, x + width - radius, y + height, radius);
self.path.add_arc_to_point(None, x, y + height, x, y + height - radius, radius);
self.path.add_arc_to_point(None, x, y, x + radius, y, radius);
}};
}
fn bounds(&self) -> FRect {
let bounds = self.path.to_immutable().path_bounding_box();
FRect::new(bounds.origin.x.floor(), bounds.origin.y.floor(), bounds.size.width.ceil(), bounds.size.height.ceil())
}
fn is_empty(&self) -> bool {
self.path.to_immutable().is_empty()
}
}
impl CoreGraphicsPath {
pub(super) fn path(&self) -> CGPath {
self.path.to_immutable()
}
}