Adding C projects and flood-fill.py

This commit is contained in:
Joseph Ferano 2024-08-17 16:20:47 +07:00
parent 80e4179448
commit 633ddb73c1
8 changed files with 4666 additions and 1 deletions

15
Makefile Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

2583
include/raymath.h Normal file

File diff suppressed because it is too large Load Diff

BIN
lib/libraylib.a Normal file

Binary file not shown.

View File

@ -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)