Skip to content

Instantly share code, notes, and snippets.

@Introscopia
Created June 7, 2023 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Introscopia/e32720497e3c962874e3a3bd406cfa5e to your computer and use it in GitHub Desktop.
Save Introscopia/e32720497e3c962874e3a3bd406cfa5e to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <SDL.h>
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
typedef int bool;
typedef struct{
int len, size;
SDL_Rect *rcts;
SDL_Rect original;
} rectBlob;
void rectBlob_init( rectBlob *rB, int x, int y, int w, int h ){
rB->len = 1;
rB->size = 4;
rB->original = (SDL_Rect){x,y,w,h};
rB->rcts = malloc( rB->size * sizeof(SDL_Rect) );
rB->rcts[0] = rB->original;
}
void rectBlob_append( rectBlob *rB, int x, int y, int w, int h ){
if( rB->len >= rB->size ){
rB->size *= 2;
//printf("(resizing to %d) ", rB->size );
rB->rcts = realloc( rB->rcts, rB->size * sizeof(SDL_Rect) );
}
//printf("A%d ", rB->len );
//if( w < 0 ) printf("negativa!!! ");
rB->rcts[ rB->len ] = (SDL_Rect){x,y,w,h};
rB->len += 1;
}
int rect_area( SDL_Rect *r ){
return r->w * r->h;
}
void clip_rectBlob( rectBlob *rB, SDL_Rect faca ){
int faca_r = faca.x + faca.w;
int faca_b = faca.y + faca.h;
int len_so_far = rB->len;
//printf("clipping %d rcts:", len_so_far );
for ( int i = 0; i < len_so_far; ++i ){
SDL_Rect *B = rB->rcts + i;
if( rect_area( rB->rcts + i ) <= 0 ) continue;
int rB_rcts_i_r = rB->rcts[i].x + rB->rcts[i].w;
int rB_rcts_i_b = rB->rcts[i].y + rB->rcts[i].h;
if( faca.x >= rB_rcts_i_r ||
faca.y >= rB_rcts_i_b ||
faca_r <= rB->rcts[i].x ||
faca_b <= rB->rcts[i].y ){
continue;
}
bool top_in = faca.y > rB->rcts[i].y && faca.y < rB_rcts_i_b;
bool bot_in = faca_b > rB->rcts[i].y && faca_b < rB_rcts_i_b;
bool lef_in = faca.x > rB->rcts[i].x && faca.x < rB_rcts_i_r;
bool rig_in = faca_r > rB->rcts[i].x && faca_r < rB_rcts_i_r;
int total = top_in + bot_in + lef_in + rig_in;
//printf("\n%d: %d%d%d%d = %d. ", i, top_in, bot_in, lef_in, rig_in, total );
switch( total ){
case 0:
//if( faca.x < rB->rcts[i].x && faca_r > rB_rcts_i_r &&
// faca.y < rB->rcts[i].y && faca_b > rB_rcts_i_b ){
//FULL CLIP
rB->rcts[i].w = 0;
break;
case 1:
if( top_in ){
rB->rcts[i].h = faca.y - rB->rcts[i].y;
}
else if( bot_in ){
rB->rcts[i].y = faca_b;
rB->rcts[i].h = rB_rcts_i_b - faca_b;
}
else if( lef_in ){
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( rig_in ){
rB->rcts[i].x = faca_r;
rB->rcts[i].w = rB_rcts_i_r - faca_r;
}
break;
case 2:
if( rig_in && bot_in ){//top left corner clipped
rectBlob_append( rB, rB->rcts[i].x, faca_b, faca_r - rB->rcts[i].x, rB_rcts_i_b - faca_b );
rB->rcts[i].x = faca_r;
rB->rcts[i].w = rB_rcts_i_r - faca_r;
}
else if( lef_in && bot_in ){//top right corner clipped
rectBlob_append( rB, faca.x, faca_b, rB_rcts_i_r - faca.x, rB_rcts_i_b - faca_b );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( lef_in && top_in ){//bottom right corner clipped
rectBlob_append( rB, faca.x, rB->rcts[i].y, rB_rcts_i_r - faca.x, faca.y - rB->rcts[i].y );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( rig_in && top_in ){// bottom left corner clipped
rectBlob_append( rB, rB->rcts[i].x, rB->rcts[i].y, faca_r - rB->rcts[i].x, faca.y - rB->rcts[i].y );
rB->rcts[i].x = faca_r;
rB->rcts[i].w = rB_rcts_i_r - faca_r;
}
else if( lef_in && rig_in ){// vertical slice
rectBlob_append( rB, faca_r, rB->rcts[i].y, rB_rcts_i_r - faca_r, rB->rcts[i].h );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( top_in && bot_in ){// horizontal slice
rectBlob_append( rB, rB->rcts[i].x, faca_b, rB->rcts[i].w, rB_rcts_i_b - faca_b );
rB->rcts[i].h = faca.y - rB->rcts[i].y;
}
break;
case 3:
if( rig_in && bot_in && top_in ){// Left bite
rectBlob_append( rB, rB->rcts[i].x, rB->rcts[i].y, faca_r - rB->rcts[i].x, faca.y - rB->rcts[i].y );
rectBlob_append( rB, rB->rcts[i].x, faca_b, faca_r - rB->rcts[i].x, rB_rcts_i_b - faca_b );
rB->rcts[i].x = faca_r;
rB->rcts[i].w = rB_rcts_i_r - faca_r;
}
else if( lef_in && bot_in && rig_in ){//top bite
rectBlob_append( rB, faca.x, faca_b, faca.w, rB_rcts_i_b - faca_b );
rectBlob_append( rB, faca_r, rB->rcts[i].y, rB_rcts_i_r - faca_r, rB->rcts[i].h );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( lef_in && top_in && bot_in ){//right bite
rectBlob_append( rB, faca.x, rB->rcts[i].y, rB_rcts_i_r - faca.x, faca.y - rB->rcts[i].y );
rectBlob_append( rB, faca.x, faca_b, rB_rcts_i_r - faca.x, rB_rcts_i_b - faca_b );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
else if( rig_in && top_in && lef_in ){//bottom bite
rectBlob_append( rB, faca.x, rB->rcts[i].y, faca.w, faca.y - rB->rcts[i].y );
rectBlob_append( rB, faca_r, rB->rcts[i].y, rB_rcts_i_r - faca_r, rB->rcts[i].h );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
}
break;
case 4:
// HOLE
rectBlob_append( rB, faca.x, rB->rcts[i].y, faca.w, faca.y - rB->rcts[i].y );
rectBlob_append( rB, faca.x, faca_b, faca.w, rB_rcts_i_b - faca_b );
rectBlob_append( rB, faca_r, rB->rcts[i].y, rB_rcts_i_r - faca_r, rB->rcts[i].h );
rB->rcts[i].w = faca.x - rB->rcts[i].x;
break;
}
//if( rB->rcts[i].w < 0 ) printf("Largura Negativa!\n");
}
//printf("clip done: len:%d\n\n", rB->len );
}
int main(int argc, char *argv[]){
SDL_Window *window;
SDL_Renderer *rend;
int width = 1;
int height = 1;
bool loop = 1;
int mouseX = 0, mouseY = 0;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
return 3;
}
window = SDL_CreateWindow( "rectClipper", 0, 0, 1, 1, SDL_WINDOW_MAXIMIZED | SDL_WINDOW_RESIZABLE );
rend = SDL_CreateRenderer( window, 0, 0 );
SDL_SetRenderDrawBlendMode( rend, SDL_BLENDMODE_BLEND );
SDL_GetWindowSize( window, &width, &height );
//printf("%d x %d\n", width, height );
rectBlob rB;
rectBlob_init( &rB, 0.25*width, 0.25*height, width/2, height/2 );
int ax = -1, ay = 0;
bool show_outlines = 0;
while ( loop ) {//============================================================================================================
SDL_Event event;
while( SDL_PollEvent(&event) ){
switch (event.type) {
case SDL_QUIT:
loop = 0;
break;
case SDL_KEYUP:
if( event.key.keysym.sym == ' ' ){
show_outlines = !show_outlines;
}
else{
for (int i = 0; i < rB.len; ++i ){
printf( "%d: %d, %d, %d, %d\n", i, rB.rcts[i].x, rB.rcts[i].y, rB.rcts[i].w, rB.rcts[i].h );
}
}
puts("");
break;
case SDL_MOUSEMOTION:
mouseX = event.motion.x;
mouseY = event.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
ax = event.button.x;
ay = event.button.y;
break;
case SDL_MOUSEBUTTONUP:;
SDL_Rect clip = (SDL_Rect){ min(mouseX,ax), min(mouseY,ay), abs(mouseX-ax), abs(mouseY-ay) };
clip_rectBlob( &rB, clip );
ax = -1;
break;
}
}
SDL_SetRenderDrawColor( rend, 0, 0, 0, 255 );
SDL_RenderClear( rend );
SDL_SetRenderDrawColor( rend, 80, 80, 80, 255 );
SDL_RenderDrawRect( rend, &(rB.original) );
for (int i = 0; i < rB.len; ++i ){
if( rect_area( rB.rcts + i ) <= 0 ) continue;
SDL_SetRenderDrawColor( rend, 255, 255, 255, 40 );
SDL_RenderFillRect( rend, rB.rcts + i );
if( show_outlines ){
SDL_SetRenderDrawColor( rend, 255, 255, 255, 255 );
SDL_RenderDrawRect( rend, rB.rcts + i );
}
}
if( ax >= 0 ){
SDL_SetRenderDrawColor( rend, 255, 0, 0, 255 );
SDL_RenderDrawRect( rend, &(SDL_Rect){ min(mouseX,ax), min(mouseY,ay), abs(mouseX-ax), abs(mouseY-ay) } );
}
SDL_RenderPresent(rend);
//SDL_framerateDelay( 16 );
}
exit:;
free( rB.rcts );
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
@villares
Copy link

villares commented Jun 7, 2023

# Convertendo para Python o clipador de retângulos do John @Introscopia
# https://gist.github.com/Introscopia/e32720497e3c962874e3a3bd406cfa5e
# 2023-06-06 Usando py5 (https://py5coding.org) para a demo/teste
# 2023-06-07 Acrescentei merge_rects() - tecla espaço, para tentar simplificar/unir alguns retângulos

import py5

rects = []
drag_x = drag_y = None

def setup():
    py5.size(600, 400)
    rects.append(Rect(50, 50, 500, 300))
    py5.text_align(py5.CENTER, py5.CENTER)
    
def draw():
    py5.background(0)
    py5.rect_mode(py5.CORNER)
    py5.fill(255, 200)
    py5.stroke(255)
    for i, r in enumerate(rects):
        py5.rect(r.x, r.y, r.w, r.h)
        py5.text(str(i), r.x + r.w / 2, r.y + r.h / 2)
        
    if drag_x:
        py5.rect_mode(py5.CORNERS)
        py5.stroke(255, 0, 0)
        py5.no_fill()
        py5.rect(py5.mouse_x, py5.mouse_y, drag_x, drag_y)

def mouse_pressed():
    global drag_x, drag_y
    drag_x, drag_y = py5.mouse_x, py5.mouse_y

def mouse_released():
    global drag_x, drag_y
    clip_rects(rects, Rect(min(py5.mouse_x, drag_x), min(py5.mouse_y, drag_y),
                           abs(py5.mouse_x - drag_x), abs(py5.mouse_y - drag_y)))
    drag_x = drag_y = None

def key_pressed():
    if py5.key == ' ':
        merge_rects()
    elif py5.key == 'r':
        rects[:] = [Rect(50, 50, 500, 300)]
        
class Rect:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

def merge_rects():
    
    def y_x(r):
        return r.y, r.x
    
    rects.sort(key=y_x)
    
    for i, r in enumerate(rects[:-1]):
        nr = rects[i + 1] # next rect
        if r.y == nr.y and r.h == nr.h and r.x + r.w == nr.x:
            nr.x = r.x
            nr.w += r.w
            r.w = 0  # r will be removed by clean_rects()
            
    clean_rects() # Filter out 0 area rects
            

def clip_rects(rects: list[Rect], knife: Rect) -> None:
    """
    Clipping function that will remove the knife area from the provided Rects,
    creating, resizing or removing axis aligned Rects (mutates the input).
    """
    knife_r = knife.x + knife.w  # knife's right edge x
    knife_b = knife.y + knife.h  # knife's bottom edge y

    for r in rects[:]:  # iterating over a copy of rects
        r_r = r.x + r.w  # rect's right edge x
        r_b = r.y + r.h  # rect's bottom edge y

        if (knife.x >= r_r or
            knife.y >= r_b or
            knife_r <= r.x or
            knife_b <= r.y):
            continue  # not intersecting with knife

        top_in = knife.y > r.y and knife.y < r_b
        bot_in = knife_b > r.y and knife_b < r_b
        lef_in = knife.x > r.x and knife.x < r_r
        rig_in = knife_r > r.x and knife_r < r_r

        total = top_in + bot_in + lef_in + rig_in

        if total == 0:  # FULL CLIP
            r.w = 0   # marks for removal in the end

        elif total == 1:  # RESIZE
            if top_in:
                r.h = knife.y - r.y
            elif bot_in:
                r.y, r.h = knife_b, r_b - knife_b
            elif lef_in:
                r.w = knife.x - r.x
            elif rig_in:
                r.x, r.w = knife_r, r_r - knife_r

        elif total == 2: # CORNERS AND SLICE THROUGH
            if rig_in and bot_in:  # top left corner clipped
                rects.append(Rect(r.x, knife_b, knife_r - r.x, r_b - knife_b))
                r.x, r.w = knife_r, r_r - knife_r
            elif lef_in and bot_in:  # top right corner clipped
                rects.append(Rect(knife.x, knife_b, r_r - knife.x, r_b - knife_b))
                r.w = knife.x - r.x
            elif lef_in and top_in:  # bottom right corner clipped
                rects.append(Rect(knife.x, r.y, r_r - knife.x, knife.y - r.y))
                r.w = knife.x - r.x
            elif rig_in and top_in:  # bottom left corner clipped
                rects.append(Rect(r.x, r.y, knife_r - r.x, knife.y - r.y))
                r.x, r.w = knife_r, r_r - knife_r
            elif lef_in and rig_in:  # vertical slice
                rects.append(Rect(knife_r, r.y, r_r - knife_r, r.h))
                r.w = knife.x - r.x
            elif top_in and bot_in:  # horizontal slice
                rects.append(Rect(r.x, knife_b, r.w, r_b - knife_b))
                r.h = knife.y - r.y

        elif total == 3:  # BITES
            if rig_in and bot_in and top_in:  # Left bite
                rects.append(Rect(r.x, r.y, knife_r - r.x, knife.y - r.y))
                rects.append(Rect(r.x, knife_b, knife_r - r.x, r_b - knife_b))
                r.x, r.w = knife_r, r_r - knife_r
            elif lef_in and bot_in and rig_in:  # top bite
                rects.append(Rect(knife.x, knife_b,  knife.w, r_b - knife_b))
                rects.append(Rect(knife_r, r.y, r_r - knife_r, r.h))
                r.w = knife.x - r.x
            elif lef_in and top_in and bot_in:  # right bite
                rects.append(Rect(knife.x, r.y, r_r - knife.x, knife.y - r.y))
                rects.append(Rect(knife.x, knife_b, r_r - knife.x, r_b - knife_b))
                r.w = knife.x - r.x
            elif rig_in and top_in and lef_in:  # bottom bite
                rects.append(Rect(knife.x, r.y, knife.w, knife.y - r.y))
                rects.append(Rect(knife_r, r.y, r_r - knife_r, r.h))
                r.w = knife.x - r.x

        elif total == 4: # HOLE
            rects.append(Rect(knife.x, r.y, knife.w, knife.y - r.y))
            rects.append(Rect(knife.x, knife_b,  knife.w, r_b - knife_b))
            rects.append(Rect(knife_r, r.y, r_r - knife_r, r.h))
            r.w = knife.x - r.x
            
    clean_rects() # Filter out 0 area rects

def clean_rects():
    rects[:] = [r for r in rects if r.w * r.h != 0]


py5.run_sketch()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment