<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.

//! A simple compute shader example that draws into a window, based on wgpu.

use std::time::Instant;

use rust_gpu_toy_shared::Config;
use wgpu::{Extent3d, ShaderModule};

use winit::dpi::PhysicalSize;
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::Window,
};

use spirv_builder::{CompileResult, SpirvBuilder};

struct Pipelines {
    compute_pipeline: wgpu::ComputePipeline,

    copy_pipeline: wgpu::RenderPipeline,
}

struct Layouts {
    compute_bg_layout: wgpu::BindGroupLayout,
    copy_bg_layout: wgpu::BindGroupLayout,

    compute_pipeline_layout: wgpu::PipelineLayout,
    copy_pipeline_layout: wgpu::PipelineLayout,
}

/// State of the runner, inspired by
/// https://sotrh.github.io/learn-wgpu/
struct State {
    pipelines: Pipelines,
    layouts: Layouts,

    surface: wgpu::Surface,
    device: wgpu::Device,
    queue: wgpu::Queue,

    surface_config: wgpu::SurfaceConfiguration,
    size: winit::dpi::PhysicalSize<u32>,

    sampler: wgpu::Sampler,

    compute_bind_group: wgpu::BindGroup,
    copy_bind_group: wgpu::BindGroup,

    start_time: Instant,
}

impl State {
    async fn new(window: &Window, compilation: CompileResult) -> Self {
        let size = window.inner_size();
        let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
        let surface = unsafe { instance.create_surface(window) };
        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: Default::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            })
            .await
            .expect("error finding adapter");
        let features = wgpu::Features::PUSH_CONSTANTS  /* | wgpu::Features::SPIRV_SHADER_PASSTHROUGH */;
        let limits = wgpu::Limits {
            max_push_constant_size: std::mem::size_of::<Config>() as u32,
            ..Default::default()
        };
        let (device, queue) = adapter
            .request_device(
                &wgpu::DeviceDescriptor {
                    label: None,
                    features,
                    limits,
                },
                None,
            )
            .await
            .expect("error creating device");
        let preferred_format = surface
            .get_preferred_format(&adapter)
            .expect("Surface is compatible with adapter, as adapter creation succeeded");
        let surface_config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: preferred_format,
            width: size.width,
            height: size.height,
            present_mode: wgpu::PresentMode::Mailbox,
        };

        surface.configure(&device, &surface_config);

        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
            address_mode_u: wgpu::AddressMode::ClampToEdge,
            address_mode_v: wgpu::AddressMode::ClampToEdge,
            address_mode_w: wgpu::AddressMode::ClampToEdge,
            mag_filter: wgpu::FilterMode::Nearest,
            min_filter: wgpu::FilterMode::Nearest,
            mipmap_filter: wgpu::FilterMode::Nearest,
            ..Default::default()
        });
        let layouts = Self::create_layouts(&device);
        let pipelines = Self::create_pipelines(&device, &surface_config, &layouts, &compilation);
        let (compute_group, copy_group) = Self::bind_for_size(&device, &sampler, size, &layouts);
        let start_time = Instant::now();
        State {
            pipelines,
            layouts,
            surface,
            device,
            queue,
            surface_config,
            size,
            sampler,
            compute_bind_group: compute_group,
            copy_bind_group: copy_group,
            start_time,
        }
    }

    fn resize(&mut self, new_size: PhysicalSize<u32>) {
        if new_size.width != 0 && new_size.height != 0 {
            self.surface_config.width = new_size.width;
            self.surface_config.height = new_size.height;
            self.size = new_size;
            self.surface.configure(&self.device, &self.surface_config);

            let (compute_bind_group, copy_bind_group) =
                State::bind_for_size(&self.device, &self.sampler, new_size, &self.layouts);
            self.compute_bind_group = compute_bind_group;
            self.copy_bind_group = copy_bind_group;
        }
    }

    fn bind_for_size(
        device: &wgpu::Device,
        sampler: &wgpu::Sampler,
        size: PhysicalSize<u32>,
        layouts: &Layouts,
    ) -> (wgpu::BindGroup, wgpu::BindGroup) {
        let img = device.create_texture(&wgpu::TextureDescriptor {
            label: None,
            size: Extent3d {
                width: size.width,
                height: size.height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: wgpu::TextureFormat::Rgba8Unorm,
            usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
        });
        let img_view = img.create_view(&Default::default());
        let compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: None,
            layout: &layouts.compute_bg_layout,
            entries: &[wgpu::BindGroupEntry {
                binding: 0,
                resource: wgpu::BindingResource::TextureView(&img_view),
            }],
        });

        let copy_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: None,
            layout: &layouts.copy_bg_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: wgpu::BindingResource::TextureView(&img_view),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: wgpu::BindingResource::Sampler(&sampler),
                },
            ],
        });
        (compute_bind_group, copy_bind_group)
    }

    fn create_layouts(device: &wgpu::Device) -> Layouts {
        let copy_bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: None,
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        multisampled: false,
                        // Should filterable be false if we want nearest-neighbor?
                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
                        view_dimension: wgpu::TextureViewDimension::D2,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Sampler {
                        filtering: false,
                        comparison: false,
                    },
                    count: None,
                },
            ],
        });
        let copy_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
            label: None,
            bind_group_layouts: &[&copy_bg_layout],
            push_constant_ranges: &[],
        });
        let compute_bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("Compute Bind Group Layout"),
            entries: &[wgpu::BindGroupLayoutEntry {
                binding: 0,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::StorageTexture {
                    access: wgpu::StorageTextureAccess::WriteOnly,
                    format: wgpu::TextureFormat::Rgba8Unorm,
                    view_dimension: wgpu::TextureViewDimension::D2,
                },
                count: None,
            }],
        });
        let compute_pipeline_layout =
            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: None,
                bind_group_layouts: &[&compute_bg_layout],
                push_constant_ranges: &[wgpu::PushConstantRange {
                    stages: wgpu::ShaderStages::COMPUTE,
                    range: 0..std::mem::size_of::<Config>() as u32,
                }],
            });
        Layouts {
            compute_bg_layout,
            copy_bg_layout,
            compute_pipeline_layout,
            copy_pipeline_layout,
        }
    }

    fn replace_pipelines(&mut self, compilation: &CompileResult) {
        let new_pipelines = State::create_pipelines(
            &self.device,
            &self.surface_config,
            &self.layouts,
            compilation,
        );
        self.pipelines = new_pipelines;
    }

    fn create_pipelines(
        device: &wgpu::Device,
        surface_config: &wgpu::SurfaceConfiguration,
        layouts: &Layouts,
        compilation: &CompileResult,
    ) -> Pipelines {
        let module = State::create_shader_module(device, compilation);
        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
            label: None,
            layout: Some(&layouts.compute_pipeline_layout),
            module: &module,
            entry_point: "main",
        });
        let copy_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: None,
            layout: Some(&layouts.copy_pipeline_layout),
            vertex: wgpu::VertexState {
                module: &module,
                entry_point: "vs_main",
                buffers: &[],
            },
            fragment: Some(wgpu::FragmentState {
                module: &module,
                entry_point: "fs_main",
                targets: &[surface_config.format.into()],
            }),
            primitive: wgpu::PrimitiveState::default(),
            depth_stencil: None,
            multisample: wgpu::MultisampleState::default(),
        });
        Pipelines {
            compute_pipeline,
            copy_pipeline,
        }
    }

    fn create_shader_module(device: &wgpu::Device, compilation: &CompileResult) -> ShaderModule {
        let spirv_path = compilation.module.unwrap_single();
        let data = std::fs::read(spirv_path).expect("spirv should exist");
        let source = wgpu::util::make_spirv(&data);
        device.create_shader_module(&wgpu::ShaderModuleDescriptor {
            label: None,
            source,
        })
    }

    fn render_frame(&self) {
        let frame = match self.surface.get_current_texture() {
            Ok(output) => output,
            Err(err) => {
                eprintln!("get_current_texture error: {:?}", err);
                match err {
                    wgpu::SurfaceError::Lost => {
                        self.surface.configure(&self.device, &self.surface_config);
                    }
                    wgpu::SurfaceError::OutOfMemory => {
                        panic!();
                    }
                    _ => (),
                }
                return;
            }
        };
        let frame_view = frame
            .texture
            .create_view(&wgpu::TextureViewDescriptor::default());
        let i_time: f32 = 0.5 + self.start_time.elapsed().as_micros() as f32 * 1e-6;
        let size = self.size;
        let config = Config {
            width: size.width,
            height: size.height,
            time: i_time,
        };

        let mut encoder = self.device.create_command_encoder(&Default::default());
        {
            let mut cpass = encoder.begin_compute_pass(&Default::default());
            cpass.set_pipeline(&self.pipelines.compute_pipeline);
            cpass.set_push_constants(0, bytemuck::bytes_of(&config));
            cpass.set_bind_group(0, &self.compute_bind_group, &[]);
            // Round up to next multiple
            cpass.dispatch((size.width + 16 - 1) / 16, (size.height + 16 - 1) / 16, 1);
        }
        {
            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[wgpu::RenderPassColorAttachment {
                    view: &frame_view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
                        store: true,
                    },
                }],
                depth_stencil_attachment: None,
            });
            rpass.set_pipeline(&self.pipelines.copy_pipeline);
            rpass.set_bind_group(0, &self.copy_bind_group, &[]);
            rpass.draw(0..3, 0..2);
        }
        self.queue.submit(Some(encoder.finish()));
        frame.present();
    }
}

async fn run(
    event_loop: EventLoop<CompileResult>,
    window: Window,
    initial_compilation: CompileResult,
) {
    let mut state = State::new(&window, initial_compilation).await;

    event_loop.run(move |event, _, control_flow| {
        // TODO: this may be excessive polling. It really should be synchronized with
        // swapchain presentation, but that's currently underbaked in wgpu.
        *control_flow = ControlFlow::Poll;
        match event {
            Event::RedrawRequested(_) => state.render_frame(),
            Event::MainEventsCleared => {
                window.request_redraw();
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            Event::UserEvent(result) => state.replace_pipelines(&result),
            Event::WindowEvent { window_id, event } if window_id == window.id() => match event {
                WindowEvent::Resized(new_size) => {
                    state.resize(new_size);
                    window.request_redraw();
                }
                _ => (),
            },
            _ => (),
        }
    });
}

fn main() {
    let event_loop = EventLoop::with_user_event();
    let proxy = event_loop.create_proxy();
    // Watch for changes on a background thread
    let initial_result = SpirvBuilder::new("./shaders", "spirv-unknown-spv1.5")
        .print_metadata(spirv_builder::MetadataPrintout::None)
        .watch(move |result| proxy.send_event(result).unwrap())
        .expect("Correctly setup");

    let window = Window::new(&event_loop).unwrap();
    pollster::block_on(run(event_loop, window, initial_result));
}
<commit_msg>Remove surplus comment<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.

//! A simple compute shader example that draws into a window, based on wgpu.

use std::time::Instant;

use rust_gpu_toy_shared::Config;
use wgpu::{Extent3d, ShaderModule};

use winit::dpi::PhysicalSize;
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::Window,
};

