# HG changeset patch # User Wuzzy # Date 1540940277 -3600 # Node ID aeac678d7c79e8da5b05669270b40849159348b1 # Parent 1ac129df8e5bf75d6dddc34558303214bd15c67a# Parent bf77c4d2294f99990e1660785a670e08b6f769b4 Merge diff -r 1ac129df8e5b -r aeac678d7c79 hedgewars/uLandOutline.pas --- a/hedgewars/uLandOutline.pas Tue Oct 30 23:51:01 2018 +0100 +++ b/hedgewars/uLandOutline.pas Tue Oct 30 23:57:57 2018 +0100 @@ -215,55 +215,4 @@ end else inc(i) end; - -function CheckIntersect(V1, V2, V3, V4: TPoint): boolean; -var c1, c2, dm: LongInt; -begin - CheckIntersect:= false; - dm:= (V4.y - V3.y) * (V2.x - V1.x) - (V4.x - V3.x) * (V2.y - V1.y); - c1:= (V4.x - V3.x) * (V1.y - V3.y) - (V4.y - V3.y) * (V1.x - V3.x); - if dm = 0 then - exit; - - CheckIntersect:= true; - c2:= (V2.x - V3.x) * (V1.y - V3.y) - (V2.y - V3.y) * (V1.x - V3.x); - if dm > 0 then - begin - if (c1 < 0) or (c1 > dm) then - CheckIntersect:= false - else if (c2 < 0) or (c2 > dm) then - CheckIntersect:= false; - end - else - begin - if (c1 > 0) or (c1 < dm) then - CheckIntersect:= false - else if (c2 > 0) or (c2 < dm) then - CheckIntersect:= false; - end; - - //AddFileLog('1 (' + inttostr(V1.x) + ',' + inttostr(V1.y) + ')x(' + inttostr(V2.x) + ',' + inttostr(V2.y) + ')'); - //AddFileLog('2 (' + inttostr(V3.x) + ',' + inttostr(V3.y) + ')x(' + inttostr(V4.x) + ',' + inttostr(V4.y) + ')'); -end; - - -function CheckSelfIntersect(var pa: TPixAr; ind: Longword): boolean; -var i: Longword; -begin - CheckSelfIntersect:= false; - if (ind <= 0) or (LongInt(ind) >= Pred(pa.Count)) then - exit; - - CheckSelfIntersect:= true; - for i:= 1 to pa.Count - 3 do - if (i <= ind - 1) or (i >= ind + 2) then - begin - if (i <> ind - 1) and CheckIntersect(pa.ar[ind], pa.ar[ind - 1], pa.ar[i], pa.ar[i - 1]) then - exit; - if (i <> ind + 2) and CheckIntersect(pa.ar[ind], pa.ar[ind + 1], pa.ar[i], pa.ar[i - 1]) then - exit; - end; - CheckSelfIntersect:= false -end; - end. diff -r 1ac129df8e5b -r aeac678d7c79 rust/integral-geometry/src/lib.rs --- a/rust/integral-geometry/src/lib.rs Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/integral-geometry/src/lib.rs Tue Oct 30 23:57:57 2018 +0100 @@ -37,6 +37,79 @@ pub fn max_norm(self) -> i32 { std::cmp::max(self.x.abs(), self.y.abs()) } + + #[inline] + pub fn transform(self, matrix: &[i32; 4]) -> Self { + Point::new(matrix[0] * self.x + matrix[1] * self.y, + matrix[2] * self.x + matrix[3] * self.y) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct Size { + pub width: usize, + pub height: usize, +} + +impl Size { + #[inline] + pub fn new(width: usize, height: usize) -> Self { + Size { width, height } + } + + #[inline] + pub fn square(size: usize) -> Self { + Size { width: size, height: size } + } + + #[inline] + pub fn area(&self) -> usize { + self.width * self.height + } + + #[inline] + pub fn linear_index(&self, x: usize, y: usize) -> usize { + y * self.width + x + } + + #[inline] + pub fn is_power_of_two(&self) -> bool { + self.width.is_power_of_two() && self.height.is_power_of_two() + } + + #[inline] + pub fn to_mask(&self) -> SizeMask { + SizeMask::new(*self) + } +} + +pub struct SizeMask{ size: Size } + +impl SizeMask { + #[inline] + pub fn new(size: Size) -> Self { + assert!(size.is_power_of_two()); + let size = Size { + width: !(size.width - 1), + height: !(size.height - 1) + }; + SizeMask { size } + } + + #[inline] + pub fn contains_x>(&self, x: T) -> bool { + (self.size.width & x.into()) == 0 + } + + #[inline] + pub fn contains_y>(&self, y: T) -> bool { + (self.size.height & y.into()) == 0 + } + + #[inline] + pub fn contains(&self, point: Point) -> bool { + self.contains_x(point.x as usize) && self.contains_y(point.y as usize) + } } macro_rules! bin_op_impl { diff -r 1ac129df8e5b -r aeac678d7c79 rust/land2d/src/lib.rs --- a/rust/land2d/src/lib.rs Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/land2d/src/lib.rs Tue Oct 30 23:57:57 2018 +0100 @@ -4,23 +4,21 @@ use std::cmp; use std::ops; -use integral_geometry::{ArcPoints, EquidistantPoints, LinePoints, Point}; +use integral_geometry::{ + ArcPoints, EquidistantPoints, LinePoints, + Point, Size, SizeMask +}; pub struct Land2D { pixels: vec2d::Vec2D, - width_mask: usize, - height_mask: usize, + mask: SizeMask } impl Land2D { - pub fn new(width: usize, height: usize, fill_value: T) -> Self { - assert!(width.is_power_of_two()); - assert!(height.is_power_of_two()); - + pub fn new(size: Size, fill_value: T) -> Self { Self { - pixels: vec2d::Vec2D::new(width, height, fill_value), - width_mask: !(width - 1), - height_mask: !(height - 1), + pixels: vec2d::Vec2D::new(size, fill_value), + mask: size.to_mask() } } @@ -36,12 +34,12 @@ #[inline] pub fn is_valid_x(&self, x: i32) -> bool { - (x as usize & self.width_mask) == 0 + self.mask.contains_x(x as usize) } #[inline] pub fn is_valid_y(&self, y: i32) -> bool { - (y as usize & self.height_mask) == 0 + self.mask.contains_y(y as usize) } #[inline] @@ -50,6 +48,11 @@ } #[inline] + pub fn rows(&self) -> impl Iterator { + self.pixels.rows() + } + + #[inline] pub fn map U>(&mut self, y: i32, x: i32, f: F) -> U { if self.is_valid_coordinate(x, y) { unsafe { @@ -214,6 +217,30 @@ .sum() } + fn fill_row(&mut self, center: Point, offset: Point, value: T) -> usize { + let row_index = center.y + offset.y; + if self.is_valid_y(row_index) { + let from_x = cmp::max(0, center.x - offset.x) as usize; + let to_x = cmp::min(self.width() - 1, (center.x + offset.x) as usize); + self.pixels[row_index as usize][from_x..=to_x] + .iter_mut().for_each(|v| *v = value); + to_x - from_x + 1 + } else { + 0 + } + } + + pub fn fill_circle(&mut self, center: Point, radius: i32, value: T) -> usize { + let transforms = + [[0, 1, 1, 0], [0, 1, -1, 0], + [1, 0, 0, 1], [1, 0, 0, -1]]; + ArcPoints::new(radius).map(|vector| { + transforms.iter().map(|m| + self.fill_row(center, vector.transform(m), value) + ).sum::() + }).sum() + } + pub fn draw_thick_line(&mut self, from: Point, to: Point, radius: i32, value: T) -> usize { let mut result = 0; @@ -240,7 +267,7 @@ #[test] fn basics() { - let l: Land2D = Land2D::new(32, 64, 0); + let l: Land2D = Land2D::new(Size::new(32, 64), 0); assert!(l.is_valid_coordinate(0, 0)); assert!(!l.is_valid_coordinate(-1, -1)); @@ -252,7 +279,7 @@ #[test] fn fill() { - let mut l: Land2D = Land2D::new(128, 128, 0); + let mut l: Land2D = Land2D::new(Size::square(128), 0); l.draw_line(Point::new(0, 0), Point::new(32, 96), 1); l.draw_line(Point::new(32, 96), Point::new(64, 32), 1); diff -r 1ac129df8e5b -r aeac678d7c79 rust/landgen/Cargo.toml --- a/rust/landgen/Cargo.toml Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/landgen/Cargo.toml Tue Oct 30 23:57:57 2018 +0100 @@ -5,3 +5,4 @@ [dependencies] integral-geometry = { path = "../integral-geometry" } +land2d = { path = "../land2d" } diff -r 1ac129df8e5b -r aeac678d7c79 rust/landgen/src/lib.rs --- a/rust/landgen/src/lib.rs Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/landgen/src/lib.rs Tue Oct 30 23:57:57 2018 +0100 @@ -1,6 +1,20 @@ -mod outline; +mod template_based; extern crate integral_geometry; +extern crate land2d; + +pub struct LandGenerationParameters { + zero: T, + basic: T, +} + +pub trait LandGenerator { + fn generate_land>( + &self, + parameters: LandGenerationParameters, + random_numbers: &mut I, + ) -> land2d::Land2D; +} #[cfg(test)] mod tests { diff -r 1ac129df8e5b -r aeac678d7c79 rust/landgen/src/outline.rs --- a/rust/landgen/src/outline.rs Tue Oct 30 23:51:01 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -use integral_geometry::Point; - -pub struct Outline { - points: Vec, -} - -fn check_intersect( - segment1_start: &Point, - segment1_end: &Point, - segment2_start: &Point, - segment2_end: &Point, -) -> bool { - let dm: i32 = (segment2_end.y - segment2_start.y) * (segment1_end.x - segment1_start.x) - - (segment2_end.x - segment2_start.x) * (segment1_end.y - segment1_start.y); - - if dm == 0 { - return false; - } - - let c1: i32 = (segment2_end.x - segment2_start.x) * (segment1_start.y - segment2_start.y) - - (segment2_end.y - segment2_start.y) * (segment1_start.x - segment2_start.x); - - if dm > 0 { - if (c1 < 0) || (c1 > dm) { - return false; - } - } else { - if (c1 > 0) || (c1 < dm) { - return false; - } - } - - let c2: i32 = (segment1_end.x - segment2_start.x) * (segment1_start.y - segment2_start.y) - - (segment1_end.y - segment2_start.y) * (segment1_start.x - segment2_start.x); - - if dm > 0 { - if (c2 < 0) || (c2 > dm) { - return false; - } - } else { - if (c2 > 0) || (c2 < dm) { - return false; - } - } - - true -} - -impl Outline { - fn check_intersects_self_at_index(&self, index: usize) -> bool { - if index <= 0 || index > self.points.len() { - return false; - } - - for i in 1..=self.points.len() - 3 { - if i <= index - 1 || i >= index + 2 { - if i != index - 1 && check_intersect( - &self.points[index], - &self.points[index - 1], - &self.points[i], - &self.points[i - 1], - ) { - return true; - } - if i != index + 2 && check_intersect( - &self.points[index], - &self.points[index + 1], - &self.points[i], - &self.points[i - 1], - ) { - return true; - } - } - } - - false - } -} - -#[cfg(test)] -#[test] -fn intersection() { - let p1 = Point { x: 0, y: 0 }; - let p2 = Point { x: 0, y: 10 }; - let p3 = Point { x: -5, y: 5 }; - let p4 = Point { x: 5, y: 5 }; - let p5 = Point { x: 5, y: 16 }; - - assert!(check_intersect(&p1, &p2, &p3, &p4)); - assert!(!check_intersect(&p1, &p2, &p3, &p5)); -} diff -r 1ac129df8e5b -r aeac678d7c79 rust/landgen/src/template_based.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/template_based.rs Tue Oct 30 23:57:57 2018 +0100 @@ -0,0 +1,53 @@ +use integral_geometry::{Point, Size}; +use land2d::Land2D; +use LandGenerationParameters; +use LandGenerator; + +struct OutlineTemplate { + islands: Vec>, + fill_points: Vec, + size: Size, + can_flip: bool, + can_invert: bool, + can_mirror: bool, + is_negative: bool, +} + +struct TemplatedLandGenerator { + outline_template: OutlineTemplate, +} + +impl OutlineTemplate {} + +impl TemplatedLandGenerator { + fn new(outline_template: OutlineTemplate) -> Self { + Self { outline_template } + } +} + +impl LandGenerator for TemplatedLandGenerator { + fn generate_land>( + &self, + parameters: LandGenerationParameters, + random_numbers: &mut I, + ) -> Land2D { + let mut pa = Vec::new(); + + for island in &self.outline_template.islands { + let mut island_points = Vec::new(); + + for p in island { + island_points.push(p); + } + + pa.push(island_points); + } + + let mut land = Land2D::new( + self.outline_template.size, + parameters.basic, + ); + + land + } +} diff -r 1ac129df8e5b -r aeac678d7c79 rust/lfprng/src/lib.rs --- a/rust/lfprng/src/lib.rs Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/lfprng/src/lib.rs Tue Oct 30 23:57:57 2018 +0100 @@ -4,7 +4,7 @@ } impl LaggedFibonacciPRNG { - fn new(init_values: &[u8]) -> Self { + pub fn new(init_values: &[u8]) -> Self { let mut buf = [0xa98765 + 68; 64]; for i in 0..std::cmp::min(init_values.len(), 54) { @@ -34,13 +34,13 @@ } #[inline] - fn get_random(&mut self, modulo: u32) -> u32 { + pub fn get_random(&mut self, modulo: u32) -> u32 { self.get_next(); self.get_next() % modulo } #[inline] - fn add_randomness(&mut self, value: u32) { + pub fn add_randomness(&mut self, value: u32) { self.index = (self.index + 1) & 0x3f; self.circular_buffer[self.index] ^= value; } diff -r 1ac129df8e5b -r aeac678d7c79 rust/theme-editor/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/theme-editor/Cargo.toml Tue Oct 30 23:57:57 2018 +0100 @@ -0,0 +1,17 @@ +[package] +name = "theme-editor" +version = "0.1.0" +authors = ["Hedgewars Project"] +edition = "2018" + +[dependencies.sdl2] +version = "0.31" +default-features = true +features = ["image"] + +[dependencies] +land2d = { path = "../land2d" } +landgen = { path = "../landgen" } +lfprng = { path = "../lfprng" } +integral-geometry = { path = "../integral-geometry" } +rand = "0.5" \ No newline at end of file diff -r 1ac129df8e5b -r aeac678d7c79 rust/theme-editor/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/theme-editor/src/main.rs Tue Oct 30 23:57:57 2018 +0100 @@ -0,0 +1,111 @@ +use sdl2::{ + keyboard::Scancode, + event::EventType, + surface::Surface, + pixels::{ + PixelFormatEnum, Color + } +}; + +use integral_geometry::{Point, Size}; + +use rand::{ + thread_rng, RngCore, Rng, + distributions::uniform::SampleUniform +}; + +use landgen::{ + LandGenerator, + LandGenerationParameters +}; +use land2d::Land2D; +use lfprng::LaggedFibonacciPRNG; + +struct LandSource { + rnd: LaggedFibonacciPRNG, + generator: T +} + +impl LandSource { + fn new(generator: T) -> Self { + let mut init = [0u8; 64]; + thread_rng().fill_bytes(&mut init); + LandSource { + rnd: LaggedFibonacciPRNG::new(&init), + generator + } + } + fn next(&mut self, parameters: LandGenerationParameters) -> Land2D { + self.generator.generate_land(parameters, &mut self.rnd) + } +} + +fn fill_pixels(pixels: &mut [u8], land: &Land2D) { + for (surf_row, land_row) in pixels.chunks_mut(land.width() * 4).zip(land.rows()) { + for (surf_pixel, land_pixel) in surf_row.chunks_mut(4).zip(land_row) { + if let [b, g, r, a] = surf_pixel { + *a = 255; *r = *land_pixel as u8; + } + } + } +} + +fn fill_texture(surface: &mut Surface, land: &Land2D) { + if surface.must_lock() { + surface.with_lock_mut(|data| fill_pixels(data, land)); + } else { + surface.without_lock_mut().map(|data| fill_pixels(data, land)); + } +} + +fn rnd(max: T) -> T { + thread_rng().gen_range(T::default(), max) +} + +const WIDTH: u32 = 512; +const HEIGHT: u32 = 512; +const SIZE: Size = Size {width: 512, height: 512}; + +fn main() { + let sdl = sdl2::init().unwrap(); + let _image = sdl2::image::init(sdl2::image::INIT_PNG).unwrap(); + let events = sdl.event().unwrap(); + + let mut pump = sdl.event_pump().unwrap(); + let video = sdl.video().unwrap(); + let window = video.window("Theme Editor", WIDTH, HEIGHT) + .position_centered() + .build().unwrap(); + + let mut land_surf = Surface::new(WIDTH, HEIGHT, PixelFormatEnum::ARGB8888).unwrap(); + + fn point() -> Point { + Point::new(rnd(WIDTH as i32), rnd(HEIGHT as i32)) + } + + let mut land = Land2D::new(SIZE, 0); + for i in 0..32 { + land.draw_thick_line(point(), point(), rnd(5), u32::max_value()); + + land.fill_circle(point(), rnd(60), u32::max_value()); + } + + fill_texture(&mut land_surf, &land); + + let mut win_surf = window.surface(&pump).unwrap(); + let win_rect = win_surf.rect(); + land_surf.blit(land_surf.rect(), &mut win_surf, win_rect).unwrap(); + win_surf.update_window(); + + 'pool: loop { + use sdl2::event::Event::*; + pump.pump_events(); + + while let Some(event) = pump.poll_event() { + match event { + Quit{ .. } => break 'pool, + _ => () + } + } + } +} \ No newline at end of file diff -r 1ac129df8e5b -r aeac678d7c79 rust/vec2d/Cargo.toml --- a/rust/vec2d/Cargo.toml Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/vec2d/Cargo.toml Tue Oct 30 23:57:57 2018 +0100 @@ -4,3 +4,4 @@ authors = ["Andrey Korotaev "] [dependencies] +integral-geometry = { path = "../integral-geometry" } diff -r 1ac129df8e5b -r aeac678d7c79 rust/vec2d/src/lib.rs --- a/rust/vec2d/src/lib.rs Tue Oct 30 23:51:01 2018 +0100 +++ b/rust/vec2d/src/lib.rs Tue Oct 30 23:57:57 2018 +0100 @@ -1,10 +1,12 @@ +extern crate integral_geometry; + use std::ops::{Index, IndexMut}; use std::slice::SliceIndex; +use integral_geometry::Size; pub struct Vec2D { data: Vec, - width: usize, - height: usize, + size: Size, } impl Index for Vec2D { @@ -12,62 +14,70 @@ #[inline] fn index(&self, row: usize) -> &[T] { - debug_assert!(row < self.height); + debug_assert!(row < self.height()); - let pos = row * self.width; + let pos = row * self.width(); - &self.data[pos..pos + self.width] + &self.data[pos..pos + self.width()] } } impl IndexMut for Vec2D { #[inline] fn index_mut(&mut self, row: usize) -> &mut [T] { - debug_assert!(row < self.height); + debug_assert!(row < self.height()); + + let pos = row * self.width(); + + &mut self.data[pos..pos + self.size.width] + } +} - let pos = row * self.width; +impl Vec2D { + #[inline] + pub fn width(&self) -> usize { + self.size.width + } - &mut self.data[pos..pos + self.width] + #[inline] + pub fn height(&self) -> usize { + self.size.height + } + + #[inline] + pub fn size(&self) -> Size { + self.size } } impl Vec2D { - pub fn new(width: usize, height: usize, value: T) -> Self { - Self { - data: vec![value; width * height], - width, - height, - } - } - - #[inline] - pub fn width(&self) -> usize { - self.width - } - - #[inline] - pub fn height(&self) -> usize { - self.height + pub fn new(size: Size, value: T) -> Self { + Self { size, data: vec![value; size.area()] } } #[inline] pub fn get(&self, row: usize, column: usize) -> Option<&>::Output> { - self.data.get(row * self.width + column) + self.data.get(row * self.width() + column) } #[inline] pub fn get_mut(&mut self, row: usize, column: usize) -> Option<&mut >::Output> { - self.data.get_mut(row * self.width + column) + self.data.get_mut(row * self.size.width + column) } #[inline] pub unsafe fn get_unchecked(&self, row: usize, column: usize) -> &>::Output { - self.data.get_unchecked(row * self.width + column) + self.data.get_unchecked(row * self.width() + column) } #[inline] pub unsafe fn get_unchecked_mut(&mut self, row: usize, column: usize) -> &mut >::Output { - self.data.get_unchecked_mut(row * self.width + column) + self.data.get_unchecked_mut(row * self.size.width + column) + } + + #[inline] + pub fn rows(&self) -> impl Iterator { + self.data.chunks(self.width()) } } @@ -77,10 +87,10 @@ #[test] fn basics() { - let mut v: Vec2D = Vec2D::new(2, 3, 0xff); + let mut v: Vec2D = Vec2D::new(Size::new(2, 3), 0xff); - assert_eq!(v.width, 2); - assert_eq!(v.height, 3); + assert_eq!(v.width(), 2); + assert_eq!(v.height(), 3); assert_eq!(v[0][0], 0xff); assert_eq!(v[2][1], 0xff);