Thread overview
Procedural drawing using ndslice
Feb 11, 2016
Claude
Feb 11, 2016
John Colvin
Feb 12, 2016
Ali Çehreli
Feb 12, 2016
Claude
February 11, 2016
Hello,

I come from the C world and try to do some procedural terrain generation, and I thought ndslice would help me to make things look clean, but I'm very new to those semantics and I need help.

Here's my problem: I have a C-style rough implementation of a function drawing a disk into a 2D buffer. Here it is:


import std.math;
import std.stdio;

void draw(ref float[16][16] buf, int x0, int y0, int x1, int y1)
{
    float xc = cast(float)(x0 + x1) / 2;
    float yc = cast(float)(y0 + y1) / 2;
    float xr = cast(float)(x1 - x0) / 2;
    float yr = cast(float)(y1 - y0) / 2;

    float disk(size_t x, size_t y)
    {
        float xx, yy;
        xx = (x - xc) / xr;
        yy = (y - yc) / yr;
        return 1.0 - sqrt(xx * xx + yy * yy);
    }

    for (int y = 0; y < 16; y++)
    {
        for (int x = 0; x < 16; x++)
        {
            buf[x][y] = disk(x, y);
            writef(" % 3.1f", buf[x][y]);
        }
        writeln("");
    }
}

void main()
{
    float[16][16] buf;

    draw(buf, 2, 2, 10, 10);
}


The final buffer contains values where positive floats are the inside of the disk, negative are outside, and 0's represents the perimeter of the disk.

I would like to simplify the code of draw() to make it look more something like:

Slice!(stuff) draw(int x0, int y0, int x1, int y1)
{
    float disk(size_t x, size_t y)
    {
        // ...same as above
    }

    return Slice!stuff.something!disk.somethingElseMaybe;
}

Is it possible?

Do I need to back-up the slice with an array, or could the slice be used lazily and modified as I want using some other drawing functions.

auto diskNoiseSlice = diskSlice.something!AddNoiseFunction;

... until I do a:

auto buf = mySlice.array;

... where the buffer would be allocated in memory and filled with the values according to all the drawing primitives I used on the slice.
February 11, 2016
On Thursday, 11 February 2016 at 13:05:41 UTC, Claude wrote:
> Hello,
>
> I come from the C world and try to do some procedural terrain generation, and I thought ndslice would help me to make things look clean, but I'm very new to those semantics and I need help.
>
> Here's my problem: I have a C-style rough implementation of a function drawing a disk into a 2D buffer. Here it is:
>
>
> import std.math;
> import std.stdio;
>
> void draw(ref float[16][16] buf, int x0, int y0, int x1, int y1)
> {
>     float xc = cast(float)(x0 + x1) / 2;
>     float yc = cast(float)(y0 + y1) / 2;
>     float xr = cast(float)(x1 - x0) / 2;
>     float yr = cast(float)(y1 - y0) / 2;
>
>     float disk(size_t x, size_t y)
>     {
>         float xx, yy;
>         xx = (x - xc) / xr;
>         yy = (y - yc) / yr;
>         return 1.0 - sqrt(xx * xx + yy * yy);
>     }
>
>     for (int y = 0; y < 16; y++)
>     {
>         for (int x = 0; x < 16; x++)
>         {
>             buf[x][y] = disk(x, y);
>             writef(" % 3.1f", buf[x][y]);
>         }
>         writeln("");
>     }
> }
>
> void main()
> {
>     float[16][16] buf;
>
>     draw(buf, 2, 2, 10, 10);
> }
>
>
> The final buffer contains values where positive floats are the inside of the disk, negative are outside, and 0's represents the perimeter of the disk.
>
> I would like to simplify the code of draw() to make it look more something like:
>
> Slice!(stuff) draw(int x0, int y0, int x1, int y1)
> {
>     float disk(size_t x, size_t y)
>     {
>         // ...same as above
>     }
>
>     return Slice!stuff.something!disk.somethingElseMaybe;
> }
>
> Is it possible?
>
> Do I need to back-up the slice with an array, or could the slice be used lazily and modified as I want using some other drawing functions.
>
> auto diskNoiseSlice = diskSlice.something!AddNoiseFunction;
>
> ... until I do a:
>
> auto buf = mySlice.array;
>
> ... where the buffer would be allocated in memory and filled with the values according to all the drawing primitives I used on the slice.

