Adding C projects and flood-fill.py
This commit is contained in:
parent
80e4179448
commit
633ddb73c1
15
Makefile
Normal file
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
CFLAGS=-g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -pedantic -O0
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
.PHONY: build clean run all
|
||||||
|
|
||||||
|
all: base floodfill
|
||||||
|
|
||||||
|
base: base.c ./lib/libraylib.a
|
||||||
|
$(CC) $(CFLAGS) -Llib/ -Iinclude/ -lm base.c -o base ./lib/libraylib.a
|
||||||
|
|
||||||
|
floodfill: floodfill.c ./lib/libraylib.a
|
||||||
|
$(CC) $(CFLAGS) -Llib/ -Iinclude/ -lm floodfill.c -o floodfill ./lib/libraylib.a
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -vf *.so *.o floodfill base
|
66
base.c
Normal file
66
base.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "include/raylib.h"
|
||||||
|
#include "raymath.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 1300
|
||||||
|
#define SCREEN_HEIGHT 1000
|
||||||
|
#define TARGET_FPS 60
|
||||||
|
|
||||||
|
typedef struct GameState {
|
||||||
|
float dt;
|
||||||
|
float time_elapsed;
|
||||||
|
int frame_count;
|
||||||
|
Camera3D cam3d;
|
||||||
|
} GameState;
|
||||||
|
|
||||||
|
|
||||||
|
GameState *Init() {
|
||||||
|
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Base");
|
||||||
|
SetTargetFPS(TARGET_FPS);
|
||||||
|
|
||||||
|
GameState state = {0};
|
||||||
|
|
||||||
|
GameState *state_ptr = RL_MALLOC(sizeof(GameState));
|
||||||
|
memcpy(state_ptr, &state, sizeof(GameState));
|
||||||
|
return state_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input(GameState *S) { (void)S; }
|
||||||
|
void Update(GameState *S) { (void)S; }
|
||||||
|
|
||||||
|
void Draw3D(const GameState *S) { (void)S; }
|
||||||
|
void Draw2D(const GameState *S) { (void)S; }
|
||||||
|
void Draw2DDebug(GameState *S) { (void)S; }
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
GameState *state = Init();
|
||||||
|
while (!WindowShouldClose()) {
|
||||||
|
state->dt = GetFrameTime();
|
||||||
|
state->time_elapsed = GetTime();
|
||||||
|
|
||||||
|
Input(state);
|
||||||
|
|
||||||
|
Update(state);
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
{
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
BeginMode3D(state->cam3d);
|
||||||
|
{
|
||||||
|
Draw3D(state);
|
||||||
|
}
|
||||||
|
EndMode3D();
|
||||||
|
|
||||||
|
Draw2D(state);
|
||||||
|
Draw2DDebug(state);
|
||||||
|
}
|
||||||
|
EndDrawing();
|
||||||
|
state->frame_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
free(state);
|
||||||
|
}
|
76
flood-fill.py
Normal file
76
flood-fill.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import pyray as RL
|
||||||
|
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D, BoundingBox)
|
||||||
|
import math
|
||||||
|
import pdb
|
||||||
|
import random
|
||||||
|
from typing import Optional, Tuple, List
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
def dump(struct):
|
||||||
|
s = f"{RL.ffi.typeof(struct)}: (".replace('<ctype ', '').replace('>', '')
|
||||||
|
for field in dir(struct):
|
||||||
|
data = struct.__getattribute__(field)
|
||||||
|
if str(data).startswith("<cdata"):
|
||||||
|
data = dump(data)
|
||||||
|
s += f"{field}:{data} "
|
||||||
|
s += ")"
|
||||||
|
return s
|
||||||
|
|
||||||
|
screen_width = 900
|
||||||
|
screen_height = 900
|
||||||
|
max_radius = 25
|
||||||
|
paint_tank_max = 250
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class World:
|
||||||
|
buffer: RL.Image
|
||||||
|
texture: RL.Texture
|
||||||
|
player_pos = Vec2(10, 10)
|
||||||
|
paint_float: int = 0
|
||||||
|
frame_count: int = 0
|
||||||
|
|
||||||
|
def init() -> World:
|
||||||
|
buf = RL.gen_image_color(screen_width, screen_height, RL.BLACK)
|
||||||
|
RL.image_draw_circle(buf, 0, 100, 100, RL.RED)
|
||||||
|
texture = RL.load_texture_from_image(buf)
|
||||||
|
return World(buf, texture)
|
||||||
|
|
||||||
|
# def flood_fill(w: World):
|
||||||
|
|
||||||
|
def player_input(w: World):
|
||||||
|
if RL.is_mouse_button_pressed(0):
|
||||||
|
w.paint_tank = paint_tank_max
|
||||||
|
if RL.is_mouse_button_down(0):
|
||||||
|
mouse_pos = RL.get_mouse_position()
|
||||||
|
RL.begin_texture_mode(w.render_tx);
|
||||||
|
mdt = RL.get_mouse_delta()
|
||||||
|
mag = RL.vector2_length(mdt)
|
||||||
|
if mag > 0 and w.paint_tank > 0:
|
||||||
|
w.paint_tank -= mag
|
||||||
|
r = w.paint_tank // 10
|
||||||
|
RL.draw_circle_v(mouse_pos, r, RL.RED);
|
||||||
|
RL.end_texture_mode();
|
||||||
|
|
||||||
|
def update(w: World):
|
||||||
|
RL.update_texture(w.texture, w.buffer.data)
|
||||||
|
|
||||||
|
def draw_2d(w: World):
|
||||||
|
RL.draw_texture_rec(w.texture, Rect(0, 0, screen_width, screen_height), Vec2(0, 0), RL.WHITE);
|
||||||
|
|
||||||
|
RL.init_window(screen_width, screen_height, "Starter");
|
||||||
|
RL.set_target_fps(60)
|
||||||
|
|
||||||
|
w = init()
|
||||||
|
while not RL.window_should_close():
|
||||||
|
# player_input(w)
|
||||||
|
update(w)
|
||||||
|
|
||||||
|
# Drawing
|
||||||
|
RL.begin_drawing()
|
||||||
|
RL.clear_background(RL.WHITE)
|
||||||
|
|
||||||
|
draw_2d(w)
|
||||||
|
|
||||||
|
RL.end_drawing()
|
||||||
|
w.frame_count += 1
|
||||||
|
|
236
floodfill.c
Normal file
236
floodfill.c
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "include/raylib.h"
|
||||||
|
#include "raymath.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 1000
|
||||||
|
#define SCREEN_HEIGHT 800
|
||||||
|
#define IMG_W SCREEN_WIDTH
|
||||||
|
#define IMG_H SCREEN_HEIGHT
|
||||||
|
#define IMG_SIZE SCREEN_WIDTH * SCREEN_HEIGHT
|
||||||
|
#define TARGET_FPS 120
|
||||||
|
|
||||||
|
const int maxRadius = 25;
|
||||||
|
const int paintTankMax = 250;
|
||||||
|
const Color playerColor = { 0, 121, 241, 255 };
|
||||||
|
|
||||||
|
typedef struct Pixel {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} Pixel;
|
||||||
|
|
||||||
|
typedef struct GameState {
|
||||||
|
float dt;
|
||||||
|
float time_elapsed;
|
||||||
|
int frame_count;
|
||||||
|
Texture displayTexture;
|
||||||
|
Image imageBuffer;
|
||||||
|
int paintTank;
|
||||||
|
bool joystickActive;
|
||||||
|
Vector2 joystickStartPos;
|
||||||
|
Vector2 playerPos;
|
||||||
|
Vector2 playerDir;
|
||||||
|
Color colorUnderPlayer;
|
||||||
|
int *bfsBuffer;
|
||||||
|
Pixel *queue;
|
||||||
|
int q_head;
|
||||||
|
int q_tail;
|
||||||
|
Pixel *fill;
|
||||||
|
int fillCount;
|
||||||
|
} GameState;
|
||||||
|
|
||||||
|
int PixelToIndex(Pixel p) {return p.y * SCREEN_WIDTH + p.x;}
|
||||||
|
Pixel IndexToPixel(int idx) {return (Pixel) {idx % SCREEN_WIDTH, (int)(idx / SCREEN_WIDTH) }; }
|
||||||
|
|
||||||
|
GameState *Init() {
|
||||||
|
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Flood Fill");
|
||||||
|
SetTargetFPS(TARGET_FPS);
|
||||||
|
|
||||||
|
Image imageBuffer = GenImageColor(IMG_W, IMG_H, WHITE);
|
||||||
|
GameState state = {
|
||||||
|
.imageBuffer = imageBuffer,
|
||||||
|
.displayTexture = LoadTextureFromImage(imageBuffer),
|
||||||
|
.playerPos = (Vector2){40, 40},
|
||||||
|
.paintTank = paintTankMax,
|
||||||
|
.colorUnderPlayer = BLUE,
|
||||||
|
.bfsBuffer = RL_CALLOC(IMG_SIZE, sizeof(int)),
|
||||||
|
.queue = RL_CALLOC(IMG_SIZE, sizeof(Pixel)),
|
||||||
|
.fill = RL_CALLOC(IMG_SIZE, sizeof(Pixel)),
|
||||||
|
};
|
||||||
|
|
||||||
|
int r = 150;
|
||||||
|
ImageDrawCircle(&state.imageBuffer, 0, 0, r, playerColor);
|
||||||
|
ImageDrawCircle(&state.imageBuffer, IMG_W, 0, r, GREEN);
|
||||||
|
ImageDrawCircle(&state.imageBuffer, 0, IMG_H, r, VIOLET);
|
||||||
|
ImageDrawCircle(&state.imageBuffer, IMG_W, IMG_H, r, ORANGE);
|
||||||
|
UpdateTexture(state.displayTexture, state.imageBuffer.data);
|
||||||
|
|
||||||
|
GameState *state_ptr = RL_MALLOC(sizeof(GameState));
|
||||||
|
memcpy(state_ptr, &state, sizeof(GameState));
|
||||||
|
return state_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input(GameState *S) {
|
||||||
|
if (IsMouseButtonPressed(0)) {
|
||||||
|
S->joystickStartPos = GetMousePosition();
|
||||||
|
S->joystickActive = true;
|
||||||
|
}
|
||||||
|
if (IsMouseButtonReleased(0)) {
|
||||||
|
S->joystickActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(GameState *S) {
|
||||||
|
bool dirty = false;
|
||||||
|
if (S->joystickActive) {
|
||||||
|
Vector2 dist = Vector2Subtract(GetMousePosition(), S->joystickStartPos);
|
||||||
|
Vector2 joystickDelta = Vector2Normalize(dist);
|
||||||
|
Vector2 dir = Vector2Normalize(joystickDelta);
|
||||||
|
S->playerDir = Vector2Lerp(S->playerDir, dir, S->dt * 5.0f);
|
||||||
|
S->playerPos = Vector2Add(S->playerPos, Vector2Scale(S->playerDir, 180.0f * S->dt));
|
||||||
|
Vector2 minBounds = (Vector2){25.0f, 25.0f};
|
||||||
|
Vector2 maxBounds = (Vector2){SCREEN_WIDTH - 25.0f, SCREEN_HEIGHT - 25.0f};
|
||||||
|
S->playerPos = Vector2Clamp(S->playerPos, minBounds, maxBounds);
|
||||||
|
|
||||||
|
float r = (float)S->paintTank / 10.0f;
|
||||||
|
|
||||||
|
dist = Vector2Scale(S->playerDir, r + 1);
|
||||||
|
Vector2 offset = Vector2Add(S->playerPos, dist);
|
||||||
|
S->colorUnderPlayer = GetImageColor(S->imageBuffer, offset.x, offset.y);
|
||||||
|
|
||||||
|
float mag = Vector2Length(joystickDelta);
|
||||||
|
if (mag > 0.0f) {
|
||||||
|
bool sameColor = ColorIsEqual(S->colorUnderPlayer, playerColor);
|
||||||
|
if (sameColor && S->paintTank < paintTankMax) {
|
||||||
|
S->paintTank += 4;
|
||||||
|
S->paintTank = S->paintTank > paintTankMax ? paintTankMax : S->paintTank;
|
||||||
|
} else {
|
||||||
|
S->paintTank -= 4;
|
||||||
|
S->paintTank = S->paintTank < 0 ? 0 : S->paintTank;
|
||||||
|
}
|
||||||
|
if (S->paintTank > 0 && !sameColor) {
|
||||||
|
ImageDrawCircleV(&S->imageBuffer, S->playerPos, r, BLUE);
|
||||||
|
UpdateTexture(S->displayTexture, S->imageBuffer.data);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < IMG_SIZE; i++) {
|
||||||
|
Pixel pixel = IndexToPixel(i);
|
||||||
|
Color color = GetImageColor(S->imageBuffer, pixel.x, pixel.y);
|
||||||
|
bool sameColor = ColorIsEqual(color, playerColor);
|
||||||
|
if (S->bfsBuffer[i] != 0 || sameColor) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
S->queue[0] = pixel;
|
||||||
|
S->q_tail++;
|
||||||
|
bool edgeDetected = false;
|
||||||
|
while (S->q_head != S->q_tail) {
|
||||||
|
Pixel pixel = S->queue[S->q_head++];
|
||||||
|
int idx = PixelToIndex(pixel);
|
||||||
|
|
||||||
|
int left = pixel.y * IMG_W - idx - 1 >= 0 ? idx - 1 : -1;
|
||||||
|
int right = pixel.y * IMG_W - idx + 1 < IMG_W ? idx + 1 : IMG_W;
|
||||||
|
int top = idx - IMG_W;
|
||||||
|
int bottom = idx + IMG_W;
|
||||||
|
int directions[] = { left, right, top, bottom };
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int j = directions[i];
|
||||||
|
if (j >= 0 && j < IMG_W) {
|
||||||
|
Pixel pixel = IndexToPixel(j);
|
||||||
|
Color c = GetImageColor(S->imageBuffer, pixel.x, pixel.y);
|
||||||
|
if (S->bfsBuffer[j] == 0 && !ColorIsEqual(playerColor, c)) {
|
||||||
|
S->queue[S->q_tail++] = pixel;
|
||||||
|
} else if (S->bfsBuffer[j] == 0) {
|
||||||
|
S->bfsBuffer[j] = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edgeDetected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
S->bfsBuffer[idx] = 1;
|
||||||
|
if (!edgeDetected) {
|
||||||
|
S->fill[S->fillCount++] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fill that sucker
|
||||||
|
if (!edgeDetected) {
|
||||||
|
for (int i = 0; i < S->fillCount; i++) {
|
||||||
|
Pixel p = S->fill[i];
|
||||||
|
ImageDrawPixel(&S->imageBuffer, p.x, p.y, playerColor);
|
||||||
|
}
|
||||||
|
UpdateTexture(S->displayTexture, S->imageBuffer.data);
|
||||||
|
}
|
||||||
|
S->fillCount = 0;
|
||||||
|
S->q_head = S->q_tail = 0;
|
||||||
|
}
|
||||||
|
memset(S->bfsBuffer, 0, IMG_SIZE * sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw2D(const GameState *S) {
|
||||||
|
DrawTexture(S->displayTexture, 0, 0, WHITE);
|
||||||
|
DrawCircleV(S->playerPos, 20, BLACK);
|
||||||
|
Vector2 lookPos = Vector2Scale(S->playerDir, 50.0f);
|
||||||
|
lookPos = Vector2Add(S->playerPos, lookPos);
|
||||||
|
DrawLineV(S->playerPos, lookPos, RED);
|
||||||
|
int barWidth = 50, barHeight = 12;
|
||||||
|
float perc = S->paintTank / (float)paintTankMax;
|
||||||
|
float barX = S->playerPos.x - barWidth / 2;
|
||||||
|
float barY = S->playerPos.y + 10;
|
||||||
|
Rectangle barBGRect = {barX, barY, barWidth, barHeight};
|
||||||
|
Rectangle barFGRect = {barX, barY, barWidth * perc, barHeight};
|
||||||
|
DrawRectangleRec(barBGRect, GRAY);
|
||||||
|
DrawRectangleRec(barFGRect, RED);
|
||||||
|
|
||||||
|
if (S->joystickActive) {
|
||||||
|
Color grayish = {200, 200, 200, 100};
|
||||||
|
DrawRing(S->joystickStartPos, 60, 80, 0.0f, 360.0f, 50, grayish);
|
||||||
|
Vector2 mousePos = GetMousePosition();
|
||||||
|
Vector2 padDir = Vector2Normalize(Vector2Subtract(mousePos, S->joystickStartPos));
|
||||||
|
Vector2 offsetPos = Vector2Add(S->joystickStartPos, Vector2Scale(padDir, 30.0f));
|
||||||
|
Vector2 clampedPos = Vector2Clamp(mousePos, S->joystickStartPos, offsetPos);
|
||||||
|
DrawCircleV(clampedPos, 25, grayish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw2DDebug(GameState *S) {
|
||||||
|
Rectangle rect = {SCREEN_WIDTH / 2 - 50, SCREEN_HEIGHT - 100, 110, 50};
|
||||||
|
DrawRectangleRec(rect, S->colorUnderPlayer);
|
||||||
|
DrawRectangleLinesEx(rect, 2, BLACK);
|
||||||
|
DrawFPS(rect.x + 17, SCREEN_HEIGHT - 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
GameState *state = Init();
|
||||||
|
while (!WindowShouldClose()) {
|
||||||
|
state->dt = GetFrameTime();
|
||||||
|
state->time_elapsed = GetTime();
|
||||||
|
|
||||||
|
Input(state);
|
||||||
|
|
||||||
|
Update(state);
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
{
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
Draw2D(state);
|
||||||
|
Draw2DDebug(state);
|
||||||
|
}
|
||||||
|
EndDrawing();
|
||||||
|
state->frame_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnloadImage(state->imageBuffer);
|
||||||
|
UnloadTexture(state->displayTexture);
|
||||||
|
CloseWindow();
|
||||||
|
free(state->fill);
|
||||||
|
free(state->queue);
|
||||||
|
free(state->bfsBuffer);
|
||||||
|
free(state);
|
||||||
|
}
|
1689
include/raylib.h
Normal file
1689
include/raylib.h
Normal file
File diff suppressed because it is too large
Load Diff
2583
include/raymath.h
Normal file
2583
include/raymath.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
lib/libraylib.a
Normal file
BIN
lib/libraylib.a
Normal file
Binary file not shown.
@ -18,7 +18,7 @@ def dump(struct):
|
|||||||
|
|
||||||
screen_width = 1024
|
screen_width = 1024
|
||||||
screen_height = 512
|
screen_height = 512
|
||||||
r = 10
|
r = 25
|
||||||
k = 30
|
k = 30
|
||||||
cell_size = r / math.sqrt(2)
|
cell_size = r / math.sqrt(2)
|
||||||
cols = int(screen_width // cell_size)
|
cols = int(screen_width // cell_size)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user