use core::ptr::NonNull;
use std::ffi::{CStr, CString, c_void};
use crate::brushes::Brush;
use crate::ffi::{
noesis_base_component_release, noesis_ellipse_create, noesis_line_create, noesis_line_get,
noesis_line_set, noesis_rectangle_create, noesis_rectangle_get_radius_x,
noesis_rectangle_get_radius_y, noesis_rectangle_set_radius_x, noesis_rectangle_set_radius_y,
noesis_shape_get_fill, noesis_shape_get_height, noesis_shape_get_stretch,
noesis_shape_get_stroke, noesis_shape_get_stroke_dash_array, noesis_shape_get_stroke_dash_cap,
noesis_shape_get_stroke_dash_offset, noesis_shape_get_stroke_end_line_cap,
noesis_shape_get_stroke_line_join, noesis_shape_get_stroke_miter_limit,
noesis_shape_get_stroke_start_line_cap, noesis_shape_get_stroke_thickness,
noesis_shape_get_trim_end, noesis_shape_get_trim_offset, noesis_shape_get_trim_start,
noesis_shape_get_width, noesis_shape_set_fill, noesis_shape_set_height,
noesis_shape_set_stretch, noesis_shape_set_stroke, noesis_shape_set_stroke_dash_array,
noesis_shape_set_stroke_dash_cap, noesis_shape_set_stroke_dash_offset,
noesis_shape_set_stroke_end_line_cap, noesis_shape_set_stroke_line_join,
noesis_shape_set_stroke_miter_limit, noesis_shape_set_stroke_start_line_cap,
noesis_shape_set_stroke_thickness, noesis_shape_set_trim_end, noesis_shape_set_trim_offset,
noesis_shape_set_trim_start, noesis_shape_set_width,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
#[non_exhaustive]
pub enum PenLineCap {
Flat = 0,
Square = 1,
Round = 2,
Triangle = 3,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
#[non_exhaustive]
pub enum PenLineJoin {
Miter = 0,
Bevel = 1,
Round = 2,
}
pub use crate::brushes::Stretch;
impl PenLineCap {
fn from_ordinal(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Flat),
1 => Some(Self::Square),
2 => Some(Self::Round),
3 => Some(Self::Triangle),
_ => None,
}
}
}
impl PenLineJoin {
fn from_ordinal(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Miter),
1 => Some(Self::Bevel),
2 => Some(Self::Round),
_ => None,
}
}
}
pub trait Shape {
fn shape_raw(&self) -> *mut c_void;
fn set_width(&mut self, width: f32) {
unsafe { noesis_shape_set_width(self.shape_raw(), width) };
}
#[must_use]
fn width(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_width(self.shape_raw(), &mut out) };
out
}
fn set_height(&mut self, height: f32) {
unsafe { noesis_shape_set_height(self.shape_raw(), height) };
}
#[must_use]
fn height(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_height(self.shape_raw(), &mut out) };
out
}
fn set_fill<B: Brush>(&mut self, brush: &B) {
unsafe { noesis_shape_set_fill(self.shape_raw(), brush.brush_raw()) };
}
fn clear_fill(&mut self) {
unsafe { noesis_shape_set_fill(self.shape_raw(), core::ptr::null_mut()) };
}
#[must_use]
fn fill_raw(&self) -> *mut c_void {
unsafe { noesis_shape_get_fill(self.shape_raw()) }
}
fn set_stroke<B: Brush>(&mut self, brush: &B) {
unsafe { noesis_shape_set_stroke(self.shape_raw(), brush.brush_raw()) };
}
fn clear_stroke(&mut self) {
unsafe { noesis_shape_set_stroke(self.shape_raw(), core::ptr::null_mut()) };
}
#[must_use]
fn stroke_raw(&self) -> *mut c_void {
unsafe { noesis_shape_get_stroke(self.shape_raw()) }
}
fn set_stroke_thickness(&mut self, value: f32) {
unsafe { noesis_shape_set_stroke_thickness(self.shape_raw(), value) };
}
#[must_use]
fn stroke_thickness(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_stroke_thickness(self.shape_raw(), &mut out) };
out
}
fn set_stroke_miter_limit(&mut self, value: f32) {
unsafe { noesis_shape_set_stroke_miter_limit(self.shape_raw(), value) };
}
#[must_use]
fn stroke_miter_limit(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_stroke_miter_limit(self.shape_raw(), &mut out) };
out
}
fn set_stroke_dash_offset(&mut self, value: f32) {
unsafe { noesis_shape_set_stroke_dash_offset(self.shape_raw(), value) };
}
#[must_use]
fn stroke_dash_offset(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_stroke_dash_offset(self.shape_raw(), &mut out) };
out
}
fn set_trim_start(&mut self, value: f32) {
unsafe { noesis_shape_set_trim_start(self.shape_raw(), value) };
}
#[must_use]
fn trim_start(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_trim_start(self.shape_raw(), &mut out) };
out
}
fn set_trim_end(&mut self, value: f32) {
unsafe { noesis_shape_set_trim_end(self.shape_raw(), value) };
}
#[must_use]
fn trim_end(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_trim_end(self.shape_raw(), &mut out) };
out
}
fn set_trim_offset(&mut self, value: f32) {
unsafe { noesis_shape_set_trim_offset(self.shape_raw(), value) };
}
#[must_use]
fn trim_offset(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_shape_get_trim_offset(self.shape_raw(), &mut out) };
out
}
fn set_stroke_dash_cap(&mut self, cap: PenLineCap) {
unsafe { noesis_shape_set_stroke_dash_cap(self.shape_raw(), cap as i32) };
}
#[must_use]
fn stroke_dash_cap(&self) -> Option<PenLineCap> {
PenLineCap::from_ordinal(unsafe { noesis_shape_get_stroke_dash_cap(self.shape_raw()) })
}
fn set_stroke_start_line_cap(&mut self, cap: PenLineCap) {
unsafe { noesis_shape_set_stroke_start_line_cap(self.shape_raw(), cap as i32) };
}
#[must_use]
fn stroke_start_line_cap(&self) -> Option<PenLineCap> {
PenLineCap::from_ordinal(unsafe {
noesis_shape_get_stroke_start_line_cap(self.shape_raw())
})
}
fn set_stroke_end_line_cap(&mut self, cap: PenLineCap) {
unsafe { noesis_shape_set_stroke_end_line_cap(self.shape_raw(), cap as i32) };
}
#[must_use]
fn stroke_end_line_cap(&self) -> Option<PenLineCap> {
PenLineCap::from_ordinal(unsafe { noesis_shape_get_stroke_end_line_cap(self.shape_raw()) })
}
fn set_stroke_line_join(&mut self, join: PenLineJoin) {
unsafe { noesis_shape_set_stroke_line_join(self.shape_raw(), join as i32) };
}
#[must_use]
fn stroke_line_join(&self) -> Option<PenLineJoin> {
PenLineJoin::from_ordinal(unsafe { noesis_shape_get_stroke_line_join(self.shape_raw()) })
}
fn set_stretch(&mut self, stretch: Stretch) {
unsafe { noesis_shape_set_stretch(self.shape_raw(), stretch as i32) };
}
#[must_use]
fn stretch(&self) -> Option<Stretch> {
Stretch::from_ordinal(unsafe { noesis_shape_get_stretch(self.shape_raw()) })
}
fn set_stroke_dash_array(&mut self, dashes: &str) {
let c = CString::new(dashes).expect("dash array contained NUL");
unsafe { noesis_shape_set_stroke_dash_array(self.shape_raw(), c.as_ptr()) };
}
#[must_use]
fn stroke_dash_array(&self) -> String {
let p = unsafe { noesis_shape_get_stroke_dash_array(self.shape_raw()) };
if p.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned()
}
}
}
macro_rules! shape_handle {
($name:ident, $create:ident, $doc:literal) => {
#[doc = $doc]
pub struct $name {
ptr: NonNull<c_void>,
}
unsafe impl Send for $name {}
impl $name {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { $create() };
Self {
ptr: NonNull::new(ptr).expect(concat!(stringify!($create), " returned null")),
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
}
impl Default for $name {
fn default() -> Self {
Self::new()
}
}
impl Shape for $name {
fn shape_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()) }
}
}
};
}
shape_handle!(
Rectangle,
noesis_rectangle_create,
"A `Rectangle` shape. Adds rounded-corner radii on top of the shared\n\
[`Shape`] surface; its size comes from the inherited\n\
[`Shape::set_width`]/[`Shape::set_height`]."
);
shape_handle!(
Ellipse,
noesis_ellipse_create,
"An `Ellipse` shape. Carries only the shared [`Shape`] surface; its size\n\
comes from the inherited [`Shape::set_width`]/[`Shape::set_height`]."
);
shape_handle!(
Line,
noesis_line_create,
"A `Line` shape defined by its two endpoints `(X1, Y1)`-`(X2, Y2)` in\n\
addition to the shared [`Shape`] surface."
);
impl Rectangle {
pub fn set_radius_x(&mut self, value: f32) {
unsafe { noesis_rectangle_set_radius_x(self.ptr.as_ptr(), value) };
}
#[must_use]
pub fn radius_x(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_rectangle_get_radius_x(self.ptr.as_ptr(), &mut out) };
out
}
pub fn set_radius_y(&mut self, value: f32) {
unsafe { noesis_rectangle_set_radius_y(self.ptr.as_ptr(), value) };
}
#[must_use]
pub fn radius_y(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_rectangle_get_radius_y(self.ptr.as_ptr(), &mut out) };
out
}
}
impl Line {
pub fn set_points(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
unsafe { noesis_line_set(self.ptr.as_ptr(), x1, y1, x2, y2) };
}
#[must_use]
pub fn points(&self) -> [f32; 4] {
let mut out = [0.0f32; 4];
unsafe { noesis_line_get(self.ptr.as_ptr(), out.as_mut_ptr()) };
out
}
}