use spirv_builder::{CompileResult, SpirvBuilder};

struct Pipelines {
    compute_pipeline: wgpu::ComputePipeline,

    copy_pipeline: wgpu::RenderPipeline,
}

struct Layouts {
    compute_bg_layout: wgpu::BindGroupLayout,
    copy_bg_layout: wgpu::BindGroupLayout,

    compute_pipeline_layout: wgpu::PipelineLayout,
    copy_pipeline_layout: wgpu::PipelineLayout,
}

/// State of the runner, inspired by
/// https://sotrh.github.io/learn-wgpu/
struct State {
    pipelines: Pipelines,
    layouts: Layouts,

    surface: wgpu::Surface,
    device: wgpu::Device,
    queue: wgpu::Queue,

    surface_config: wgpu::SurfaceConfiguration,
    size: winit::dpi::PhysicalSize<u32>,

    sampler: wgpu::Sampler,

    compute_bind_group: wgpu::BindGroup,
    copy_bind_group: wgpu::BindGroup,

    start_time: Instant,
}

impl State {
    async fn new(window: &Window, compilation: CompileResult) -> Self {
        let size = window.inner_size();
        let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
        let surface = unsafe { instance.create_surface(window) };
        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: Default::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            })
            .await
            .expect("error finding adapter");
        let features = wgpu::Features::PUSH_CONSTANTS;
        let limits = wgpu::Limits {
            max_push_constant_size: std::mem::size_of::<Config>() as u32,
            ..Default::default()
        };
        let (device, queue) = adapter
            .request_device(
                &wgpu::DeviceDescriptor {
                    label: None,
                    features,
                    limits,
                },
                None,
            )
            .await
            .expect("error creating device");
        let preferred_format = surface
            .get_preferred_format(&adapter)
            .expect("Surface is compatible with adapter, as adapter creation succeeded");
        let surface_config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: preferred_format,
            width: size.width,
            height: size.height,
            present_mode: wgpu::PresentMode::Mailbox,
        };

        surface.configure(&device, &surface_config);

        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
            address_mode_u: wgpu::AddressMode::ClampToEdge,
            address_mode_v: wgpu::AddressMode::ClampToEdge,
            address_mode_w: wgpu::AddressMode::ClampToEdge,
            mag_filter: wgpu::FilterMode::Nearest,
            min_filter: wgpu::FilterMode::Nearest,
            mipmap_filter: wgpu::FilterMode::Nearest,
            ..Default::default()
        });
        let layouts = Self::create_layouts(&device);
        let pipelines = Self::create_pipelines(&device, &surface_config, &layouts, &compilation);
        let (compute_group, copy_group) = Self::bind_for_size(&device, &sampler, size, &layouts);
        let start_time = Instant::now();
        State {
            pipelines,
            layouts,
            surface,
            device,
            queue,
            surface_config,
            size,
            sampler,
            compute_bind_group: compute_group,
            copy_bind_group: copy_group,
            start_time,
        }
    }

    fn resize(&mut self, new_size: PhysicalSize<u32>) {
        if new_size.width != 0 && new_size.height != 0 {
            self.surface_config.width = new_size.width;
            self.surface_config.height = new_size.height;
            self.size = new_size;
            self.surface.configure(&self.device, &self.surface_config);

            let (compute_bind_group, copy_bind_group) =
                State::bind_for_size(&self.device, &self.sampler, new_size, &self.layouts);
            self.compute_bind_group = compute_bind_group;
            self.copy_bind_group = copy_bind_group;
        }
    }

    fn bind_for_size(
        device: &wgpu::Device,
        sampler: &wgpu::Sampler,
        size: PhysicalSize<u32>,
        layouts: &Layouts,
    ) -> (wgpu::BindGroup, wgpu::BindGroup) {
        let img = device.create_texture(&wgpu::TextureDescriptor {
            label: None,
            size: Extent3d {
                width: size.width,
                height: size.height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: wgpu::TextureFormat::Rgba8Unorm,
            usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
        });
        let img_view = img.create_view(&Default::default());
        let compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: None,
            layout: &layouts.compute_bg_layout,
            entries: &[wgpu::BindGroupEntry {
                binding: 0,
                resource: wgpu::BindingResource::TextureView(&img_view),
            }],
        });

        let copy_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: None,
            layout: &layouts.copy_bg_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: wgpu::BindingResource::TextureView(&img_view),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: wgpu::BindingResource::Sampler(&sampler),
                },
            ],
        });
        (compute_bind_group, copy_bind_group)
    }

    fn create_layouts(device: &wgpu::Device) -> Layouts {
        let copy_bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: None,
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        multisampled: false,
                        // Should filterable be false if we want nearest-neighbor?
                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
                        view_dimension: wgpu::TextureViewDimension::D2,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Sampler {
                        filtering: false,
                        comparison: false,
                    },
                    count: None,
                },
            ],
        });
        let copy_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
            label: None,
            bind_group_layouts: &[&copy_bg_layout],
            push_constant_ranges: &[],
        });
        let compute_bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("Compute Bind Group Layout"),
            entries: &[wgpu::BindGroupLayoutEntry {
                binding: 0,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::StorageTexture {
                    access: wgpu::StorageTextureAccess::WriteOnly,
                    format: wgpu::TextureFormat::Rgba8Unorm,
                    view_dimension: wgpu::TextureViewDimension::D2,
                },
                count: None,
            }],
        });
        let compute_pipeline_layout =
            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: None,
                bind_group_layouts: &[&compute_bg_layout],
                push_constant_ranges: &[wgpu::PushConstantRange {
                    stages: wgpu::ShaderStages::COMPUTE,
                    range: 0..std::mem::size_of::<Config>() as u32,
                }],
            });
        Layouts {
            compute_bg_layout,
            copy_bg_layout,
            compute_pipeline_layout,
            copy_pipeline_layout,
        }
    }

    fn replace_pipelines(&mut self, compilation: &CompileResult) {
        let new_pipelines = State::create_pipelines(
            &self.device,
            &self.surface_config,
            &self.layouts,
            compilation,
        );
        self.pipelines = new_pipelines;
    }

    fn create_pipelines(
        device: &wgpu::Device,
        surface_config: &wgpu::SurfaceConfiguration,
        layouts: &Layouts,
        compilation: &CompileResult,
    ) -> Pipelines {
        let module = State::create_shader_module(device, compilation);
        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
            label: None,
            layout: Some(&layouts.compute_pipeline_layout),
            module: &module,
            entry_point: "main",
        });
        let copy_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: None,
            layout: Some(&layouts.copy_pipeline_layout),
            vertex: wgpu::VertexState {
                module: &module,
                entry_point: "vs_main",
                buffers: &[],
            },
            fragment: Some(wgpu::FragmentState {
                module: &module,
                entry_point: "fs_main",
                targets: &[surface_config.format.into()],
            }),
            primitive: wgpu::PrimitiveState::default(),
            depth_stencil: None,
            multisample: wgpu::MultisampleState::default(),
        });
        Pipelines {
            compute_pipeline,
            copy_pipeline,
        }
    }

    fn create_shader_module(device: &wgpu::Device, compilation: &CompileResult) -> ShaderModule {
        let spirv_path = compilation.module.unwrap_single();
        let data = std::fs::read(spirv_path).expect("spirv should exist");
        let source = wgpu::util::make_spirv(&data);
        device.create_shader_module(&wgpu::ShaderModuleDescriptor {
            label: None,
            source,
        })
    }

    fn render_frame(&self) {
        let frame = match self.surface.get_current_texture() {
            Ok(output) => output,
            Err(err) => {
                eprintln!("get_current_texture error: {:?}", err);
                match err {
                    wgpu::SurfaceError::Lost => {
                        self.surface.configure(&self.device, &self.surface_config);
                    }
                    wgpu::SurfaceError::OutOfMemory => {
                        panic!();
                    }
                    _ => (),
                }
                return;
            }
        };
        let frame_view = frame
            .texture
            .create_view(&wgpu::TextureViewDescriptor::default());
        let i_time: f32 = 0.5 + self.start_time.elapsed().as_micros() as f32 * 1e-6;
        let size = self.size;
        let config = Config {
            width: size.width,
            height: size.height,
            time: i_time,
        };

        let mut encoder = self.device.create_command_encoder(&Default::default());
        {
            let mut cpass = encoder.begin_compute_pass(&Default::default());
            cpass.set_pipeline(&self.pipelines.compute_pipeline);
            cpass.set_push_constants(0, bytemuck::bytes_of(&config));
            cpass.set_bind_group(0, &self.compute_bind_group, &[]);
            // Round up to next multiple
            cpass.dispatch((size.width + 16 - 1) / 16, (size.height + 16 - 1) / 16, 1);
        }
        {
            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[wgpu::RenderPassColorAttachment {
                    view: &frame_view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
                        store: true,
                    },
                }],
                depth_stencil_attachment: None,
            });
            rpass.set_pipeline(&self.pipelines.copy_pipeline);
            rpass.set_bind_group(0, &self.copy_bind_group, &[]);
            rpass.draw(0..3, 0..2);
        }
        self.queue.submit(Some(encoder.finish()));
        frame.present();
    }
}

async fn run(
    event_loop: EventLoop<CompileResult>,
    window: Window,
    initial_compilation: CompileResult,
) {
    let mut state = State::new(&window, initial_compilation).await;

    event_loop.run(move |event, _, control_flow| {
        // TODO: this may be excessive polling. It really should be synchronized with
        // swapchain presentation, but that's currently underbaked in wgpu.
        *control_flow = ControlFlow::Poll;
        match event {
            Event::RedrawRequested(_) => state.render_frame(),
            Event::MainEventsCleared => {
                window.request_redraw();
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            Event::UserEvent(result) => state.replace_pipelines(&result),
            Event::WindowEvent { window_id, event } if window_id == window.id() => match event {
                WindowEvent::Resized(new_size) => {
                    state.resize(new_size);
                    window.request_redraw();
                }
                _ => (),
            },
            _ => (),
        }
    });
}

fn main() {
    let event_loop = EventLoop::with_user_event();
    let proxy = event_loop.create_proxy();
    // Watch for changes on a background thread
    let initial_result = SpirvBuilder::new("./shaders", "spirv-unknown-spv1.5")
        .print_metadata(spirv_builder::MetadataPrintout::None)
        .watch(move |result| proxy.send_event(result).unwrap())
        .expect("Correctly setup");

    let window = Window::new(&event_loop).unwrap();
    pollster::block_on(run(event_loop, window, initial_result));
}
<|endoftext|>


<commit_before>//! The `parser` module contains structures and functions for parsing the VQA
//! (Vector Quantized Animation) format.

use nom::{be_u32, le_u8, le_u16, le_u32};

#[derive(Debug)]
pub struct FormChunk {
    pub size: u32
}

named!(pub form_chunk<FormChunk>,
    do_parse!(
        tag!("FORM") >>
        size: be_u32 >>
        (FormChunk {
            size: size
        })
    )
);

#[derive(Debug)]
pub enum VQAVersion {
    One,
    Two,
    Three
}

named!(pub vqa_version<VQAVersion>,
    switch!(le_u16,
        1 => value!(VQAVersion::One) |
        2 => value!(VQAVersion::Two) |
        3 => value!(VQAVersion::Three)
    )
);

bitflags! {
    pub struct VQAFlags: u16 {
        const HAS_SOUND = 0b00000001;
    }
}

#[derive(Debug)]
pub struct VQAHeader {
    pub version       : VQAVersion, // VQA version number
    pub flags         : VQAFlags,   // VQA flags
    pub num_frames    : u16,        // Number of frames
    pub width         : u16,        // Movie width (pixels)
    pub height        : u16,        // Movie height (pixels)
    pub block_width   : u8,         // Width of each image block (pixels)
    pub block_height  : u8,         // Height of each image block (pixels)
    pub frame_rate    : u8,         // Frame rate of the VQA
    pub cbparts       : u8,         // How many images use the same lookup table
    pub colors        : u16,        // Max number of colors used in VQA
    pub maxblocks     : u16,        // Max number of image blocks
    pub unk1          : u32,        // Always 0?
    pub unk2          : u16,        // Some kind of size?
    pub freq          : u16,        // Sound sampling frequency
    pub channels      : u8,         // Number of sound channels
    pub bits          : u8,         // Sound resolution
    pub unk3          : u32,        // Always 0?
    pub unk4          : u16,        // 0 in old VQAs, 4 in HiColor VQAs?
    pub max_cbfz_size : u32,        // 0 in old VQAs, CBFZ size in HiColor
    pub unk5          : u32,        // Always 0?
}

named!(pub vqa_header<VQAHeader>,
    do_parse!(
        tag!("VQHD")              >>
        tag!(b"\x00\x00\x00\x2a") >> // VQAHeader is always 42 bytes long
        version: vqa_version      >>
        flags: le_u16             >>
        num_frames: le_u16        >>
        width: le_u16             >>
        height: le_u16            >>
        block_width: le_u8        >>
        block_height: le_u8       >>
        frame_rate: le_u8         >>
        cbparts: le_u8            >>
        colors: le_u16            >>
        maxblocks: le_u16         >>
        unk1: le_u32              >>
        unk2: le_u16              >>
        freq: le_u16              >>
        channels: le_u8           >>
        bits: le_u8               >>
        unk3: le_u32              >>
        unk4: le_u16              >>
        max_cbfz_size: le_u32     >>
        unk5: le_u32              >>
        (VQAHeader {
            version: version,
            flags: VQAFlags::from_bits_truncate(flags),
            num_frames: num_frames,
            width: width,
            height: height,
            block_width: block_width,
            block_height: block_height,
            frame_rate: frame_rate,
            cbparts: cbparts,
            colors: colors,
            maxblocks: maxblocks,
            unk1: unk1,
            unk2: unk2,
            freq: freq,
            channels: channels,
            bits: bits,
            unk3: unk3,
            unk4: unk4,
            max_cbfz_size: max_cbfz_size,
            unk5: unk5
        })
    )
);

#[derive(Debug)]
pub struct FINFChunk {
    pub offsets: Vec<u32>
}

named!(pub finf_chunk<FINFChunk>,
    do_parse!(
        tag!("FINF") >>
        size: be_u32 >>
        offsets: many_m_n!(size as usize / 4, size as usize / 4, le_u32) >>
        (FINFChunk {
            offsets: offsets
        })
    )
);

#[derive(Debug)]
pub struct SND2Chunk<'a> {
    pub data: &'a [u8]
}

named!(pub snd2_chunk<SND2Chunk>,
    do_parse!(
        tag!("SND2")       >>
        size: be_u32       >>
        data: take!(size)  >>
        (SND2Chunk {
            data: data
        })
    )
);

#[derive(Debug)]
pub struct VQFRChunk<'a> {
    pub size: u32,
    pub data: &'a [u8]
}

named!(pub vqfr_chunk<VQFRChunk>,
    do_parse!(
        tag!("VQFR")      >>
        size: be_u32      >>
        data: take!(size) >>
        (VQFRChunk {
            size: size,
            data: data
        })
    )
);
<commit_msg>store size of all chunks when parsing<commit_after>//! The `parser` module contains structures and functions for parsing the VQA
//! (Vector Quantized Animation) format.

use nom::{be_u32, le_u8, le_u16, le_u32};

#[derive(Debug)]
pub struct FormChunk {
    pub size: u32
}

named!(pub form_chunk<FormChunk>,
    do_parse!(
        tag!("FORM") >>
        size: be_u32 >>
        (FormChunk {
            size: size
        })
    )
);

#[derive(Debug)]
pub enum VQAVersion {
    One,
    Two,
    Three
}

named!(pub vqa_version<VQAVersion>,
    switch!(le_u16,
        1 => value!(VQAVersion::One) |
        2 => value!(VQAVersion::Two) |
        3 => value!(VQAVersion::Three)
    )
);

bitflags! {
    pub struct VQAFlags: u16 {
        const HAS_SOUND = 0b00000001;
    }
}

#[derive(Debug)]
pub struct VQAHeader {
    pub version       : VQAVersion, // VQA version number
    pub flags         : VQAFlags,   // VQA flags
    pub num_frames    : u16,        // Number of frames
    pub width         : u16,        // Movie width (pixels)
    pub height        : u16,        // Movie height (pixels)
    pub block_width   : u8,         // Width of each image block (pixels)
    pub block_height  : u8,         // Height of each image block (pixels)
    pub frame_rate    : u8,         // Frame rate of the VQA
    pub cbparts       : u8,         // How many images use the same lookup table
    pub colors        : u16,        // Max number of colors used in VQA
    pub maxblocks     : u16,        // Max number of image blocks
    pub unk1          : u32,        // Always 0?
    pub unk2          : u16,        // Some kind of size?
    pub freq          : u16,        // Sound sampling frequency
    pub channels      : u8,         // Number of sound channels
    pub bits          : u8,         // Sound resolution
    pub unk3          : u32,        // Always 0?
    pub unk4          : u16,        // 0 in old VQAs, 4 in HiColor VQAs?
    pub max_cbfz_size : u32,        // 0 in old VQAs, CBFZ size in HiColor
    pub unk5          : u32,        // Always 0?
}

named!(pub vqa_header<VQAHeader>,
    do_parse!(
        tag!("VQHD")              >>
        tag!(b"\x00\x00\x00\x2a") >> // VQAHeader is always 42 bytes long
        version: vqa_version      >>
        flags: le_u16             >>
        num_frames: le_u16        >>
        width: le_u16             >>
        height: le_u16            >>
        block_width: le_u8        >>
        block_height: le_u8       >>
        frame_rate: le_u8         >>
        cbparts: le_u8            >>
        colors: le_u16            >>
        maxblocks: le_u16         >>
        unk1: le_u32              >>
        unk2: le_u16              >>
        freq: le_u16              >>
        channels: le_u8           >>
        bits: le_u8               >>
        unk3: le_u32              >>
        unk4: le_u16              >>
        max_cbfz_size: le_u32     >>
        unk5: le_u32              >>
        (VQAHeader {
            version: version,
            flags: VQAFlags::from_bits_truncate(flags),
            num_frames: num_frames,
            width: width,
            height: height,
            block_width: block_width,
            block_height: block_height,
            frame_rate: frame_rate,
            cbparts: cbparts,
            colors: colors,
            maxblocks: maxblocks,
            unk1: unk1,
            unk2: unk2,
            freq: freq,
            channels: channels,
            bits: bits,
            unk3: unk3,
            unk4: unk4,
            max_cbfz_size: max_cbfz_size,
            unk5: unk5
        })
    )
);

#[derive(Debug)]
pub struct FINFChunk {
    pub size: u32,
    pub offsets: Vec<u32>
}

named!(pub finf_chunk<FINFChunk>,
    do_parse!(
        tag!("FINF") >>
        size: be_u32 >>
        offsets: many_m_n!(size as usize / 4, size as usize / 4, le_u32) >>
        (FINFChunk {
            size: size,
            offsets: offsets
        })
    )
);

#[derive(Debug)]
pub struct SND2Chunk<'a> {
    pub size: u32,
    pub data: &'a [u8]
}

named!(pub snd2_chunk<SND2Chunk>,
    do_parse!(
        tag!("SND2")       >>
        size: be_u32       >>
        data: take!(size)  >>
        (SND2Chunk {
            size: size,
            data: data
        })
    )
);

#[derive(Debug)]
pub struct VQFRChunk<'a> {
    pub size: u32,
    pub data: &'a [u8]
}

named!(pub vqfr_chunk<VQFRChunk>,
    do_parse!(
        tag!("VQFR")      >>
        size: be_u32      >>
        data: take!(size) >>
        (VQFRChunk {
            size: size,
            data: data
        })
    )
);
<|endoftext|>


<commit_before>use combine::{Stream, Parser, ParseError, ParseResult};
use combine::{between, many, many1, parser, satisfy_map, token, value, try, not_followed_by};
use token::Token;
use types::{Expr, List, Vector, Symbol};

pub fn parse<I>(input: I) -> Result<(Vec<Expr>, I), ParseError<I>>
where
    I: Stream<Item = Token>,
{
    // Balanced delimiters
    many(parser(expr))
        .skip(not_followed_by(token(Token::RParen)))
        .skip(not_followed_by(token(Token::RBracket)))
        .parse(input)
}

fn expr<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    parser(atom)
        .or(parser(quote))
        .or(parser(list))
        .or(parser(vector))
        .parse_stream(input)
}

fn quote<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    (
        token(Token::Quote),
        parser(expr)
    )
    .map(|(_, expr)| {
        let quote_symbol = Expr::Sym(Symbol("quote".into()));
        Expr::List(List(vec![quote_symbol, expr]))
    }).parse_stream(input)
}

fn atom<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    satisfy_map(|token| match token {
        Token::Literal(lit) => Some(Expr::from(lit)),
        Token::Symbol(sym) => Some(Expr::from(Symbol(sym))),
        _ => None,
    }).parse_stream(input)
}

fn list<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
        try(between(
            token(Token::LParen),
            token(Token::RParen),
            many1(parser(expr)).map(List).map(Expr::List).or(value(
                Expr::Nil,
            )),
        ))
        .parse_stream(input)
}

