Help: how to get a non rectangular region of a surface?

Started by froggy77, July 10, 2016, 06:49:16 PM

Previous topic - Next topic
Hello,

Is it possible to get the opposite region of several regions in a surface?
In this example, there are several yellow rectangles and I would like to get the black part.


technically those are still rectangles. You could get them in the same way you would get the yellow parts, but just imagining where the connecting lines would be between them.
Draw the biggest black rectangle you can find in the non-yellow space, and where there's an oddly shaped corner, fill it in with another separate rectangle.

Also, if you're trying to paint in the empty space, you can draw on the surface before you draw whatever goes in the yellow, and draw the yellow spaces afterward, and whatever's in the yellow should be drawn over whatever you filled the surface with before...

At least that's how I understand surfaces. (Similar with on_pre_draw and on_post_draw for sprites)

Like this:


EDIT:
If you're doing a 'lighting' script, where yellow spaces would be lights you want to 'cut out' of the blackness, I might have to rethink how to do that.. its something I've been wanting to try too.
Still, if you could define your yellow blocks before they're drawn... there should be a way to replace the black area with a transparent space... by specifying an shape made of some color like 0xFFFFFF00, which is pure white, but with 0 alpha opacity...

Maybe that helps maybe not. Get back to us if you come up with something that does, I'd like to see it.
Patience is difficult and rarely thanked, but always appreciated, and sorely missed when absent.

Quotetechnically those are still rectangles. You could get them in the same way you would get the yellow parts, but just imagining where the connecting lines would be between them.
Draw the biggest black rectangle you can find in the non-yellow space, and where there's an oddly shaped corner, fill it in with another separate rectangle.
==> I had the same idea but for now , I have a little trouble to think the script in its entirety.

