#[doc(hidden)]
#[macro_export]
macro_rules! draw_env {
{} => {
use sketchbook::derive_sketch;
use macro_rules_attribute::apply;
use sketchbook::aspects::draw::SketchExt;
mod single_aspect {
use sketchbook::aspects::draw;
use sketchbook::aspects::draw::*;
use sketchbook::Size2;
sketchbook::env_for_aspect!(draw);
sketchbook::compose! {
pub enum Events {
#[part]
Aspect(draw::Event<EnvSpecificMarker>),
}
}
sketchbook::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: draw::EnvData<EnvSpecificMarker>,
}
}
impl draw::EnvSpecific for EnvSpecificMarker {
type Num = f32;
type Color = (u8, u8, u8);
type ShapeBuffer = Vec<Shape<f32, (u8, u8, u8)>>;
fn default_size() -> Size2<Self::Num> { Size2 { width: 0.0, height: 0.0 } }
fn default_context() -> Context<Self::Num, Self::Color> { Context::default() }
fn default_background() -> Self::Color { (0, 0, 0) }
fn default_frame_rate() -> Self::Num { 1.0 }
fn no_color() -> Self::Color { (0, 0, 0) }
}
}
}
}
mod circle;
mod context;
mod mode;
mod rectangle;
mod shape;
#[cfg(feature = "color")]
mod color;
pub use circle::*;
pub use context::*;
pub use mode::*;
pub use rectangle::*;
pub use shape::*;
#[cfg(feature = "color")]
pub use color::*;
use crate::{geometry::*, Real};
use crate::real::ToReal;
use core::marker::PhantomData;
use crate::{compose::AsPart, Environment, PageOf, Sketch};
pub trait Handlers
where
Self: Sketch,
Self::Env: AssociatedEnvSpecificMarker,
{
#[inline]
fn draw(&mut self) {
}
}
pub trait SketchExt<M>
where
Self: AsPart<EnvData<M>>,
M: EnvSpecific + 'static,
{
#[inline]
fn width(&self) -> NumFor<M> {
self.as_part().size.width
}
#[inline]
fn height(&self) -> NumFor<M> {
self.as_part().size.height
}
#[inline]
fn size(&self) -> Size2<NumFor<M>> {
self.as_part().size
}
#[inline]
fn rect_mode(&mut self, mode: Mode) {
self.as_part_mut().context.rectangle_mode = mode;
}
#[inline]
fn ellipse_mode(&mut self, mode: Mode) {
self.as_part_mut().context.ellipse_mode = mode;
}
#[inline]
fn fill<C: Into<ColorFor<M>>>(&mut self, color: C) {
self.as_part_mut().context.fill = color.into();
}
#[inline]
fn no_fill(&mut self) {
self.as_part_mut().context.fill = M::no_color();
}
#[inline]
fn stroke<C: Into<ColorFor<M>>>(&mut self, color: C) {
self.as_part_mut().context.stroke = color.into();
}
#[inline]
fn no_stroke(&mut self) {
self.as_part_mut().context.stroke = M::no_color();
self.as_part_mut().context.weight = M::Num::zero();
}
#[inline]
fn weight<R: ToReal<NumFor<M>>>(&mut self, width: R) {
self.as_part_mut().context.weight = width.to_real();
}
fn background<C: Into<ColorFor<M>>>(&mut self, color: C) {
let color = color.into();
let part = self.as_part_mut();
part.shapes.shape_buffer_clear();
part.shapes
.shape_buffer_push(Shape::Background(color.clone()));
part.background = color;
}
fn circle<S: IntoWithContext<Circle<NumFor<M>, ColorFor<M>>, NumFor<M>, ColorFor<M>>>(
&mut self,
circle: S,
) {
let circle = circle.into_with_context(&self.as_part().context);
self.as_part_mut().shapes.shape_buffer_push(Shape::Circle(circle));
}
fn rect<S: IntoWithContext<Rectangle<NumFor<M>, ColorFor<M>>, NumFor<M>, ColorFor<M>>>(
&mut self,
rectangle: S,
) {
let rect = rectangle.into_with_context(&self.as_part().context);
self.as_part_mut().shapes.shape_buffer_push(Shape::Rectangle(rect));
}
#[inline]
fn frame_rate<R: ToReal<NumFor<M>>>(&mut self, rate: R) {
self.as_part_mut().frame_rate = rate.to_real()
}
#[inline]
fn context(&self) -> &Context<NumFor<M>, ColorFor<M>> {
&self.as_part().context
}
}
impl<M, T> SketchExt<M> for T
where
Self: AsPart<EnvData<M>>,
M: EnvSpecific,
{
}
pub trait RunHandlers
where
Self: Sketch + Handlers,
Self::Env: AssociatedEnvSpecificMarker + Environment,
PageOf<Self::Env>: AsPart<EnvData<SpecificallyFor<Self::Env>>>,
{
fn run_handlers(&mut self, event: &Event<SpecificallyFor<Self::Env>>) {
match event {
Event::Draw => {
self.draw();
}
Event::_MetaPhantom(_, _) => unreachable!(),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Event<M>
where
M: EnvSpecific,
{
#[doc(hidden)]
#[cfg_attr(feature = "serde", serde(skip))]
_MetaPhantom(core::convert::Infallible, PhantomData<M>),
Draw,
}
pub trait AssociatedEnvSpecificMarker {
type EnvSpecificMarker: EnvSpecific;
}
pub trait EnvSpecific: 'static {
type Num: Real;
type Color: Clone;
type ShapeBuffer: ShapeBuffer<Self::Num, Self::Color>;
fn default_size() -> Size2<Self::Num>;
fn default_context() -> Context<Self::Num, Self::Color>;
fn default_background() -> Self::Color;
fn default_frame_rate() -> Self::Num;
fn no_color() -> Self::Color;
}
pub trait ShapeBuffer<N, C> {
fn shape_buffer_push(&mut self, shape: Shape<N, C>);
fn shape_buffer_clear(&mut self);
}
#[cfg(feature = "alloc")]
#[allow(unused_qualifications)]
impl<N, C> ShapeBuffer<N, C> for alloc::vec::Vec<Shape<N, C>> {
#[inline]
fn shape_buffer_push(&mut self, shape: Shape<N, C>) {
self.push(shape)
}
#[inline]
fn shape_buffer_clear(&mut self) {
self.clear()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EnvData<M>
where
M: EnvSpecific,
{
marker: PhantomData<M>,
frame_rate: M::Num,
shapes: M::ShapeBuffer,
background: M::Color,
size: Size2<M::Num>,
context: Context<M::Num, M::Color>,
}
impl<M> EnvData<M>
where
M: EnvSpecific,
{
#[inline]
pub fn set_size(&mut self, size: Size2<M::Num>) {
self.size = size;
}
#[inline]
pub fn frame_rate(&self) -> M::Num {
self.frame_rate
}
#[inline]
pub fn background_color(&self) -> &M::Color {
&self.background
}
#[inline]
pub fn shape_buffer_mut(&mut self) -> &mut M::ShapeBuffer {
&mut self.shapes
}
}
impl<M> Default for EnvData<M>
where
M: EnvSpecific,
M::ShapeBuffer: Default,
{
#[inline]
fn default() -> Self {
Self {
marker: PhantomData,
size: M::default_size(),
context: M::default_context(),
shapes: M::ShapeBuffer::default(),
background: M::default_background(),
frame_rate: M::default_frame_rate(),
}
}
}
pub type NumFor<T> = <T as EnvSpecific>::Num;
pub type ColorFor<T> = <T as EnvSpecific>::Color;
pub type SpecificallyFor<T> = <T as AssociatedEnvSpecificMarker>::EnvSpecificMarker;
#[test]
fn test_draw() {
use crate::aspects::draw;
use crate::env::*;
use crate::*;
mod single_aspect {
use crate::aspects::draw;
use crate::aspects::draw::*;
crate::env_for_aspect!(draw);
crate::compose! {
pub enum Events {
#[part]
Aspect(Event<EnvSpecificMarker>),
}
}
crate::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: EnvData<EnvSpecificMarker>,
pub time: i16,
}
}
impl EnvSpecific for EnvSpecificMarker {
type Num = f32;
type Color = u8;
type ShapeBuffer = Vec<Shape<f32, u8>>;
fn default_size() -> Size2<Self::Num> {
Size2 {
width: 0.0,
height: 0.0,
}
}
fn default_context() -> Context<Self::Num, Self::Color> {
Context {
fill: 0,
ellipse_mode: Mode::Center,
rectangle_mode: Mode::Center,
rotation: 0.0,
stroke: 0,
weight: 0.0,
}
}
fn default_background() -> Self::Color {
0
}
fn default_frame_rate() -> Self::Num {
0.0
}
fn no_color() -> Self::Color {
0
}
}
}
derive_sketch! {
#[sketch(
env = single_aspect,
aspects = (draw),
)]
struct App {
#[page] page: single_aspect::Page,
drew: bool,
}
}
impl Setup for App {
fn setup(page: single_aspect::Page, _: ()) -> Self {
App { page, drew: false }
}
}
impl Handlers for App {
fn draw(&mut self) {
self.drew = true;
}
}
let mut mill = single_aspect::Mill;
let mut app = App::setup(mill.new_page(), ());
assert!(!app.drew);
app.handle_environment_event(&single_aspect::Events::Aspect(draw::Event::Draw));
assert!(app.drew);
}