use core::ptr::NonNull;
use std::ffi::{CString, c_void};
use crate::ffi::{
noesis_arc_segment_create, noesis_arc_segment_get, noesis_base_component_release,
noesis_bezier_segment_create, noesis_bezier_segment_get, noesis_combined_geometry_create,
noesis_combined_geometry_get_geometry1, noesis_combined_geometry_get_geometry2,
noesis_combined_geometry_get_mode, noesis_combined_geometry_set_geometry1,
noesis_combined_geometry_set_geometry2, noesis_combined_geometry_set_mode,
noesis_ellipse_geometry_create, noesis_ellipse_geometry_get, noesis_geometry_get_bounds,
noesis_geometry_get_render_bounds, noesis_geometry_get_transform,
noesis_geometry_group_add_child, noesis_geometry_group_child_count,
noesis_geometry_group_create, noesis_geometry_group_get_fill_rule,
noesis_geometry_group_set_fill_rule, noesis_geometry_is_empty, noesis_geometry_set_transform,
noesis_line_geometry_create, noesis_line_geometry_get, noesis_line_segment_create,
noesis_line_segment_get_point, noesis_path_figure_add_segment, noesis_path_figure_create,
noesis_path_figure_get_is_closed, noesis_path_figure_get_is_filled,
noesis_path_figure_get_start_point, noesis_path_figure_segment_count,
noesis_path_figure_set_is_closed, noesis_path_figure_set_is_filled,
noesis_path_figure_set_start_point, noesis_path_geometry_add_figure,
noesis_path_geometry_create, noesis_path_geometry_figure_count,
noesis_path_geometry_get_fill_rule, noesis_path_geometry_set_fill_rule,
noesis_poly_bezier_segment_create, noesis_poly_line_segment_create,
noesis_poly_quadratic_bezier_segment_create, noesis_poly_segment_get_point,
noesis_poly_segment_point_count, noesis_quadratic_bezier_segment_create,
noesis_quadratic_bezier_segment_get, noesis_rectangle_geometry_create,
noesis_rectangle_geometry_get, noesis_stream_geometry_context_arc_to,
noesis_stream_geometry_context_begin_figure, noesis_stream_geometry_context_close,
noesis_stream_geometry_context_cubic_to, noesis_stream_geometry_context_destroy,
noesis_stream_geometry_context_line_to, noesis_stream_geometry_context_quadratic_to,
noesis_stream_geometry_context_set_is_closed, noesis_stream_geometry_create,
noesis_stream_geometry_create_from_data, noesis_stream_geometry_get_fill_rule,
noesis_stream_geometry_open, noesis_stream_geometry_set_data,
noesis_stream_geometry_set_fill_rule,
};
use crate::transforms::Transform;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect {
fn from_array(a: [f32; 4]) -> Self {
Self {
x: a[0],
y: a[1],
width: a[2],
height: a[3],
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FillRule {
EvenOdd,
Nonzero,
}
impl FillRule {
fn to_ordinal(self) -> i32 {
match self {
FillRule::EvenOdd => 0,
FillRule::Nonzero => 1,
}
}
fn from_ordinal(v: i32) -> Self {
match v {
1 => FillRule::Nonzero,
_ => FillRule::EvenOdd,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum GeometryCombineMode {
Union,
Intersect,
Xor,
Exclude,
}
impl GeometryCombineMode {
fn to_ordinal(self) -> i32 {
match self {
GeometryCombineMode::Union => 0,
GeometryCombineMode::Intersect => 1,
GeometryCombineMode::Xor => 2,
GeometryCombineMode::Exclude => 3,
}
}
fn from_ordinal(v: i32) -> Option<Self> {
match v {
0 => Some(GeometryCombineMode::Union),
1 => Some(GeometryCombineMode::Intersect),
2 => Some(GeometryCombineMode::Xor),
3 => Some(GeometryCombineMode::Exclude),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SweepDirection {
Counterclockwise,
Clockwise,
}
impl SweepDirection {
fn to_ordinal(self) -> i32 {
match self {
SweepDirection::Counterclockwise => 0,
SweepDirection::Clockwise => 1,
}
}
fn from_ordinal(v: i32) -> Self {
match v {
1 => SweepDirection::Clockwise,
_ => SweepDirection::Counterclockwise,
}
}
}
pub trait Geometry {
fn geometry_raw(&self) -> *mut c_void;
#[must_use]
fn bounds(&self) -> Rect {
let mut out = [0.0f32; 4];
unsafe { noesis_geometry_get_bounds(self.geometry_raw(), out.as_mut_ptr()) };
Rect::from_array(out)
}
#[must_use]
fn render_bounds(&self) -> Rect {
let mut out = [0.0f32; 4];
unsafe { noesis_geometry_get_render_bounds(self.geometry_raw(), out.as_mut_ptr()) };
Rect::from_array(out)
}
#[must_use]
fn is_empty(&self) -> bool {
unsafe { noesis_geometry_is_empty(self.geometry_raw()) == 1 }
}
fn set_transform(&mut self, transform: &dyn Transform) -> bool {
unsafe { noesis_geometry_set_transform(self.geometry_raw(), transform.transform_raw()) }
}
#[must_use]
fn transform_raw(&self) -> *mut c_void {
unsafe { noesis_geometry_get_transform(self.geometry_raw()) }
}
}
pub trait PathSegment {
fn segment_raw(&self) -> *mut c_void;
}
macro_rules! base_component_handle {
($name:ident) => {
unsafe impl Send for $name {}
impl $name {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
}
impl Drop for $name {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
};
}
macro_rules! geometry_handle {
($name:ident) => {
base_component_handle!($name);
impl Geometry for $name {
fn geometry_raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
}
};
}
macro_rules! segment_handle {
($name:ident) => {
base_component_handle!($name);
impl PathSegment for $name {
fn segment_raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
}
};
}
pub struct StreamGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(StreamGeometry);
impl Default for StreamGeometry {
fn default() -> Self {
Self::new()
}
}
impl StreamGeometry {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_stream_geometry_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_stream_geometry_create returned null"),
}
}
#[must_use]
pub fn from_data(data: &str) -> Self {
let c = CString::new(data).expect("data contains NUL");
let ptr = unsafe { noesis_stream_geometry_create_from_data(c.as_ptr()) };
Self {
ptr: NonNull::new(ptr).expect("noesis_stream_geometry_create_from_data returned null"),
}
}
pub fn set_data(&mut self, data: &str) {
let c = CString::new(data).expect("data contains NUL");
unsafe { noesis_stream_geometry_set_data(self.ptr.as_ptr(), c.as_ptr()) };
}
pub fn set_fill_rule(&mut self, rule: FillRule) {
unsafe { noesis_stream_geometry_set_fill_rule(self.ptr.as_ptr(), rule.to_ordinal()) };
}
#[must_use]
pub fn fill_rule(&self) -> FillRule {
FillRule::from_ordinal(unsafe { noesis_stream_geometry_get_fill_rule(self.ptr.as_ptr()) })
}
#[must_use]
pub fn open(&self) -> StreamGeometryContext {
let ptr = unsafe { noesis_stream_geometry_open(self.ptr.as_ptr()) };
StreamGeometryContext {
ctx: NonNull::new(ptr).expect("noesis_stream_geometry_open returned null"),
}
}
}
pub struct StreamGeometryContext {
ctx: NonNull<c_void>,
}
unsafe impl Send for StreamGeometryContext {}
impl StreamGeometryContext {
pub fn begin_figure(&self, x: f32, y: f32, is_closed: bool) {
unsafe { noesis_stream_geometry_context_begin_figure(self.ctx.as_ptr(), x, y, is_closed) };
}
pub fn line_to(&self, x: f32, y: f32) {
unsafe { noesis_stream_geometry_context_line_to(self.ctx.as_ptr(), x, y) };
}
pub fn cubic_to(&self, p1: (f32, f32), p2: (f32, f32), p3: (f32, f32)) {
unsafe {
noesis_stream_geometry_context_cubic_to(
self.ctx.as_ptr(),
p1.0,
p1.1,
p2.0,
p2.1,
p3.0,
p3.1,
)
};
}
pub fn quadratic_to(&self, p1: (f32, f32), p2: (f32, f32)) {
unsafe {
noesis_stream_geometry_context_quadratic_to(self.ctx.as_ptr(), p1.0, p1.1, p2.0, p2.1)
};
}
pub fn arc_to(
&self,
x: f32,
y: f32,
width: f32,
height: f32,
rotation_deg: f32,
is_large_arc: bool,
sweep: SweepDirection,
) {
unsafe {
noesis_stream_geometry_context_arc_to(
self.ctx.as_ptr(),
x,
y,
width,
height,
rotation_deg,
is_large_arc,
sweep.to_ordinal(),
)
};
}
pub fn set_is_closed(&self, is_closed: bool) {
unsafe { noesis_stream_geometry_context_set_is_closed(self.ctx.as_ptr(), is_closed) };
}
pub fn close(self) {
unsafe { noesis_stream_geometry_context_close(self.ctx.as_ptr()) };
core::mem::forget(self);
}
}
impl Drop for StreamGeometryContext {
fn drop(&mut self) {
unsafe { noesis_stream_geometry_context_destroy(self.ctx.as_ptr()) };
}
}
pub struct PathGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(PathGeometry);
impl Default for PathGeometry {
fn default() -> Self {
Self::new()
}
}
impl PathGeometry {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_path_geometry_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_path_geometry_create returned null"),
}
}
pub fn add_figure(&mut self, figure: &PathFigure) -> i32 {
unsafe { noesis_path_geometry_add_figure(self.ptr.as_ptr(), figure.raw()) }
}
#[must_use]
pub fn figure_count(&self) -> usize {
unsafe { noesis_path_geometry_figure_count(self.ptr.as_ptr()) }.max(0) as usize
}
pub fn set_fill_rule(&mut self, rule: FillRule) {
unsafe { noesis_path_geometry_set_fill_rule(self.ptr.as_ptr(), rule.to_ordinal()) };
}
#[must_use]
pub fn fill_rule(&self) -> FillRule {
FillRule::from_ordinal(unsafe { noesis_path_geometry_get_fill_rule(self.ptr.as_ptr()) })
}
}
pub struct PathFigure {
ptr: NonNull<c_void>,
}
base_component_handle!(PathFigure);
impl Default for PathFigure {
fn default() -> Self {
Self::new()
}
}
impl PathFigure {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_path_figure_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_path_figure_create returned null"),
}
}
pub fn set_start_point(&mut self, x: f32, y: f32) {
unsafe { noesis_path_figure_set_start_point(self.ptr.as_ptr(), x, y) };
}
#[must_use]
pub fn start_point(&self) -> (f32, f32) {
let mut out = [0.0f32; 2];
unsafe { noesis_path_figure_get_start_point(self.ptr.as_ptr(), out.as_mut_ptr()) };
(out[0], out[1])
}
pub fn set_is_closed(&mut self, is_closed: bool) {
unsafe { noesis_path_figure_set_is_closed(self.ptr.as_ptr(), is_closed) };
}
pub fn set_is_filled(&mut self, is_filled: bool) {
unsafe { noesis_path_figure_set_is_filled(self.ptr.as_ptr(), is_filled) };
}
#[must_use]
pub fn is_closed(&self) -> bool {
unsafe { noesis_path_figure_get_is_closed(self.ptr.as_ptr()) == 1 }
}
#[must_use]
pub fn is_filled(&self) -> bool {
unsafe { noesis_path_figure_get_is_filled(self.ptr.as_ptr()) == 1 }
}
pub fn add_segment<S: PathSegment>(&mut self, segment: &S) -> i32 {
unsafe { noesis_path_figure_add_segment(self.ptr.as_ptr(), segment.segment_raw()) }
}
#[must_use]
pub fn segment_count(&self) -> usize {
unsafe { noesis_path_figure_segment_count(self.ptr.as_ptr()) }.max(0) as usize
}
}
pub struct LineSegment {
ptr: NonNull<c_void>,
}
segment_handle!(LineSegment);
impl LineSegment {
#[must_use]
pub fn new(x: f32, y: f32) -> Self {
let ptr = unsafe { noesis_line_segment_create(x, y) };
Self {
ptr: NonNull::new(ptr).expect("noesis_line_segment_create returned null"),
}
}
#[must_use]
pub fn point(&self) -> (f32, f32) {
let mut out = [0.0f32; 2];
unsafe { noesis_line_segment_get_point(self.ptr.as_ptr(), out.as_mut_ptr()) };
(out[0], out[1])
}
}
pub struct BezierSegment {
ptr: NonNull<c_void>,
}
segment_handle!(BezierSegment);
impl BezierSegment {
#[must_use]
pub fn new(p1: (f32, f32), p2: (f32, f32), p3: (f32, f32)) -> Self {
let ptr = unsafe { noesis_bezier_segment_create(p1.0, p1.1, p2.0, p2.1, p3.0, p3.1) };
Self {
ptr: NonNull::new(ptr).expect("noesis_bezier_segment_create returned null"),
}
}
#[must_use]
pub fn points(&self) -> [(f32, f32); 3] {
let mut out = [0.0f32; 6];
unsafe { noesis_bezier_segment_get(self.ptr.as_ptr(), out.as_mut_ptr()) };
[(out[0], out[1]), (out[2], out[3]), (out[4], out[5])]
}
}
pub struct QuadraticBezierSegment {
ptr: NonNull<c_void>,
}
segment_handle!(QuadraticBezierSegment);
impl QuadraticBezierSegment {
#[must_use]
pub fn new(p1: (f32, f32), p2: (f32, f32)) -> Self {
let ptr = unsafe { noesis_quadratic_bezier_segment_create(p1.0, p1.1, p2.0, p2.1) };
Self {
ptr: NonNull::new(ptr).expect("noesis_quadratic_bezier_segment_create returned null"),
}
}
#[must_use]
pub fn points(&self) -> [(f32, f32); 2] {
let mut out = [0.0f32; 4];
unsafe { noesis_quadratic_bezier_segment_get(self.ptr.as_ptr(), out.as_mut_ptr()) };
[(out[0], out[1]), (out[2], out[3])]
}
}
pub struct ArcSegment {
ptr: NonNull<c_void>,
}
segment_handle!(ArcSegment);
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ArcFields {
pub point: (f32, f32),
pub size: (f32, f32),
pub rotation_deg: f32,
pub is_large_arc: bool,
pub sweep: SweepDirection,
}
impl ArcSegment {
#[must_use]
pub fn new(
x: f32,
y: f32,
width: f32,
height: f32,
rotation_deg: f32,
is_large_arc: bool,
sweep: SweepDirection,
) -> Self {
let ptr = unsafe {
noesis_arc_segment_create(
x,
y,
width,
height,
rotation_deg,
is_large_arc,
sweep.to_ordinal(),
)
};
Self {
ptr: NonNull::new(ptr).expect("noesis_arc_segment_create returned null"),
}
}
#[must_use]
pub fn from_fields(fields: ArcFields) -> Self {
Self::new(
fields.point.0,
fields.point.1,
fields.size.0,
fields.size.1,
fields.rotation_deg,
fields.is_large_arc,
fields.sweep,
)
}
#[must_use]
pub fn get(&self) -> ArcFields {
let mut point = [0.0f32; 2];
let mut size = [0.0f32; 2];
let mut rotation_deg = 0.0f32;
let mut is_large_arc = false;
let mut sweep = 0i32;
unsafe {
noesis_arc_segment_get(
self.ptr.as_ptr(),
point.as_mut_ptr(),
size.as_mut_ptr(),
&mut rotation_deg,
&mut is_large_arc,
&mut sweep,
)
};
ArcFields {
point: (point[0], point[1]),
size: (size[0], size[1]),
rotation_deg,
is_large_arc,
sweep: SweepDirection::from_ordinal(sweep),
}
}
}
macro_rules! poly_segment {
($name:ident, $create:ident, $doc:literal) => {
#[doc = $doc]
pub struct $name {
ptr: NonNull<c_void>,
}
segment_handle!($name);
impl $name {
#[must_use]
pub fn new(points: &[(f32, f32)]) -> Self {
let flat: Vec<f32> = points.iter().flat_map(|p| [p.0, p.1]).collect();
let ptr = unsafe { $create(flat.as_ptr(), points.len() as u32) };
Self {
ptr: NonNull::new(ptr).expect(concat!(stringify!($create), " returned null")),
}
}
#[must_use]
pub fn point_count(&self) -> usize {
unsafe { noesis_poly_segment_point_count(self.ptr.as_ptr()) }.max(0) as usize
}
#[must_use]
pub fn point(&self, index: usize) -> Option<(f32, f32)> {
let mut out = [0.0f32; 2];
let ok = unsafe {
noesis_poly_segment_get_point(self.ptr.as_ptr(), index as u32, out.as_mut_ptr())
};
ok.then_some((out[0], out[1]))
}
}
};
}
poly_segment!(
PolyLineSegment,
noesis_poly_line_segment_create,
"A `PolyLineSegment`: a run of straight lines through a point list."
);
poly_segment!(
PolyBezierSegment,
noesis_poly_bezier_segment_create,
"A `PolyBezierSegment`: a run of cubic Béziers (points in groups of three)."
);
poly_segment!(
PolyQuadraticBezierSegment,
noesis_poly_quadratic_bezier_segment_create,
"A `PolyQuadraticBezierSegment`: a run of quadratic Béziers (points in pairs)."
);
pub struct EllipseGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(EllipseGeometry);
impl EllipseGeometry {
#[must_use]
pub fn new(cx: f32, cy: f32, rx: f32, ry: f32) -> Self {
let ptr = unsafe { noesis_ellipse_geometry_create(cx, cy, rx, ry) };
Self {
ptr: NonNull::new(ptr).expect("noesis_ellipse_geometry_create returned null"),
}
}
#[must_use]
pub fn get(&self) -> [f32; 4] {
let mut out = [0.0f32; 4];
unsafe { noesis_ellipse_geometry_get(self.ptr.as_ptr(), out.as_mut_ptr()) };
out
}
}
pub struct RectangleGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(RectangleGeometry);
impl RectangleGeometry {
#[must_use]
pub fn new(x: f32, y: f32, width: f32, height: f32, rx: f32, ry: f32) -> Self {
let ptr = unsafe { noesis_rectangle_geometry_create(x, y, width, height, rx, ry) };
Self {
ptr: NonNull::new(ptr).expect("noesis_rectangle_geometry_create returned null"),
}
}
#[must_use]
pub fn from_rect(rect: Rect, radii: (f32, f32)) -> Self {
Self::new(rect.x, rect.y, rect.width, rect.height, radii.0, radii.1)
}
#[must_use]
pub fn rect(&self) -> [f32; 4] {
let mut out = [0.0f32; 4];
unsafe {
noesis_rectangle_geometry_get(
self.ptr.as_ptr(),
out.as_mut_ptr(),
core::ptr::null_mut(),
)
};
out
}
#[must_use]
pub fn radii(&self) -> (f32, f32) {
let mut out = [0.0f32; 2];
unsafe {
noesis_rectangle_geometry_get(
self.ptr.as_ptr(),
core::ptr::null_mut(),
out.as_mut_ptr(),
)
};
(out[0], out[1])
}
}
pub struct LineGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(LineGeometry);
impl LineGeometry {
#[must_use]
pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
let ptr = unsafe { noesis_line_geometry_create(x1, y1, x2, y2) };
Self {
ptr: NonNull::new(ptr).expect("noesis_line_geometry_create returned null"),
}
}
#[must_use]
pub fn get(&self) -> [f32; 4] {
let mut out = [0.0f32; 4];
unsafe { noesis_line_geometry_get(self.ptr.as_ptr(), out.as_mut_ptr()) };
out
}
}
pub struct CombinedGeometry {
ptr: NonNull<c_void>,
}
geometry_handle!(CombinedGeometry);
impl CombinedGeometry {
#[must_use]
pub fn new<A: Geometry, B: Geometry>(
mode: GeometryCombineMode,
geometry1: &A,
geometry2: &B,
) -> Self {
let ptr = unsafe {
noesis_combined_geometry_create(
mode.to_ordinal(),
geometry1.geometry_raw(),
geometry2.geometry_raw(),
)
};
Self {
ptr: NonNull::new(ptr).expect("noesis_combined_geometry_create returned null"),
}
}
pub fn set_geometry1<A: Geometry>(&mut self, geometry: &A) {
unsafe {
noesis_combined_geometry_set_geometry1(self.ptr.as_ptr(), geometry.geometry_raw())
};
}
pub fn set_geometry2<B: Geometry>(&mut self, geometry: &B) {
unsafe {
noesis_combined_geometry_set_geometry2(self.ptr.as_ptr(), geometry.geometry_raw())
};
}
#[must_use]
pub fn geometry1_raw(&self) -> *mut c_void {
unsafe { noesis_combined_geometry_get_geometry1(self.ptr.as_ptr()) }
}
#[must_use]
pub fn geometry2_raw(&self) -> *mut c_void {
unsafe { noesis_combined_geometry_get_geometry2(self.ptr.as_ptr()) }
}
pub fn set_mode(&mut self, mode: GeometryCombineMode) {
unsafe { noesis_combined_geometry_set_mode(self.ptr.as_ptr(), mode.to_ordinal()) };
}
#[must_use]
pub fn mode(&self) -> Option<GeometryCombineMode> {
GeometryCombineMode::from_ordinal(unsafe {
noesis_combined_geometry_get_mode(self.ptr.as_ptr())
})
}
}
pub struct GeometryGroup {
ptr: NonNull<c_void>,
}
geometry_handle!(GeometryGroup);
impl Default for GeometryGroup {
fn default() -> Self {
Self::new()
}
}
impl GeometryGroup {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_geometry_group_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_geometry_group_create returned null"),
}
}
pub fn add_child<G: Geometry>(&mut self, child: &G) -> i32 {
unsafe { noesis_geometry_group_add_child(self.ptr.as_ptr(), child.geometry_raw()) }
}
#[must_use]
pub fn child_count(&self) -> usize {
unsafe { noesis_geometry_group_child_count(self.ptr.as_ptr()) }.max(0) as usize
}
pub fn set_fill_rule(&mut self, rule: FillRule) {
unsafe { noesis_geometry_group_set_fill_rule(self.ptr.as_ptr(), rule.to_ordinal()) };
}
#[must_use]
pub fn fill_rule(&self) -> FillRule {
FillRule::from_ordinal(unsafe { noesis_geometry_group_get_fill_rule(self.ptr.as_ptr()) })
}
}