QuoteAlso, if you're trying to paint in the empty space, you can draw on the surface before you draw whatever goes in the yellow, and draw the yellow spaces afterward, and whatever's in the yellow should be drawn over whatever you filled the surface with before...
==> My first idea is to use dynamic tiles for lights in the editor and then add black rectangles by a script. If I work with the black parts from the editor , I move mistakenly tiles ; Moreover, the mapping is more difficult and less flexible. I move anything !  >:(

QuoteIf you're doing a 'lighting' script, where yellow spaces would be lights you want to 'cut out' of the blackness, I might have to rethink how to do that.. its something I've been wanting to try too.
==> Yes I'm trying to make a lighting script for example to light/extinguish a candle on a table.

QuoteStill, if you could define your yellow blocks before they're drawn... there should be a way to replace the black area with a transparent space... by specifying an shape made of some color like 0xFFFFFF00, which is pure white, but with 0 alpha opacity...
==> It is a very good idea if it is possible with the engine. I'm going to test this.  :)

QuoteMaybe that helps maybe not. Get back to us if you come up with something that does, I'd like to see it.
Thank you for taking the time to answer me. Ok

In hindsight, it seems not to be possible to replace a fully opaque pixel with a transparent one...
I'm assuming this is a consequence of SDL_BLENDMODE_BLEND in the source. This is an occasion where SDL_BLENDMODE_NONE would allow for more freedome, as you could use it to cut a section of the surface out and replace it completely (no blending) with the new section you drew...

if I understood the whole C++/lua interface thing I'd try my hand at making an unofficial branch with the feature, but alas, I don't get the C++ end at all. It'll be up to Christopho to add the lua function, whenever or if ever he does that.

A different option is required until then... I tried making a big lua table of filled and empty 'darkness' tiles and looping through them each frame, drawing them into the surface... which... kind of worked... but not well... and also caused a lot of lag... I'm still hopeful there is a method that will work... but I get the feeling it'll require some heady math.

If I could figure out how to graph 'everything but these 5 rectangles' as a limit... that might work... but would be speed dependant...
I'll let you know if I come up with any more ideas you can try.
Patience is difficult and rarely thanked, but always appreciated, and sorely missed when absent.


Thanks Christopho, but I continue with my first idea. Obviously, I will also use your new feature.
I manage to get this table that will fill the empty areas. For now, it's just a LUA function that I must adapt to Solarus .  ;)

LUA FUNCTION:

Code (LUA) Select
-- TEST -- WIP --

-- Function to initialize a table which will contain values (x, y , width, height) in order to draw the opposite rectangles to 'tbl' to fill a surface.
-- 'tbl' is not the new table, but the table which contains values.

local function init_opposite_table(tbl)

    -- Length of the original table
    local length = #tbl
   
    -- Clone this table in a temporary table
    local t_temp = {}
    k, v = next(tbl, nil)
    while k do
        t_temp[k] = v
        k, v = next(tbl,k)
    end
   
    -- Then, add x and y of opposite corners in this new table
    for i = 1, length do
        local l = #t_temp + 1
        t_temp[l] = {}
        t_temp[l][1] = t_temp[i][1] + t_temp[i][3]
        t_temp[l][2] = t_temp[i][2] + t_temp[i][4]
    end
   
    -- Function to initialize a simple table (for example for t_x and t_y)
    -- 'tbl' is not the new table, but the table which contains values.
    -- TODO: replace 320 and 240 by sol.video.get_window_size() in Solarus
    local function init_simple_table(tbl, key, min, max)
   
        -- Create a temporary table
        local t = {}
        -- Insert minimun and maximum values
        if min ~= nil then
            t[#t + 1] = min
          else
              t[#t + 1] = 0
        end
        if max ~= nil then
            t[#t + 1] = max
        else
            if key == 1 then
                max = 320
            elseif key == 2 then
                max =    240
            end
            t[#t + 1] = max
        end
        -- Add entries in the table
        for k in pairs(tbl) do
            t[#t + 1] = tbl[k][key]
        end
        -- Create another temporary table
        -- and delete duplicate entries
        local t_no_dup = {}; 
        for _, v in pairs(t) do
            t_no_dup[v] = v
        end
        -- Clear the first temporary table
        local t = {}
        -- Sort
        for k in pairs(t_no_dup) do
            t[#t + 1] = k
        end
        table.sort(t)
        -- Return the result
        return t
    end
   
    -- Initialize a table for x and a table for y
    local t_x = init_simple_table(t_temp, 1)
    local t_y = init_simple_table(t_temp, 2)
   
    -- Max values for x and y
    local vx_max = math.max(unpack(t_x))
    local vy_max = math.max(unpack(t_y))
   
    -- Create a temporary table
    -- Merge rectangles that are on the same line (the width is changed) when possible.
    local t_temp = {}
    for ky, vy in pairs(t_y) do
        if vy < vy_max then
            local x
            local y
            local width
            local height = t_y[ky + 1] - vy
            for kx, vx in pairs(t_x) do
                if vx < vx_max then
                    for i = 1, length do
                        if x == nil then
                            x = vx
                            y = vy
                        end
                        if vx == tbl[i][1] and vy >= tbl[i][2] and vy < (tbl[i][2] + tbl[i][4])  then
                            width = vx - x
                            if width > 0 then
                                local l = #t_temp + 1
                                t_temp[l] = {x, y , width, height, ky}
                            end                           
                            x = (tbl[i][1] + tbl[i][3])
                        end
                    end
                else
                    width = vx - x
                    local l = #t_temp + 1
                    t_temp[l] = {x, y , width, height, ky}
                end
            end
        end
    end
   
    -- Create a table from the previous temporary table
    -- Merge the rectangles which have the same x and the same width (the height is changed) when possible
    local opp_tbl = {}
    local max_n = #t_temp
    local count = 0
    for j in pairs(t_temp) do
        local height = t_temp[j][4]
        local ky = t_temp[j][5]
        local var = 1
        if     t_temp[j][5] ~= nil then
            for n = j, max_n do
                if t_temp[j][1] == t_temp[n][1] and t_temp[j][3] == t_temp[n][3] and ky ~= t_temp[n][5] then
                    if (ky + var) == t_temp[n][5] then
                        t_temp[n][5] = nil
                        var = var + 1
                        height = height + t_temp[n][4]
                    end
                end
            end
            t_temp[j][5] = nil
            count = count + 1
            opp_tbl[count] = t_temp[j]
            opp_tbl[count][4] = height
        end
    end
    return opp_tbl
end


-- Original table
local t_lights = {{108, 46, 34, 19}, {19, 212, 85, 28}, {174, 4, 86, 54}, {44, 32, 53, 166}}

-- Initialize the opposite table
local t_nolight = init_opposite_table(t_lights)




PRINT:

Code (LUA) Select

-- Print tables to test the results
print("-------- t_lights --------")
print("x", "y","w","h")
print("---------------------------")
t = t_lights
for k in pairs(t) do
    print(t[k][1], t[k][2], t[k][3], t[k][4])
end
print("-------------")
print("rectangles " .. #t_lights)
print("-------------")
print("-------- t_nolight --------")
print("x", "y","w","h")
print("---------------------------")
t = t_nolight
for k in pairs(t) do
    print(t[k][1], t[k][2], t[k][3], t[k][4])
end
print("-------------")
print("rectangles " .. #t_nolight)
print("-------------")
print("TOTAL " .. #t_lights + #t_nolight)
print("---------------------------")



RESULTS:

-------- t_lights --------
x    y    w    h
---------------------------
108    46    34    19
19    212    85    28
174    4    86    54
44    32    53    166
-------------
rectangles 4
-------------
-------- t_nolight --------
x    y    w    h
---------------------------
0    0    320    4
0    4    174    28
260    4    60    54
0    32    44    166
97    32    77    14
97    46    11    19
142    46    32    12
142    58    178    7
97    65    223    133
0    198    320    14
0    212    19    28
104    212    216    28
-------------
rectangles 12
-------------
TOTAL 16
---------------------------

Here the result in picture:

       
  • yellow rectangles = table named "t_lights" (4 rectangles)
  • black rectangles = table named "t_nolight" (12 rectangles)



Here the result if I had not optimized the function. There would be too many rectangles... :o


Work still in progress with a few headache especially after a few months without making.

It should be easier with surface blending now, you just need a bitmap and 2 surfaces: one modulate (for the darkness, tone, whatever), and one "blend" (where you draw the light effect)