Merge
authorWuzzy <Wuzzy2@mail.ru>
Tue, 30 Oct 2018 23:57:57 +0100
changeset 14039 aeac678d7c79
parent 14038 1ac129df8e5b (current diff)
parent 14033 bf77c4d2294f (diff)
child 14040 128fbd36eee4
Merge
rust/landgen/src/outline.rs
--- 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.
--- 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<T: Into<usize>>(&self, x: T) -> bool {
+        (self.size.width & x.into()) == 0
+    }
+
+    #[inline]
+    pub fn contains_y<T: Into<usize>>(&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 {
--- 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<T> {
     pixels: vec2d::Vec2D<T>,
-    width_mask: usize,
-    height_mask: usize,
+    mask: SizeMask
 }
 
 impl<T: Copy + PartialEq> Land2D<T> {
-    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<Item = &[T]> {
+        self.pixels.rows()
+    }
+
+    #[inline]
     pub fn map<U: Default, F: FnOnce(&mut T) -> 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::<usize>()
+        }).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<u8> = Land2D::new(32, 64, 0);
+        let l: Land2D<u8> = 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<u8> = Land2D::new(128, 128, 0);
+        let mut l: Land2D<u8> = 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);
--- 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" }
--- 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<T> {
+    zero: T,
+    basic: T,
+}
+
+pub trait LandGenerator {
+    fn generate_land<T: Copy + PartialEq, I: Iterator<Item = u32>>(
+        &self,
+        parameters: LandGenerationParameters<T>,
+        random_numbers: &mut I,
+    ) -> land2d::Land2D<T>;
+}
 
 #[cfg(test)]
 mod tests {
--- 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<Point>,
-}
-
-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));
-}
--- /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<Vec<Point>>,
+    fill_points: Vec<Point>,
+    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<T: Copy + PartialEq, I: Iterator<Item = u32>>(
+        &self,
+        parameters: LandGenerationParameters<T>,
+        random_numbers: &mut I,
+    ) -> Land2D<T> {
+        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
+    }
+}
--- 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;
     }
--- /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
--- /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<T> {
+    rnd: LaggedFibonacciPRNG,
+    generator: T
+}
+
+impl <T: LandGenerator> LandSource<T> {
+    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<u32>) -> Land2D<u32> {
+        self.generator.generate_land(parameters, &mut self.rnd)
+    }
+}
+
+fn fill_pixels(pixels: &mut [u8], land: &Land2D<u32>) {
+    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<u32>) {
+    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<T: Default + SampleUniform + Ord>(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
--- 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 <a.korotaev@hedgewars.org>"]
 
 [dependencies]
+integral-geometry = { path = "../integral-geometry" }
--- 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<T> {
     data: Vec<T>,
-    width: usize,
-    height: usize,
+    size: Size,
 }
 
 impl<T> Index<usize> for Vec2D<T> {
@@ -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<T> IndexMut<usize> for Vec2D<T> {
     #[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 <T> Vec2D<T> {
+    #[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<T: Copy> Vec2D<T> {
-    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<&<usize as SliceIndex<[T]>>::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 <usize as SliceIndex<[T]>>::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) -> &<usize as SliceIndex<[T]>>::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 <usize as SliceIndex<[T]>>::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<Item = &[T]> {
+        self.data.chunks(self.width())
     }
 }
 
@@ -77,10 +87,10 @@
 
     #[test]
     fn basics() {
-        let mut v: Vec2D<u8> = Vec2D::new(2, 3, 0xff);
+        let mut v: Vec2D<u8> = 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);