fn vector<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    try(between(
        token(Token::LBracket),
        token(Token::RBracket),
        many(parser(expr)).map(Vector).map(Expr::Vector),
    ))
    .parse_stream(input)
}
<commit_msg>Tiny parser tests<commit_after>use combine::{Stream, Parser, ParseError, ParseResult};
use combine::{between, many, many1, parser, satisfy_map, token, value, try, not_followed_by};
use token::Token;
use types::{Expr, List, Vector, Symbol};

pub fn parse<I>(input: I) -> Result<(Vec<Expr>, I), ParseError<I>>
where
    I: Stream<Item = Token>,
{
    // Balanced delimiters
    many(parser(expr))
        .skip(not_followed_by(token(Token::RParen)))
        .skip(not_followed_by(token(Token::RBracket)))
        .parse(input)
}

fn expr<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    parser(atom)
        .or(parser(quote))
        .or(parser(list))
        .or(parser(vector))
        .parse_stream(input)
}

fn quote<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    (
        token(Token::Quote),
        parser(expr)
    )
    .map(|(_, expr)| {
        let quote_symbol = Expr::Sym(Symbol("quote".into()));
        Expr::List(List(vec![quote_symbol, expr]))
    }).parse_stream(input)
}

fn atom<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    satisfy_map(|token| match token {
        Token::Literal(lit) => Some(Expr::from(lit)),
        Token::Symbol(sym) => Some(Expr::from(Symbol(sym))),
        _ => None,
    }).parse_stream(input)
}

fn list<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
        try(between(
            token(Token::LParen),
            token(Token::RParen),
            many1(parser(expr)).map(List).map(Expr::List).or(value(
                Expr::Nil,
            )),
        ))
        .parse_stream(input)
}

fn vector<I>(input: I) -> ParseResult<Expr, I>
where
    I: Stream<Item = Token>,
{
    try(between(
        token(Token::LBracket),
        token(Token::RBracket),
        many(parser(expr)).map(Vector).map(Expr::Vector),
    ))
    .parse_stream(input)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn empty_list() {
        let input = vec![Token::LParen, Token::RParen];
        let output = vec![Expr::Vector(Vector(Vec::new()))];
        let empty: &[Token] = &[];
        assert_eq!(
            Ok((output, empty)),
            parse(&*input)
        );
    }

    #[test]
    fn empty_vector() {
        let input = vec![Token::LBracket, Token::RBracket];
        let output = vec![Expr::Vector(Vector(Vec::new()))];
        let empty: &[Token] = &[];
        assert_eq!(
            Ok((output, empty)),
            parse(&*input)
        );
    }
}<|endoftext|>


<commit_before>/*
 * Copyright 2022 Miklos Vajna. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

use anyhow::Context as _;

/// Downscales a bitmap (scanned document) to 96x96 DPI to avoid huge PDFs and then converts the
/// result to PDF.
///
/// Example usage: scan-document ~/Downloads/*.jpg out.pdf
fn main() -> anyhow::Result<()> {
    // Separate earlier input and last output arg.
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);
    let output = args.last().cloned().context("no output arg")?;
    args.pop();

    // Downconvert inputs.
    let mut converteds: Vec<String> = Vec::new();
    for arg in args {
        let path: String;
        {
            let tempfile = tempfile::Builder::new().suffix(".jpg").tempfile()?;
            path = tempfile
                .path()
                .to_str()
                .context("to_str() failed")?
                .to_string();
        }
        let convert_args = vec![arg.clone(), "-density".into(), "96".into(), path.clone()];
        println!("convert {}", convert_args.join(" "));
        let exit_status = std::process::Command::new("convert")
            .args(convert_args)
            .status()?;
        let exit_code = exit_status.code().context("code() failed")?;
        if exit_code != 0 {
            return Err(anyhow::anyhow!("convert failed"));
        }
        converteds.push(path.to_string());
    }

    // Convert downconverted intputs into a single output.
    let mut convert_args = converteds.clone();
    convert_args.push(output);
    println!("convert {}", convert_args.join(" "));
    let exit_status = std::process::Command::new("convert")
        .args(convert_args)
        .status()?;
    let exit_code = exit_status.code().context("code() failed")?;
    if exit_code != 0 {
        return Err(anyhow::anyhow!("convert failed"));
    }

    for converted in converteds {
        std::fs::remove_file(converted)?;
    }

    Ok(())
}
<commit_msg>scan-document: avoid manual std::fs::remove_file()<commit_after>/*
 * Copyright 2022 Miklos Vajna. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

use anyhow::Context as _;

/// Converts a tempfile to a path that external commands can access.
fn tempfile_to_path(tempfile: &tempfile::NamedTempFile) -> anyhow::Result<String> {
    Ok(tempfile
        .path()
        .to_str()
        .context("to_str() failed")?
        .to_string())
}

/// Downscales a bitmap (scanned document) to 96x96 DPI to avoid huge PDFs and then converts the
/// result to PDF.
///
/// Example usage: scan-document ~/Downloads/*.jpg out.pdf
fn main() -> anyhow::Result<()> {
    // Separate earlier input and last output arg.
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);
    let output = args.last().cloned().context("no output arg")?;
    args.pop();

    // Downconvert inputs.
    let mut converteds: Vec<tempfile::NamedTempFile> = Vec::new();
    for arg in args {
        let tempfile = tempfile::Builder::new().suffix(".jpg").tempfile()?;
        let path = tempfile_to_path(&tempfile)?;
        let convert_args = vec![arg.clone(), "-density".into(), "96".into(), path.clone()];
        println!("convert {}", convert_args.join(" "));
        let exit_status = std::process::Command::new("convert")
            .args(convert_args)
            .status()?;
        let exit_code = exit_status.code().context("code() failed")?;
        if exit_code != 0 {
            return Err(anyhow::anyhow!("convert failed"));
        }
        converteds.push(tempfile);
    }

    // Convert downconverted intputs into a single output.
    let mut convert_args: Vec<String> = converteds
        .iter()
        .map(|tempfile| Ok(tempfile_to_path(&tempfile)?))
        .collect::<anyhow::Result<Vec<String>>>()?;
    convert_args.push(output);
    println!("convert {}", convert_args.join(" "));
    let exit_status = std::process::Command::new("convert")
        .args(convert_args)
        .status()?;
    let exit_code = exit_status.code().context("code() failed")?;
    if exit_code != 0 {
        return Err(anyhow::anyhow!("convert failed"));
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use rand::{self, Rng};
use vecmath::{vec2_len, vec2_sub, vec2_scale, vec2_add, vec2_normalized};

use types::{Object, State, Extent, Scalar, Pt, Position, Velocity, Hunter, Obstacle};
use types::ObstacleKind::*;
use types::CollisionShape::*;
use transition::Transition;
use transition::TransitionState::*;
use transition::TransitionDirection::*;

const MIN_FIELD_MARGIN: Scalar = 30.0;
const FIELD_VELOCITY_COEFF: Scalar = 0.4;
const OBSTACLE_SIZE_COEFF: Scalar = 0.3;
const MIN_OBSTACLE_TO_HUNTER_COEFF: Scalar = 0.1;
const TRANSITION_DURATION: Scalar =  0.5;
const HOLD_INVISIBILITY_DURATION: Scalar = 0.5;
const COLLISION_VELOCITY_COEFF: Scalar = 0.5;
const ATTRACTIVE_FORCE_DURATION: Scalar = 5.0;
const ATTRACTIVE_FORCE_COEFF: Scalar = 1.25;
const SPECIAL_OBSTACLE_PROBABILITY: f32 = 0.1;
const HUNTER_FORCE: Scalar = 1.0 * 0.1;
const HUNTER_FORCE_SIZE_COEFF: Scalar = 1.5;
const SCORE_PER_PREY: Scalar = 10.0;
const SCORE_COEFF_INCREMENT_MULTIPLIER: Scalar = 0.1;
const SPECIAL_OBSTACLE_STATE_SCORE_MULTIPLIER: Scalar = 2.0;

/// The engine implements the game logic
///
/// It relies on user input given as 2d coordinates
pub struct Engine {
    state: Option<State>,
    min_distance: Scalar,
    rng: RefCell<rand::XorShiftRng>,
}

impl Engine {

    fn rnd_obj_pos_in_field(field: &Extent, half_size: Pt,
                            rng: &mut rand::XorShiftRng) -> Position {
        Self::clamp_to_field(field, half_size, [rng.gen_range(0.0, field[0]),
                                                rng.gen_range(0.0, field[1])])
    }

    fn clamp_to_field(field: &Extent, half_size: Pt, mut pos: Position) -> Position {
        if pos[0] - half_size < 0.0 {
            pos[0] = half_size;
        }
        if pos[0] + half_size > field[0] {
            pos[0] = field[0] - half_size;
        }
        if pos[1] - half_size < 0.0 {
            pos[1] = half_size;
        }
        if pos[1] + half_size > field[1] {
            pos[1] = field[1] - half_size;
        }
        pos
    }

    fn state_from_field(field: Extent) -> State {
        assert!(field[0].min(field[1]) >= 320.0, "Playing field is too small");
        let margin = (field[0].min(field[1]) * 0.05).max(MIN_FIELD_MARGIN);
        let half_size = (margin - (MIN_FIELD_MARGIN / 6.0)) / 2.0;

        let mut rng = rand::weak_rng();
        let prey_pos = Self::rnd_obj_pos_in_field(&field, half_size, &mut rng);

        State {
            field: field,
            hunter: Hunter {
                object: Object {
                    pos: [-half_size * 2.0, -half_size * 2.0],
                    half_size: half_size,
                    shape: Circle
                },
                force: 0.0,
                velocity: [0.0, 0.0],
            },
            prey: Object {
                pos: prey_pos,
                half_size: half_size,
                shape: Square
            },
            obstacles: Vec::new(),
            obstacle_opacity: Transition::new(1.0, 0.0, TRANSITION_DURATION),
            attracting_force: Transition::new(0.0, HUNTER_FORCE * ATTRACTIVE_FORCE_COEFF,
                                              TRANSITION_DURATION),
            score: 0,
            score_coeff: 1.0,
            last_dt: 1.0,
        }
    }

    fn set_state(&mut self, state: State) {
        self.min_distance = (state.field[0].powi(2) + state.field[1].powi(2)).sqrt()
                             * MIN_OBSTACLE_TO_HUNTER_COEFF;
        self.state = Some(state);
    }

    fn new_obstacle(rng: &mut rand::XorShiftRng, s: &mut State, min_distance: Scalar) {
        s.prey.pos = Self::rnd_obj_pos_in_field(&s.field, s.prey.half_size, rng);

        let mut half_size = s.hunter.object.half_size * OBSTACLE_SIZE_COEFF;
        let kind = match rng.gen_range(0.0f32, 1.0) {
            _p if _p < SPECIAL_OBSTACLE_PROBABILITY => {
                half_size *= 2.0;
                if rng.gen_range(0.0f32, 1.0) > 0.5 {
                    InvisibiltySwitch
                } else {
                    AttractiveForceSwitch
                }
            },
            _               => Deadly,
        };
        let vel: Velocity = [
            rng.gen_range(-s.field[0] * FIELD_VELOCITY_COEFF,
                           s.field[0] * FIELD_VELOCITY_COEFF),
            rng.gen_range(-s.field[1] * FIELD_VELOCITY_COEFF,
                           s.field[1] * FIELD_VELOCITY_COEFF),
        ];

        let mut pos = s.hunter.object.pos;
        while vec2_len(vec2_sub(pos, s.hunter.object.pos)) < min_distance {
            pos = Self::rnd_obj_pos_in_field(&s.field, s.prey.half_size, rng)
        }
        s.obstacles.push(Obstacle {
            kind: kind,
            object: Object {
                pos: Self::clamp_to_field(&s.field, half_size, pos),
                half_size: half_size,
                shape: Circle,
            },
            velocity: vel,
        });
    }

    fn advect_obstacles(s: &mut State, dt: f64) {
        // Move and collide the obstacles.
        for obstacle in s.obstacles.iter_mut() {

            let obj = &mut obstacle.object;

            let repell_velocity =
                if s.hunter.force > 0.0 || s.attracting_force.current > 0.0 {
                    let vel = vec2_sub(obj.pos, s.hunter.object.pos);
                    let velocity_scale = vec2_len(vel) / (s.hunter.object.half_size * 2.0 * 4.0);

                    if velocity_scale <= 1.0 {
                        vec2_scale(vel, (1.0 - velocity_scale)
                                        * (s.hunter.force - s.attracting_force.current))
                    } else {
                        [0.0, 0.0]
                    }
                } else {
                    [0.0, 0.0]
                };
            obstacle.velocity = vec2_add(obstacle.velocity, repell_velocity);
            obj.pos = vec2_add(obj.pos, vec2_scale(obstacle.velocity , dt));

            if obj.left() <= 0.0 {
                obstacle.velocity[0] = -obstacle.velocity[0];
            } else if obj.right() >= s.field[0] {
                obstacle.velocity[0] = -obstacle.velocity[0];
            }
            if obj.top() <= 0.0 {
                obstacle.velocity[1] = -obstacle.velocity[1];
            } else if obj.bottom() >= s.field[1] {
                obstacle.velocity[1] = -obstacle.velocity[1];
            }

            obj.pos = Self::clamp_to_field(&s.field, obj.half_size,
                                            obj.pos);
        }
    }

    fn pos_out_of_field(field: &Extent, pos: &Position) -> bool {
        pos[0] < 0.0 || pos[0] > field[0] || pos[1] < 0.0 || pos[1] > field[1]
    }
}

impl Engine {

    pub fn from_field(field: Extent) -> Engine {
        let mut e = Engine {
            state: None,
            min_distance: 0.0,
            rng: RefCell::new(rand::weak_rng()),
        };
        e.set_state(Self::state_from_field(field));
        e
    }

    /// Reset the engine to use the given game-state.
    /// Can be used to setup a new game as well.
    pub fn reset(&mut self, field: Extent) {
        self.set_state(Self::state_from_field(field));
    }

    /// Update the game state.
    ///
    /// If the returned value is the last game-state, it indicates that the player
    /// is game-over.
    pub fn update(&mut self, dt: f64) -> Result<(), State> {
        let mut is_game_over = false;

        if let Some(ref mut s) = self.state {

            s.last_dt = dt;
            if !Self::pos_out_of_field(&s.field, &s.hunter.object.pos)
                && vec2_len(s.hunter.velocity) > 10.0 {
                s.score_coeff += SCORE_COEFF_INCREMENT_MULTIPLIER * dt;
            }

            if s.hunter.object.intersects(&s.prey) {
                let mut multiplier = s.score_coeff;
                for transition in &[&s.obstacle_opacity, &s.attracting_force] {
                    if !transition.is_pristine() {
                        multiplier *= SPECIAL_OBSTACLE_STATE_SCORE_MULTIPLIER;
                    }
                }
                s.score += (SCORE_PER_PREY * multiplier) as u32;
                Self::new_obstacle(&mut self.rng.borrow_mut(), s, self.min_distance);
            }// check hunter-prey intersection

            Self::advect_obstacles(s, dt);

            // advance transitions
            for &mut (ref mut t, duration) in
                                [(&mut s.obstacle_opacity, HOLD_INVISIBILITY_DURATION),
                                 (&mut s.attracting_force, ATTRACTIVE_FORCE_DURATION),]
                                 .iter_mut() {
                match t.state() {
                    Start => {
                        if t.direction == ToFrom {
                            t.reverse();
                        }
                    }
                    InProgress => {
                        t.advance(dt);
                    }
                    Finished => {
                        t.state_time += dt;

                        if t.state_time >= TRANSITION_DURATION + duration {
                            let dir = t.direction.clone();
                            t.reverse();

                            if dir == FromTo {
                                t.advance(dt);
                            }
                        }
                    }
                }
            }

            // Handle obstacle hits
            for obstacle in &mut s.obstacles {
                if obstacle.object.intersects(&s.hunter.object) {
                    match obstacle.kind {
                        Deadly => {
                            is_game_over = true;
                            break;
                        },
                         InvisibiltySwitch
                        |AttractiveForceSwitch => {
                            let new_vel = vec2_scale(
                                                vec2_normalized(
                                                        vec2_sub(obstacle.object.pos,
                                                                 s.hunter.object.pos)),
                                                vec2_len(obstacle.velocity)
                                                         * COLLISION_VELOCITY_COEFF);
                            obstacle.velocity = vec2_add(new_vel, s.hunter.velocity);
                            let transition = match obstacle.kind {
                                InvisibiltySwitch => &mut s.obstacle_opacity,
                                AttractiveForceSwitch => &mut s.attracting_force,
                                Deadly => unreachable!(),
                            };

                            if transition.state() == Start &&
                               transition.direction == FromTo {
                                transition.advance(dt);
                            }
                        },
                    }
                }
            }

            // hunter velocity only remains once we get a move input
            s.hunter.velocity = [0.0, 0.0];
        } // end have game state

        if is_game_over {
            Err(self.state.take().unwrap())
        } else {
            Ok(())
        }
    }

    pub fn state(&self) -> &Option<State> {
        &self.state
    }

    /// Position will be clamped into the playing field
    pub fn set_hunter_pos(&mut self, pos: Position) {
        if let Some(ref mut s) = self.state {
            s.hunter.velocity = vec2_scale(vec2_sub(pos, s.hunter.object.pos),
                                           1.0 / s.last_dt);
            s.hunter.object.pos = pos;

            if Self::pos_out_of_field(&s.field, &pos) {
                s.score_coeff = 1.0;
            }
        }
    }

    /// If enabled, a forcefield is created around the hunter, usually repelling
    /// spheres.
    pub fn set_hunter_force(&mut self, enabled: bool) {
        if let Some(ref mut s) = self.state {
            if enabled {
                s.hunter.force += HUNTER_FORCE;
                s.hunter.object.half_size *= HUNTER_FORCE_SIZE_COEFF;
            } else {
                s.hunter.force -= HUNTER_FORCE;
                s.hunter.object.half_size *= 1.0 / HUNTER_FORCE_SIZE_COEFF;
            }
        }
    }
}
<commit_msg>fix(state): compute absolute hunter-force<commit_after>use std::cell::RefCell;

use rand::{self, Rng};
use vecmath::{vec2_len, vec2_sub, vec2_scale, vec2_add, vec2_normalized};

use types::{Object, State, Extent, Scalar, Pt, Position, Velocity, Hunter, Obstacle};
use types::ObstacleKind::*;
use types::CollisionShape::*;
use transition::Transition;
use transition::TransitionState::*;
use transition::TransitionDirection::*;

const MIN_FIELD_MARGIN: Scalar = 30.0;
const FIELD_VELOCITY_COEFF: Scalar = 0.4;
const OBSTACLE_SIZE_COEFF: Scalar = 0.3;
const MIN_OBSTACLE_TO_HUNTER_COEFF: Scalar = 0.1;
const TRANSITION_DURATION: Scalar =  0.5;
const HOLD_INVISIBILITY_DURATION: Scalar = 0.5;
const COLLISION_VELOCITY_COEFF: Scalar = 0.5;
const ATTRACTIVE_FORCE_DURATION: Scalar = 5.0;
const ATTRACTIVE_FORCE_COEFF: Scalar = 1.25;
const SPECIAL_OBSTACLE_PROBABILITY: f32 = 0.1;
const HUNTER_FORCE: Scalar = 1.0 * 0.1;
const HUNTER_FORCE_SIZE_COEFF: Scalar = 1.5;
const SCORE_PER_PREY: Scalar = 10.0;
const SCORE_COEFF_INCREMENT_MULTIPLIER: Scalar = 0.1;
const SPECIAL_OBSTACLE_STATE_SCORE_MULTIPLIER: Scalar = 2.0;

/// The engine implements the game logic
///
/// It relies on user input given as 2d coordinates
pub struct Engine {
    state: Option<State>,
    min_distance: Scalar,
    rng: RefCell<rand::XorShiftRng>,
}

impl Engine {

    fn rnd_obj_pos_in_field(field: &Extent, half_size: Pt,
                            rng: &mut rand::XorShiftRng) -> Position {
        Self::clamp_to_field(field, half_size, [rng.gen_range(0.0, field[0]),
                                                rng.gen_range(0.0, field[1])])
    }

    fn clamp_to_field(field: &Extent, half_size: Pt, mut pos: Position) -> Position {
        if pos[0] - half_size < 0.0 {
            pos[0] = half_size;
        }
        if pos[0] + half_size > field[0] {
            pos[0] = field[0] - half_size;
        }
        if pos[1] - half_size < 0.0 {
            pos[1] = half_size;
        }
        if pos[1] + half_size > field[1] {
            pos[1] = field[1] - half_size;
        }
        pos
    }

    fn hunter_half_size(field: &Extent) -> Scalar {
        let margin = (field[0].min(field[1]) * 0.05).max(MIN_FIELD_MARGIN);
        (margin - (MIN_FIELD_MARGIN / 6.0)) / 2.0
    }

    fn state_from_field(field: Extent) -> State {
        assert!(field[0].min(field[1]) >= 320.0, "Playing field is too small");
        let half_size = Self::hunter_half_size(&field);

        let mut rng = rand::weak_rng();
        let prey_pos = Self::rnd_obj_pos_in_field(&field, half_size, &mut rng);

        State {
            field: field,
            hunter: Hunter {
                object: Object {
                    pos: [-half_size * 2.0, -half_size * 2.0],
                    half_size: half_size,
                    shape: Circle
                },
                force: 0.0,
                velocity: [0.0, 0.0],
            },
            prey: Object {
                pos: prey_pos,
                half_size: half_size,
                shape: Square
            },
            obstacles: Vec::new(),
            obstacle_opacity: Transition::new(1.0, 0.0, TRANSITION_DURATION),
            attracting_force: Transition::new(0.0, HUNTER_FORCE * ATTRACTIVE_FORCE_COEFF,
                                              TRANSITION_DURATION),
            score: 0,
            score_coeff: 1.0,
            last_dt: 1.0,
        }
    }

    fn set_state(&mut self, state: State) {
        self.min_distance = (state.field[0].powi(2) + state.field[1].powi(2)).sqrt()
                             * MIN_OBSTACLE_TO_HUNTER_COEFF;
        self.state = Some(state);
    }

    fn new_obstacle(rng: &mut rand::XorShiftRng, s: &mut State, min_distance: Scalar) {
        s.prey.pos = Self::rnd_obj_pos_in_field(&s.field, s.prey.half_size, rng);

        let mut half_size = s.hunter.object.half_size * OBSTACLE_SIZE_COEFF;
        let kind = match rng.gen_range(0.0f32, 1.0) {
            _p if _p < SPECIAL_OBSTACLE_PROBABILITY => {
                half_size *= 2.0;
                if rng.gen_range(0.0f32, 1.0) > 0.5 {
                    InvisibiltySwitch
                } else {
                    AttractiveForceSwitch
                }
            },
            _               => Deadly,
        };
        let vel: Velocity = [
            rng.gen_range(-s.field[0] * FIELD_VELOCITY_COEFF,
                           s.field[0] * FIELD_VELOCITY_COEFF),
            rng.gen_range(-s.field[1] * FIELD_VELOCITY_COEFF,
                           s.field[1] * FIELD_VELOCITY_COEFF),
        ];

        let mut pos = s.hunter.object.pos;
        while vec2_len(vec2_sub(pos, s.hunter.object.pos)) < min_distance {
            pos = Self::rnd_obj_pos_in_field(&s.field, s.prey.half_size, rng)
        }
        s.obstacles.push(Obstacle {
            kind: kind,
            object: Object {
                pos: Self::clamp_to_field(&s.field, half_size, pos),
                half_size: half_size,
                shape: Circle,
            },
            velocity: vel,
        });
    }

    fn advect_obstacles(s: &mut State, dt: f64) {
        // Move and collide the obstacles.
        for obstacle in s.obstacles.iter_mut() {

            let obj = &mut obstacle.object;

            let repell_velocity =
                if s.hunter.force > 0.0 || s.attracting_force.current > 0.0 {
                    let vel = vec2_sub(obj.pos, s.hunter.object.pos);
                    let velocity_scale = vec2_len(vel) / (s.hunter.object.half_size * 2.0 * 4.0);

                    if velocity_scale <= 1.0 {
                        vec2_scale(vel, (1.0 - velocity_scale)
                                        * (s.hunter.force - s.attracting_force.current))
                    } else {
                        [0.0, 0.0]
                    }
                } else {
                    [0.0, 0.0]
                };
            obstacle.velocity = vec2_add(obstacle.velocity, repell_velocity);
            obj.pos = vec2_add(obj.pos, vec2_scale(obstacle.velocity , dt));

            if obj.left() <= 0.0 {
                obstacle.velocity[0] = -obstacle.velocity[0];
            } else if obj.right() >= s.field[0] {
                obstacle.velocity[0] = -obstacle.velocity[0];
            }
            if obj.top() <= 0.0 {
                obstacle.velocity[1] = -obstacle.velocity[1];
            } else if obj.bottom() >= s.field[1] {
                obstacle.velocity[1] = -obstacle.velocity[1];
            }

            obj.pos = Self::clamp_to_field(&s.field, obj.half_size,
                                            obj.pos);
        }
    }

    fn pos_out_of_field(field: &Extent, pos: &Position) -> bool {
        pos[0] < 0.0 || pos[0] > field[0] || pos[1] < 0.0 || pos[1] > field[1]
    }
}

impl Engine {

    pub fn from_field(field: Extent) -> Engine {
        let mut e = Engine {
            state: None,
            min_distance: 0.0,
            rng: RefCell::new(rand::weak_rng()),
        };
        e.set_state(Self::state_from_field(field));
        e
    }

    /// Reset the engine to use the given game-state.
    /// Can be used to setup a new game as well.
    pub fn reset(&mut self, field: Extent) {
        self.set_state(Self::state_from_field(field));
    }

    /// Update the game state.
    ///
    /// If the returned value is the last game-state, it indicates that the player
    /// is game-over.
    pub fn update(&mut self, dt: f64) -> Result<(), State> {
        let mut is_game_over = false;

        if let Some(ref mut s) = self.state {

            s.last_dt = dt;
            if !Self::pos_out_of_field(&s.field, &s.hunter.object.pos)
                && vec2_len(s.hunter.velocity) > 10.0 {
                s.score_coeff += SCORE_COEFF_INCREMENT_MULTIPLIER * dt;
            }

            if s.hunter.object.intersects(&s.prey) {
                let mut multiplier = s.score_coeff;
                for transition in &[&s.obstacle_opacity, &s.attracting_force] {
                    if !transition.is_pristine() {
                        multiplier *= SPECIAL_OBSTACLE_STATE_SCORE_MULTIPLIER;
                    }
                }
                s.score += (SCORE_PER_PREY * multiplier) as u32;
                Self::new_obstacle(&mut self.rng.borrow_mut(), s, self.min_distance);
            }// check hunter-prey intersection

            Self::advect_obstacles(s, dt);

            // advance transitions
            for &mut (ref mut t, duration) in
                                [(&mut s.obstacle_opacity, HOLD_INVISIBILITY_DURATION),
                                 (&mut s.attracting_force, ATTRACTIVE_FORCE_DURATION),]
                                 .iter_mut() {
                match t.state() {
                    Start => {
                        if t.direction == ToFrom {
                            t.reverse();
                        }
                    }
                    InProgress => {
                        t.advance(dt);
                    }
                    Finished => {
                        t.state_time += dt;

                        if t.state_time >= TRANSITION_DURATION + duration {
                            let dir = t.direction.clone();
                            t.reverse();

                            if dir == FromTo {
                                t.advance(dt);
                            }
                        }
                    }
                }
            }

            // Handle obstacle hits
            for obstacle in &mut s.obstacles {
                if obstacle.object.intersects(&s.hunter.object) {
                    match obstacle.kind {
                        Deadly => {
                            is_game_over = true;
                            break;
                        },
                         InvisibiltySwitch
                        |AttractiveForceSwitch => {
                            let new_vel = vec2_scale(
                                                vec2_normalized(
                                                        vec2_sub(obstacle.object.pos,
                                                                 s.hunter.object.pos)),
                                                vec2_len(obstacle.velocity)
                                                         * COLLISION_VELOCITY_COEFF);
                            obstacle.velocity = vec2_add(new_vel, s.hunter.velocity);
                            let transition = match obstacle.kind {
                                InvisibiltySwitch => &mut s.obstacle_opacity,
                                AttractiveForceSwitch => &mut s.attracting_force,
                                Deadly => unreachable!(),
                            };

                            if transition.state() == Start &&
                               transition.direction == FromTo {
                                transition.advance(dt);
                            }
                        },
                    }
                }
            }

            // hunter velocity only remains once we get a move input
            s.hunter.velocity = [0.0, 0.0];
        } // end have game state

        if is_game_over {
            Err(self.state.take().unwrap())
        } else {
            Ok(())
        }
    }

    pub fn state(&self) -> &Option<State> {
        &self.state
    }

    /// Position will be clamped into the playing field
    pub fn set_hunter_pos(&mut self, pos: Position) {
        if let Some(ref mut s) = self.state {
            s.hunter.velocity = vec2_scale(vec2_sub(pos, s.hunter.object.pos),
                                           1.0 / s.last_dt);
            s.hunter.object.pos = pos;

            if Self::pos_out_of_field(&s.field, &pos) {
                s.score_coeff = 1.0;
            }
        }
    }

    /// If enabled, a forcefield is created around the hunter, usually repelling
    /// spheres.
    pub fn set_hunter_force(&mut self, enabled: bool) {
        if let Some(ref mut s) = self.state {
            if enabled {
                s.hunter.force = HUNTER_FORCE;
                s.hunter.object.half_size = Self::hunter_half_size(&s.field)
                                            * HUNTER_FORCE_SIZE_COEFF;
            } else {
                s.hunter.force = 0.0;
                s.hunter.object.half_size = Self::hunter_half_size(&s.field);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::{io, string};

/// Represents errors returned by `decode` or `encode`.
#[derive(Debug)]
pub enum OscError {
    StringError(string::FromUtf8Error),
    ReadError(io::Error),
    BadPacket(&'static str),
    BadAddress(&'static str),
    BadMessage(&'static str),
    BadString(&'static str),
    BadArg(String),
    BadBundle(String),
    Unimplemented,
}
<commit_msg>Implement std::error::Error for OscError<commit_after>use std::{error, fmt, io, string};

/// Represents errors returned by `decode` or `encode`.
#[derive(Debug)]
pub enum OscError {
    StringError(string::FromUtf8Error),
    ReadError(io::Error),
    BadPacket(&'static str),
    BadAddress(&'static str),
    BadMessage(&'static str),
    BadString(&'static str),
    BadArg(String),
    BadBundle(String),
    Unimplemented,
}

impl fmt::Display for OscError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            OscError::StringError(err) => write!(f, "reading OSC string as utf-8: {}", err),
            OscError::ReadError(err) => write!(f, "reading from buffer: {}", err),
            OscError::BadPacket(msg) => write!(f, "{}", msg),
            OscError::BadAddress(msg) => write!(f, "{}", msg),
            OscError::BadMessage(msg) => write!(f, "bad OSC message: {}", msg),
            OscError::BadString(msg) => write!(f, "bad OSC string: {}", msg),
            OscError::BadArg(msg) => write!(f, "bad OSC argument: {}", msg),
            OscError::BadBundle(msg) => write!(f, "bad OSC bundle: {}", msg),
            OscError::Unimplemented => write!(f, "unimplemented"),
        }
    }
}

impl error::Error for OscError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            OscError::StringError(ref err) => Some(err),
            OscError::ReadError(ref err) => Some(err),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use graphics::math::{ Scalar, Matrix2d };
use piston_window::{ G2d };

use operation::Operation;
use game::Context;

pub const DEFAULT_LENGTH: Scalar = 150.0;
pub const LENGTH_RANGE: Scalar = 240.0;
const LENGTH_TRANS_STEP: i32 = 200;
pub const DEFAULT_Y: Scalar = 0.95;
const MAX_Y: Scalar = 0.95;
const MIN_Y: Scalar = 0.5;
const MOVE: Scalar = 0.4;
const FRICTION: Scalar = 0.95;
pub const HEIGHT: Scalar = 10.0;
const COLOR: [f32; 4] = [0.8, 0.8, 0.9, 0.8];

pub struct Player {
    pub pos: [Scalar; 2],
    pub vec: [Scalar; 2],
    pub length: Scalar,
    pub length_trans: Scalar,
    pub length_trans_step: i32,
}

impl Player {
    pub fn new(con: Context) -> Self {
        Player {
            pos: [(con.width / 2) as Scalar, con.height as f64 * DEFAULT_Y],
            vec: [0.0, 0.0],
            length: DEFAULT_LENGTH,
            length_trans: 0.0,
            length_trans_step: 0,
        }
    }

    fn transform(&mut self) {
        if self.length_trans_step != 0 {
            self.length_trans_step -= 1;
            self.length += (self.length_trans - self.length) / LENGTH_TRANS_STEP as Scalar;
        }
    }

    pub fn transform_length(&mut self, trans: Scalar) {
        self.length_trans_step = LENGTH_TRANS_STEP;
        self.length_trans = trans;
    }

    pub fn update(&mut self, con: &Context) -> &mut Self {
        self.transform();
        self.pos = [self.pos[0] + self.vec[0], self.pos[1] + self.vec[1]];
        for v in &mut self.vec {
            *v = *v * FRICTION;
        }

        if self.pos[1] < con.height as f64 * MIN_Y {
            self.pos[1] = con.height as f64 * MIN_Y;
        }

        if self.pos[1] > con.height as f64 * MAX_Y {
            self.pos[1] = con.height as f64 * MAX_Y;
        }
        self
    }

    pub fn press(&mut self, op: &Operation) {
        use operation::{ Operation, Direction, };
        match op {
            &Operation::Move(Direction::Left) => {
                self.vec = [self.vec[0] - MOVE, self.vec[1]];
            },
            &Operation::Move(Direction::Right) => {
                self.vec = [self.vec[0] + MOVE, self.vec[1]];
            },
            &Operation::Move(Direction::Up) => {
                self.vec = [self.vec[0], self.vec[1] - MOVE];
            },
            &Operation::Move(Direction::Down) => {
                self.vec = [self.vec[0], self.vec[1] + MOVE];
            },
            _ => {},
        }
    }

    pub fn draw(&self, t: Matrix2d, g: &mut G2d) {
        use figure;
        figure::rect(self.pos[0] - self.length / 2.0, self.pos[1], self.length, HEIGHT, COLOR, t, g);
    }
}
<commit_msg>Reset vector when the bar is on the edge<commit_after>use graphics::math::{ Scalar, Matrix2d };
use piston_window::{ G2d };

use operation::Operation;
use game::Context;

pub const DEFAULT_LENGTH: Scalar = 150.0;
pub const LENGTH_RANGE: Scalar = 240.0;
const LENGTH_TRANS_STEP: i32 = 200;
pub const DEFAULT_Y: Scalar = 0.95;
const MAX_Y: Scalar = 0.95;
const MIN_Y: Scalar = 0.5;
const MOVE: Scalar = 0.4;
const FRICTION: Scalar = 0.95;
pub const HEIGHT: Scalar = 10.0;
const COLOR: [f32; 4] = [0.8, 0.8, 0.9, 0.8];

pub struct Player {
    pub pos: [Scalar; 2],
    pub vec: [Scalar; 2],
    pub length: Scalar,
    pub length_trans: Scalar,
    pub length_trans_step: i32,
}

impl Player {
    pub fn new(con: Context) -> Self {
        Player {
            pos: [(con.width / 2) as Scalar, con.height as f64 * DEFAULT_Y],
            vec: [0.0, 0.0],
            length: DEFAULT_LENGTH,
            length_trans: 0.0,
            length_trans_step: 0,
        }
    }

    fn transform(&mut self) {
        if self.length_trans_step != 0 {
            self.length_trans_step -= 1;
            self.length += (self.length_trans - self.length) / LENGTH_TRANS_STEP as Scalar;
        }
    }

    pub fn transform_length(&mut self, trans: Scalar) {
        self.length_trans_step = LENGTH_TRANS_STEP;
        self.length_trans = trans;
    }

    pub fn update(&mut self, con: &Context) -> &mut Self {
        self.transform();
        self.pos = [self.pos[0] + self.vec[0], self.pos[1] + self.vec[1]];
        for v in &mut self.vec {
            *v = *v * FRICTION;
        }

        if self.pos[1] < con.height as f64 * MIN_Y {
            self.pos[1] = con.height as f64 * MIN_Y;
            self.vec[1] = 0.;
        }

        if self.pos[1] > con.height as f64 * MAX_Y {
            self.pos[1] = con.height as f64 * MAX_Y;
            self.vec[1] = 0.;
        }
        self
    }

    pub fn press(&mut self, op: &Operation) {
        use operation::{ Operation, Direction, };
        match op {
            &Operation::Move(Direction::Left) => {
                self.vec = [self.vec[0] - MOVE, self.vec[1]];
            },
            &Operation::Move(Direction::Right) => {
                self.vec = [self.vec[0] + MOVE, self.vec[1]];
            },
            &Operation::Move(Direction::Up) => {
                self.vec = [self.vec[0], self.vec[1] - MOVE];
            },
            &Operation::Move(Direction::Down) => {
                self.vec = [self.vec[0], self.vec[1] + MOVE];
            },
            _ => {},
        }
    }

    pub fn draw(&self, t: Matrix2d, g: &mut G2d) {
        use figure;
        figure::rect(self.pos[0] - self.length / 2.0, self.pos[1], self.length, HEIGHT, COLOR, t, g);
    }
}
<|endoftext|>


<commit_before>extern crate tokio;
extern crate tokio_executor;
extern crate futures;

use tokio::executor::current_thread::{self, block_on_all, CurrentThread};

use std::any::Any;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::thread;
use std::time::Duration;

use futures::task;
use futures::future::{self, lazy};
use futures::prelude::*;
use futures::sync::oneshot;

#[test]
fn spawn_from_block_on_all() {
    let cnt = Rc::new(Cell::new(0));
    let c = cnt.clone();

    let msg = current_thread::block_on_all(lazy(move || {
        c.set(1 + c.get());

        // Spawn!
        current_thread::spawn(lazy(move || {
            c.set(1 + c.get());
            Ok::<(), ()>(())
        }));

        Ok::<_, ()>("hello")
    })).unwrap();

    assert_eq!(2, cnt.get());
    assert_eq!(msg, "hello");
}

#[test]
fn block_waits() {
    let cnt = Rc::new(Cell::new(0));
    let cnt2 = cnt.clone();

    let (tx, rx) = oneshot::channel();

    thread::spawn(|| {
        thread::sleep(Duration::from_millis(1000));
        tx.send(()).unwrap();
    });

    block_on_all(rx.then(move |_| {
        cnt.set(1 + cnt.get());
        Ok::<_, ()>(())
    })).unwrap();

    assert_eq!(1, cnt2.get());
}

#[test]
fn spawn_many() {
    const ITER: usize = 200;

    let cnt = Rc::new(Cell::new(0));
    let mut current_thread = CurrentThread::new();

    for _ in 0..ITER {
        let cnt = cnt.clone();
        current_thread.spawn(lazy(move || {
            cnt.set(1 + cnt.get());
            Ok::<(), ()>(())
        }));
    }

    current_thread.run().unwrap();

    assert_eq!(cnt.get(), ITER);
}

#[test]
fn does_not_set_global_executor_by_default() {
    use tokio_executor::Executor;

    block_on_all(lazy(|| {
        tokio_executor::DefaultExecutor::current()
            .spawn(Box::new(lazy(|| ok())))
            .unwrap_err();

        ok()
    })).unwrap();
}

#[test]
fn spawn_from_block_on_future() {
    let cnt = Rc::new(Cell::new(0));

    let mut current_thread = CurrentThread::new();

    current_thread.block_on(lazy(|| {
        let cnt = cnt.clone();

        current_thread::spawn(lazy(move || {
            cnt.set(1 + cnt.get());
            Ok(())
        }));

        Ok::<_, ()>(())
    })).unwrap();

    current_thread.run().unwrap();

    assert_eq!(1, cnt.get());
}

struct Never(Rc<()>);

impl Future for Never {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        Ok(Async::NotReady)
    }
}

#[test]
fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
    let mut rc = Rc::new(());

    let mut current_thread = CurrentThread::new();
    current_thread.spawn(Never(rc.clone()));

    drop(current_thread);

    // Ensure the daemon is dropped
    assert!(Rc::get_mut(&mut rc).is_some());

    // Using the global spawn fn

    let mut rc = Rc::new(());

    let mut current_thread = CurrentThread::new();

    current_thread.block_on(lazy(|| {
        current_thread::spawn(Never(rc.clone()));
        Ok::<_, ()>(())
    })).unwrap();

    drop(current_thread);

    // Ensure the daemon is dropped
    assert!(Rc::get_mut(&mut rc).is_some());
}

#[test]
#[should_panic]
fn nesting_run() {
    block_on_all(lazy(|| {
        block_on_all(lazy(|| {
            ok()
        })).unwrap();

        ok()
    })).unwrap();
}

#[test]
#[should_panic]
fn run_in_future() {
    block_on_all(lazy(|| {
        current_thread::spawn(lazy(|| {
            block_on_all(lazy(|| {
                ok()
            })).unwrap();
            ok()
        }));
        ok()
    })).unwrap();
}

#[test]
fn tick_on_infini_future() {
    let num = Rc::new(Cell::new(0));

    struct Infini {
        num: Rc<Cell<usize>>,
    }

    impl Future for Infini {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.num.set(1 + self.num.get());
            task::current().notify();
            Ok(Async::NotReady)
        }
    }

    CurrentThread::new()
        .spawn(Infini {
            num: num.clone(),
        })
        .turn(None)
        .unwrap();

    assert_eq!(1, num.get());
}

#[test]
fn tasks_are_scheduled_fairly() {
    let state = Rc::new(RefCell::new([0, 0]));

    struct Spin {
        state: Rc<RefCell<[i32; 2]>>,
        idx: usize,
    }

    impl Future for Spin {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            let mut state = self.state.borrow_mut();

            if self.idx == 0 {
                let diff = state[0] - state[1];

                assert!(diff.abs() <= 1);

                if state[0] >= 50 {
                    return Ok(().into());
                }
            }

            state[self.idx] += 1;

            if state[self.idx] >= 100 {
                return Ok(().into());
            }

            task::current().notify();
            Ok(Async::NotReady)
        }
    }

    block_on_all(lazy(|| {
        current_thread::spawn(Spin {
            state: state.clone(),
            idx: 0,
        });

        current_thread::spawn(Spin {
            state: state,
            idx: 1,
        });

        ok()
    })).unwrap();
}

#[test]
fn spawn_and_turn() {
    let cnt = Rc::new(Cell::new(0));
    let c = cnt.clone();

    let mut current_thread = CurrentThread::new();

    // Spawn a basic task to get the executor to turn
    current_thread.spawn(lazy(move || {
        Ok(())
    }));

    // Turn once...
    current_thread.turn(None).unwrap();

    current_thread.spawn(lazy(move || {
        c.set(1 + c.get());

        // Spawn!
        current_thread::spawn(lazy(move || {
            c.set(1 + c.get());
            Ok::<(), ()>(())
        }));

        Ok(())
    }));

    // This does not run the newly spawned thread
    current_thread.turn(None).unwrap();
    assert_eq!(1, cnt.get());

    // This runs the newly spawned thread
    current_thread.turn(None).unwrap();
    assert_eq!(2, cnt.get());
}

#[test]
fn spawn_in_drop() {
    let mut current_thread = CurrentThread::new();

    let (tx, rx) = oneshot::channel();

    struct OnDrop<F: FnOnce()>(Option<F>);

    impl<F: FnOnce()> Drop for OnDrop<F> {
        fn drop(&mut self) {
            (self.0.take().unwrap())();
        }
    }

    struct MyFuture {
        _data: Box<Any>,
    }

    impl Future for MyFuture {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            Ok(().into())
        }
    }

    current_thread.spawn({
        MyFuture {
            _data: Box::new(OnDrop(Some(move || {
                current_thread::spawn(lazy(move || {
                    tx.send(()).unwrap();
                    Ok(())
                }));
            }))),
        }
    });

    current_thread.block_on(rx).unwrap();
    current_thread.run().unwrap();
}

#[test]
fn hammer_turn() {
    use futures::sync::mpsc;

    const ITER: usize = 100;
    const N: usize = 100;
    const THREADS: usize = 4;

    for _ in 0..ITER {
        let mut ths = vec![];

        // Add some jitter
        for _ in 0..THREADS {
            let th = thread::spawn(|| {
                let mut current_thread = CurrentThread::new();

                let (tx, rx) = mpsc::unbounded();

                current_thread.spawn({
                    rx.for_each(|_| {
                        Ok(())
                    })
                    .map_err(|e| panic!("err={:?}", e))
                });

                thread::spawn(move || {
                    for _ in 0..N {
                        tx.unbounded_send(()).unwrap();
                        thread::yield_now();
                    }
                });

                while !current_thread.is_idle() {
                    current_thread.turn(None).unwrap();
                }
            });

            ths.push(th);
        }

        for th in ths {
            th.join().unwrap();
        }
    }
}

fn ok() -> future::FutureResult<(), ()> {
    future::ok(())
}
<commit_msg>Improve current thread tests (#161)<commit_after>extern crate tokio;
extern crate tokio_executor;
extern crate futures;

use tokio::executor::current_thread::{self, block_on_all, CurrentThread};

use std::any::Any;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::thread;
use std::time::Duration;

use futures::task;
use futures::future::{self, lazy};
use futures::prelude::*;
use futures::sync::oneshot;

#[test]
fn spawn_from_block_on_all() {
    let cnt = Rc::new(Cell::new(0));
    let c = cnt.clone();

    let msg = current_thread::block_on_all(lazy(move || {
        c.set(1 + c.get());

        // Spawn!
        current_thread::spawn(lazy(move || {
            c.set(1 + c.get());
            Ok::<(), ()>(())
        }));

        Ok::<_, ()>("hello")
    })).unwrap();

    assert_eq!(2, cnt.get());
    assert_eq!(msg, "hello");
}

#[test]
fn block_waits() {
    let (tx, rx) = oneshot::channel();

    thread::spawn(|| {
        thread::sleep(Duration::from_millis(1000));
        tx.send(()).unwrap();
    });

    let cnt = Rc::new(Cell::new(0));
    let cnt2 = cnt.clone();

    block_on_all(rx.then(move |_| {
        cnt.set(1 + cnt.get());
        Ok::<_, ()>(())
    })).unwrap();

    assert_eq!(1, cnt2.get());
}

#[test]
fn spawn_many() {
    const ITER: usize = 200;

    let cnt = Rc::new(Cell::new(0));
    let mut current_thread = CurrentThread::new();

    for _ in 0..ITER {
        let cnt = cnt.clone();
        current_thread.spawn(lazy(move || {
            cnt.set(1 + cnt.get());
            Ok::<(), ()>(())
        }));
    }

    current_thread.run().unwrap();

    assert_eq!(cnt.get(), ITER);
}

#[test]
fn does_not_set_global_executor_by_default() {
    use tokio_executor::Executor;

    block_on_all(lazy(|| {
        tokio_executor::DefaultExecutor::current()
            .spawn(Box::new(lazy(|| ok())))
            .unwrap_err();

        ok()
    })).unwrap();
}

#[test]
fn spawn_from_block_on_future() {
    let cnt = Rc::new(Cell::new(0));

    let mut current_thread = CurrentThread::new();

    current_thread.block_on(lazy(|| {
        let cnt = cnt.clone();

        current_thread::spawn(lazy(move || {
            cnt.set(1 + cnt.get());
            Ok(())
        }));

        Ok::<_, ()>(())
    })).unwrap();

    current_thread.run().unwrap();

    assert_eq!(1, cnt.get());
}

struct Never(Rc<()>);

impl Future for Never {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        Ok(Async::NotReady)
    }
}

#[test]
fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
    let mut rc = Rc::new(());

    let mut current_thread = CurrentThread::new();
    current_thread.spawn(Never(rc.clone()));

    drop(current_thread);

    // Ensure the daemon is dropped
    assert!(Rc::get_mut(&mut rc).is_some());

    // Using the global spawn fn

    let mut rc = Rc::new(());

    let mut current_thread = CurrentThread::new();

    current_thread.block_on(lazy(|| {
        current_thread::spawn(Never(rc.clone()));
        Ok::<_, ()>(())
    })).unwrap();

    drop(current_thread);

    // Ensure the daemon is dropped
    assert!(Rc::get_mut(&mut rc).is_some());
}

#[test]
#[should_panic]
fn nesting_run() {
    block_on_all(lazy(|| {
        block_on_all(lazy(|| {
            ok()
        })).unwrap();

        ok()
    })).unwrap();
}

#[test]
#[should_panic]
fn run_in_future() {
    block_on_all(lazy(|| {
        current_thread::spawn(lazy(|| {
            block_on_all(lazy(|| {
                ok()
            })).unwrap();
            ok()
        }));
        ok()
    })).unwrap();
}

#[test]
fn tick_on_infini_future() {
    let num = Rc::new(Cell::new(0));

    struct Infini {
        num: Rc<Cell<usize>>,
    }

    impl Future for Infini {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.num.set(1 + self.num.get());
            task::current().notify();
            Ok(Async::NotReady)
        }
    }

    CurrentThread::new()
        .spawn(Infini {
            num: num.clone(),
        })
        .turn(None)
        .unwrap();

    assert_eq!(1, num.get());
}

#[test]
fn tasks_are_scheduled_fairly() {
    let state = Rc::new(RefCell::new([0, 0]));

    struct Spin {
        state: Rc<RefCell<[i32; 2]>>,
        idx: usize,
    }

    impl Future for Spin {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            let mut state = self.state.borrow_mut();

            if self.idx == 0 {
                let diff = state[0] - state[1];

                assert!(diff.abs() <= 1);

                if state[0] >= 50 {
                    return Ok(().into());
                }
            }

            state[self.idx] += 1;

            if state[self.idx] >= 100 {
                return Ok(().into());
            }

            task::current().notify();
            Ok(Async::NotReady)
        }
    }

    block_on_all(lazy(|| {
        current_thread::spawn(Spin {
            state: state.clone(),
            idx: 0,
        });

        current_thread::spawn(Spin {
            state: state,
            idx: 1,
        });

        ok()
    })).unwrap();
}

#[test]
fn spawn_and_turn() {
    let cnt = Rc::new(Cell::new(0));
    let c = cnt.clone();

    let mut current_thread = CurrentThread::new();

    // Spawn a basic task to get the executor to turn
    current_thread.spawn(lazy(move || {
        Ok(())
    }));

    // Turn once...
    current_thread.turn(None).unwrap();

    current_thread.spawn(lazy(move || {
        c.set(1 + c.get());

        // Spawn!
        current_thread::spawn(lazy(move || {
            c.set(1 + c.get());
            Ok::<(), ()>(())
        }));

        Ok(())
    }));

    // This does not run the newly spawned thread
    current_thread.turn(None).unwrap();
    assert_eq!(1, cnt.get());

    // This runs the newly spawned thread
    current_thread.turn(None).unwrap();
    assert_eq!(2, cnt.get());
}

#[test]
fn spawn_in_drop() {
    let mut current_thread = CurrentThread::new();

    let (tx, rx) = oneshot::channel();

    current_thread.spawn({
        struct OnDrop<F: FnOnce()>(Option<F>);

        impl<F: FnOnce()> Drop for OnDrop<F> {
            fn drop(&mut self) {
                (self.0.take().unwrap())();
            }
        }

        struct MyFuture {
            _data: Box<Any>,
        }

        impl Future for MyFuture {
            type Item = ();
            type Error = ();

            fn poll(&mut self) -> Poll<(), ()> {
                Ok(().into())
            }
        }

        MyFuture {
            _data: Box::new(OnDrop(Some(move || {
                current_thread::spawn(lazy(move || {
                    tx.send(()).unwrap();
                    Ok(())
                }));
            }))),
        }
    });

    current_thread.block_on(rx).unwrap();
    current_thread.run().unwrap();
}

#[test]
fn hammer_turn() {
    use futures::sync::mpsc;

    const ITER: usize = 100;
    const N: usize = 100;
    const THREADS: usize = 4;

    for _ in 0..ITER {
        let mut ths = vec![];

        // Add some jitter
        for _ in 0..THREADS {
            let th = thread::spawn(|| {
                let mut current_thread = CurrentThread::new();

                let (tx, rx) = mpsc::unbounded();

                current_thread.spawn({
                    let cnt = Rc::new(Cell::new(0));
                    let c = cnt.clone();

                    rx.for_each(move |_| {
                        c.set(1 + c.get());
                        Ok(())
                    })
                    .map_err(|e| panic!("err={:?}", e))
                    .map(move |v| {
                        assert_eq!(N, cnt.get());
                        v
                    })
                });

                thread::spawn(move || {
                    for _ in 0..N {
                        tx.unbounded_send(()).unwrap();
                        thread::yield_now();
                    }
                });

                while !current_thread.is_idle() {
                    current_thread.turn(None).unwrap();
                }
            });

            ths.push(th);
        }

        for th in ths {
            th.join().unwrap();
        }
    }
}

fn ok() -> future::FutureResult<(), ()> {
    future::ok(())
}
<|endoftext|>