I had a go at trying the sort of thing you are talking about: http://dpaste.dzfl.pl/8f9da4f4cc34

That won't work with std.experimental.ndslice in 2.070.0, so either use dmd git master or use the latest version of ndslice in mir (https://github.com/DlangScience/mir).
February 11, 2016
On 02/11/2016 05:05 AM, Claude wrote:
> Hello,
>
> I come from the C world and try to do some procedural terrain
> generation, and I thought ndslice would help me to make things look
> clean, but I'm very new to those semantics and I need help.
>
> Here's my problem: I have a C-style rough implementation of a function
> drawing a disk into a 2D buffer. Here it is:
>
>
> import std.math;
> import std.stdio;
>
> void draw(ref float[16][16] buf, int x0, int y0, int x1, int y1)
> {
>      float xc = cast(float)(x0 + x1) / 2;
>      float yc = cast(float)(y0 + y1) / 2;
>      float xr = cast(float)(x1 - x0) / 2;
>      float yr = cast(float)(y1 - y0) / 2;
>
>      float disk(size_t x, size_t y)
>      {
>          float xx, yy;
>          xx = (x - xc) / xr;
>          yy = (y - yc) / yr;
>          return 1.0 - sqrt(xx * xx + yy * yy);
>      }
>
>      for (int y = 0; y < 16; y++)
>      {
>          for (int x = 0; x < 16; x++)
>          {
>              buf[x][y] = disk(x, y);
>              writef(" % 3.1f", buf[x][y]);
>          }
>          writeln("");
>      }
> }
>
> void main()
> {
>      float[16][16] buf;
>
>      draw(buf, 2, 2, 10, 10);
> }
>
>
> The final buffer contains values where positive floats are the inside of
> the disk, negative are outside, and 0's represents the perimeter of the
> disk.
>
> I would like to simplify the code of draw() to make it look more
> something like:
>
> Slice!(stuff) draw(int x0, int y0, int x1, int y1)
> {
>      float disk(size_t x, size_t y)
>      {
>          // ...same as above
>      }
>
>      return Slice!stuff.something!disk.somethingElseMaybe;
> }
>
> Is it possible?
>
> Do I need to back-up the slice with an array, or could the slice be used
> lazily and modified as I want using some other drawing functions.
>
> auto diskNoiseSlice = diskSlice.something!AddNoiseFunction;
>
> ... until I do a:
>
> auto buf = mySlice.array;
>
> ... where the buffer would be allocated in memory and filled with the
> values according to all the drawing primitives I used on the slice.

Here is a very very rough proof of concept:

import std.stdio;

struct LazyLine(Element, int Length, alias func) {
    int y;

    Element opIndex(int x) {
        return func(y, x);
    }
}

struct LazyRect(Element, int Height, int Width, alias func) {

    Element opIndex(A...)(A args) {
        const y = args[0];
        const x = args[1];
        auto line = LazyLine!(Element, Width, func)(y);

        return line[x];
    }

    auto chain(alias nextFunc)() {
        return NextLazyRect!(Element, Height, Width, nextFunc,
                             typeof(this))(this);
    }
}

struct NextLazyRect(Element, int Height, int Width, alias func,
                    PreviousLazyRect) {
    PreviousLazyRect previous;

    Element opIndex(A...)(A args) {
        const y = args[0];
        const x = args[1];
        return func(previous[y, x]);
    }
}

auto abc(int line, int row) {
    return line * 10 + row;
}

auto xyz(float f) {
    return f / 10;
}

void main() {
    auto rect = LazyRect!(float, 3, 5, abc)().chain!xyz;

    foreach (line; 0 .. 5) {
        foreach (row; 0 .. 4) {
            writef("%6.1f", rect[line, row]);
        }
        writeln();
    }
}

Output:

   0.0   0.1   0.2   0.3
   1.0   1.1   1.2   1.3
   2.0   2.1   2.2   2.3
   3.0   3.1   3.2   3.3
   4.0   4.1   4.2   4.3

Ali

February 12, 2016
Thanks for your replies, John and Ali. I wasn't sure I was clear.

I'm going to try to see if I can fit Ali concept (totally lazy, which is what I was looking for) within ndslices, so that I can also use it in 3D and apply window() function to the result and mess around with it.