跳转至

rectangle.c

Types

Name
enum ____CSCode

Functions

Name
int cs_compute_code(const CFBDGraphicRect * r, int32_t x, int32_t y)

Defines

Name
MAX(a, b)
MIN(a, b)

Types Documentation

enum ____CSCode

Enumerator Value Description
CS_LEFT 1 << 0
CS_RIGHT 1 << 1
CS_BOTTOM 1 << 2
CS_TOP 1 << 3

Functions Documentation

function cs_compute_code

static int cs_compute_code(
    const CFBDGraphicRect * r,
    int32_t x,
    int32_t y
)

Macros Documentation

define MAX

#define MAX(
    a,
    b
)
((a) > (b) ? (a) : (b))

define MIN

#define MIN(
    a,
    b
)
((a) < (b) ? (a) : (b))

Source code

#include "rectangle.h"

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

#include "device/graphic_device.h"

typedef enum
{
    CS_LEFT = 1 << 0,
    CS_RIGHT = 1 << 1,
    CS_BOTTOM = 1 << 2,
    CS_TOP = 1 << 3
} ____CSCode;

void CFBDGraphic_DrawRect(CFBD_GraphicDevice* device, CFBDGraphicRect* rect)
{
    if (device == NULL || rect == NULL)
        return;
    if (device->ops == NULL)
        return;

    CFBDGraphicRect n = rect_normalize(*rect);
    int32_t lx = asInt32_t(n.tl.x);
    int32_t ty = asInt32_t(n.tl.y);
    int32_t rx = asInt32_t(n.br.x);
    int32_t by = asInt32_t(n.br.y);

    if (rx < lx)
        rx = lx;
    if (by < ty)
        by = ty;

    uint16_t w = (uint16_t) (rx - lx + 1);
    uint16_t h = (uint16_t) (by - ty + 1);
    device->ops->clear_area(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(ty), w, h);

    if (lx == rx && ty == by) {
        device->ops->setPixel(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(ty));
    }
    else {
        /* 顶边与底边(水平): x = lx..rx */
        for (int32_t x = lx; x <= rx; ++x) {
            device->ops->setPixel(device, clamp_u16_from_i32(x), clamp_u16_from_i32(ty));
            if (by != ty) /* 如果高度>1 再画底边,防止重复画同一行 */
                device->ops->setPixel(device, clamp_u16_from_i32(x), clamp_u16_from_i32(by));
        }
        if (by - ty >= 2) {
            for (int32_t y = ty + 1; y <= by - 1; ++y) {
                device->ops->setPixel(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(y));
                if (rx != lx)
                    device->ops->setPixel(device, clamp_u16_from_i32(rx), clamp_u16_from_i32(y));
            }
        }
    }

    if (CFBDGraphic_DeviceRequestUpdateAtOnce(device)) {
        device->ops->update_area(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(ty), w, h);
    }
}

void CFBDGraphic_FillRect(CFBD_GraphicDevice* device, CFBDGraphicRect* rect)
{
    CFBDGraphicRect n = rect_normalize(*rect);
    int32_t lx = asInt32_t(n.tl.x);
    int32_t ty = asInt32_t(n.tl.y);
    int32_t rx = asInt32_t(n.br.x);
    int32_t by = asInt32_t(n.br.y);

    uint16_t w = (uint16_t) (rx - lx + 1);
    uint16_t h = (uint16_t) (by - ty + 1);
    device->ops->clear_area(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(ty), w, h);

    for (int32_t y = ty; y <= by; ++y) {
        for (int32_t x = lx; x <= rx; ++x) {
            device->ops->setPixel(device, clamp_u16_from_i32(x), clamp_u16_from_i32(y));
        }
    }

    if (CFBDGraphic_DeviceRequestUpdateAtOnce(device)) {
        device->ops->update_area(device, clamp_u16_from_i32(lx), clamp_u16_from_i32(ty), w, h);
    }
}

static int cs_compute_code(const CFBDGraphicRect* r, int32_t x, int32_t y)
{
    CFBDGraphicRect n = rect_normalize(*r);
    int32_t code = 0;

    if (x < asInt32_t(n.tl.x))
        code |= CS_LEFT;
    else if (x > asInt32_t(n.br.x))
        code |= CS_RIGHT;

    if (y < asInt32_t(n.tl.y))
        code |= CS_TOP;
    else if (y > asInt32_t(n.br.y))
        code |= CS_BOTTOM;

    return code;
}

CFBDGraphicRect rect_intersection(const CFBDGraphicRect* a, const CFBDGraphicRect* b)
{
    CFBDGraphicRect A = rect_normalize(*a);
    CFBDGraphicRect B = rect_normalize(*b);

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
    int32_t lx = MAX(asInt32_t(A.tl.x), asInt32_t(B.tl.x));
    int32_t ty = MAX(asInt32_t(A.tl.y), asInt32_t(B.tl.y));
    int32_t rx = MIN(asInt32_t(A.br.x), asInt32_t(B.br.x));
    int32_t by = MIN(asInt32_t(A.br.y), asInt32_t(B.br.y));
#undef MAX
#undef MIN

    if (rx < lx)
        rx = lx;
    if (by < ty)
        by = ty;

    return (CFBDGraphicRect) {{clamp_u16_from_i32(lx), clamp_u16_from_i32(ty)},
                              {clamp_u16_from_i32(rx), clamp_u16_from_i32(by)}};
}

CFBDGraphicRect rect_union(const CFBDGraphicRect* a, const CFBDGraphicRect* b)
{
    CFBDGraphicRect A = rect_normalize(*a);
    CFBDGraphicRect B = rect_normalize(*b);

    int32_t lx = asInt32_t(A.tl.x) < asInt32_t(B.tl.x) ? asInt32_t(A.tl.x) : asInt32_t(B.tl.x);
    int32_t ty = asInt32_t(A.tl.y) < asInt32_t(B.tl.y) ? asInt32_t(A.tl.y) : asInt32_t(B.tl.y);
    int32_t rx = asInt32_t(A.br.x) > asInt32_t(B.br.x) ? asInt32_t(A.br.x) : asInt32_t(B.br.x);
    int32_t by = asInt32_t(A.br.y) > asInt32_t(B.br.y) ? asInt32_t(A.br.y) : asInt32_t(B.br.y);

    return (CFBDGraphicRect) {{clamp_u16_from_i32(lx), clamp_u16_from_i32(ty)},
                              {clamp_u16_from_i32(rx), clamp_u16_from_i32(by)}};
}

CFBDGraphicRect rect_offset(const CFBDGraphicRect* r, int32_t dx, int32_t dy)
{
    CFBDGraphicRect n = rect_normalize(*r);

    return (CFBDGraphicRect) {{clamp_u16_from_i32(asInt32_t(n.tl.x) + dx),
                               clamp_u16_from_i32(asInt32_t(n.tl.y) + dy)},
                              {clamp_u16_from_i32(asInt32_t(n.br.x) + dx),
                               clamp_u16_from_i32(asInt32_t(n.br.y) + dy)}};
}

CFBDGraphicRect
rect_inset(const CFBDGraphicRect* r, int32_t left, int32_t top, int32_t right, int32_t bottom)
{
    CFBDGraphicRect n = rect_normalize(*r);

    int32_t lx = asInt32_t(n.tl.x) + left;
    int32_t ty = asInt32_t(n.tl.y) + top;
    int32_t rx = asInt32_t(n.br.x) - right;
    int32_t by = asInt32_t(n.br.y) - bottom;

    if (rx < lx)
        rx = lx;
    if (by < ty)
        by = ty;

    return (CFBDGraphicRect) {{clamp_u16_from_i32(lx), clamp_u16_from_i32(ty)},
                              {clamp_u16_from_i32(rx), clamp_u16_from_i32(by)}};
}

CFBDGraphicRect rect_scale_about_center(const CFBDGraphicRect* r, float sx, float sy)
{
    CFBDGraphicRect n = rect_normalize(*r);

    int32_t cx = (asInt32_t(n.tl.x) + asInt32_t(n.br.x)) / 2;
    int32_t cy = (asInt32_t(n.tl.y) + asInt32_t(n.br.y)) / 2;

    int32_t hw = asInt32_t(n.br.x) - asInt32_t(n.tl.x);
    int32_t hh = asInt32_t(n.br.y) - asInt32_t(n.tl.y);

    int32_t new_hw = (int32_t) floorf(hw * sx / 2.0f + 0.5f);
    int32_t new_hh = (int32_t) floorf(hh * sy / 2.0f + 0.5f);

    return (CFBDGraphicRect) {{clamp_u16_from_i32(cx - new_hw), clamp_u16_from_i32(cy - new_hh)},
                              {clamp_u16_from_i32(cx + new_hw), clamp_u16_from_i32(cy + new_hh)}};
}

CFBDGraphicRect
rect_align_in_parent(const CFBDGraphicRect* parent, const CFBDGraphicRect* child, uint8_t anchor)
{
    CFBDGraphicRect P = rect_normalize(*parent);
    CFBDGraphicRect C = rect_normalize(*child);
    int32_t pw = asInt32_t(P.br.x) - asInt32_t(P.tl.x);
    int32_t ph = asInt32_t(P.br.y) - asInt32_t(P.tl.y);
    int32_t cw = asInt32_t(C.br.x) - asInt32_t(C.tl.x);
    int32_t ch = asInt32_t(C.br.y) - asInt32_t(C.tl.y);
    int32_t nx = asInt32_t(P.tl.x);
    int32_t ny = asInt32_t(P.tl.y); /* 水平 */
    if (anchor & RECT_ANCHOR_LEFT) {
        nx = asInt32_t(P.tl.x);
    }
    else if (anchor & RECT_ANCHOR_RIGHT) {
        nx = asInt32_t(P.br.x) - cw;
    }
    else { /* center by default */
        nx = asInt32_t(P.tl.x) + (pw - cw) / 2;
    } /* 垂直 */
    if (anchor & RECT_ANCHOR_TOP) {
        ny = asInt32_t(P.tl.y);
    }
    else if (anchor & RECT_ANCHOR_BOTTOM) {
        ny = asInt32_t(P.br.y) - ch;
    }
    else {
        ny = asInt32_t(P.tl.y) + (ph - ch) / 2;
    }
    return rect_from_xywh(nx, ny, cw, ch);
}

/* ---------- 最近点 / 点到矩形距离 ---------- */

/* 返回矩形内到点最近的点(如果点在矩形内,则返回点本身,结果为 uint16_t) */
CFBDGraphic_Point rect_closest_point(const CFBDGraphicRect* r, CFBDGraphic_Point p)
{
    CFBDGraphicRect n = rect_normalize(*r);
    int32_t px = asInt32_t(p.x), py = asInt32_t(p.y);
    int32_t lx = asInt32_t(n.tl.x), ty = asInt32_t(n.tl.y);
    int32_t rx = asInt32_t(n.br.x), by = asInt32_t(n.br.y);
    int32_t cx = px < lx ? lx : (px > rx ? rx : px);
    int32_t cy = py < ty ? ty : (py > by ? by : py);
    CFBDGraphic_Point out = {clamp_u16_from_i32(cx), clamp_u16_from_i32(cy)};
    return out;
}

/* 点到矩形最短距离(像素距离,若在矩形内则为0)*/
uint32_t rect_distance_to_point(const CFBDGraphicRect* r, CFBDGraphic_Point p)
{
    CFBDGraphic_Point cp = rect_closest_point(r, p);
    int32_t dx = asInt32_t(cp.x) - asInt32_t(p.x);
    int32_t dy = asInt32_t(cp.y) - asInt32_t(p.y);
    return (uint32_t) (sqrt((double) (dx * dx + dy * dy)));
}

/* ---------- 扩展 / 包含点 ---------- */
CFBDGraphicRect rect_expand_to_include_point(const CFBDGraphicRect* r, CFBDGraphic_Point p)
{
    CFBDGraphicRect n = rect_normalize(*r);
    int32_t lx = asInt32_t(n.tl.x) < asInt32_t(p.x) ? asInt32_t(n.tl.x) : asInt32_t(p.x);
    int32_t ty = asInt32_t(n.tl.y) < asInt32_t(p.y) ? asInt32_t(n.tl.y) : asInt32_t(p.y);
    int32_t rx = asInt32_t(n.br.x) > asInt32_t(p.x) ? asInt32_t(n.br.x) : asInt32_t(p.x);
    int32_t by = asInt32_t(n.br.y) > asInt32_t(p.y) ? asInt32_t(n.br.y) : asInt32_t(p.y);
    CFBDGraphicRect out = {{clamp_u16_from_i32(lx), clamp_u16_from_i32(ty)},
                           {clamp_u16_from_i32(rx), clamp_u16_from_i32(by)}};
    return out;
}

CFBD_Bool rect_clip_line(CFBDGraphicRect* r, CFBDGraphic_Point* p0, CFBDGraphic_Point* p1)
{
    CFBDGraphicRect n = rect_normalize(*r);
    int32_t x0 = asInt32_t(p0->x), y0 = asInt32_t(p0->y);
    int32_t x1 = asInt32_t(p1->x), y1 = asInt32_t(p1->y);
    int code0 = cs_compute_code(&n, x0, y0);
    int code1 = cs_compute_code(&n, x1, y1);
    CFBD_Bool accept = CFBD_FALSE;
    while (1) {
        if ((code0 | code1) == 0) {
            accept = CFBD_TRUE;
            break;
        }
        else if (code0 & code1) {
            accept = CFBD_FALSE;
            break;
        }
        else {
            int outcode = code0 ? code0 : code1;
            int32_t nx = 0, ny = 0;
            if (outcode & CS_TOP) {
                nx = x0 +
                     (int32_t) ((double) (x1 - x0) * (asInt32_t(n.tl.y) - y0) / (double) (y1 - y0));
                ny = asInt32_t(n.tl.y);
            }
            else if (outcode & CS_BOTTOM) {
                nx = x0 +
                     (int32_t) ((double) (x1 - x0) * (asInt32_t(n.br.y) - y0) / (double) (y1 - y0));
                ny = asInt32_t(n.br.y);
            }
            else if (outcode & CS_RIGHT) {
                ny = y0 +
                     (int32_t) ((double) (y1 - y0) * (asInt32_t(n.br.x) - x0) / (double) (x1 - x0));
                nx = asInt32_t(n.br.x);
            }
            else if (outcode & CS_LEFT) {
                ny = y0 +
                     (int32_t) ((double) (y1 - y0) * (asInt32_t(n.tl.x) - x0) / (double) (x1 - x0));
                nx = asInt32_t(n.tl.x);
            }
            if (outcode == code0) {
                x0 = nx;
                y0 = ny;
                code0 = cs_compute_code(&n, x0, y0);
            }
            else {
                x1 = nx;
                y1 = ny;
                code1 = cs_compute_code(&n, x1, y1);
            }
        }
    }
    if (accept) {
        p0->x = clamp_u16_from_i32(x0);
        p0->y = clamp_u16_from_i32(y0);
        p1->x = clamp_u16_from_i32(x1);
        p1->y = clamp_u16_from_i32(y1);
    }
    return accept;
}

/* ---------- 多用途:裁剪到屏幕边界 ---------- */
CFBDGraphicRect rect_clamp_to_screen(const CFBDGraphicRect* r, uint16_t screen_w, uint16_t screen_h)
{
    CFBDGraphicRect n = rect_normalize(*r);
    int32_t lx = asInt32_t(n.tl.x) < 0 ? 0 : asInt32_t(n.tl.x);
    int32_t ty = asInt32_t(n.tl.y) < 0 ? 0 : asInt32_t(n.tl.y);
    int32_t rx = asInt32_t(n.br.x) > screen_w ? screen_w : asInt32_t(n.br.x);
    int32_t by = asInt32_t(n.br.y) > screen_h ? screen_h : asInt32_t(n.br.y);
    if (rx < lx)
        rx = lx;
    if (by < ty)
        by = ty;
    CFBDGraphicRect out = {{clamp_u16_from_i32(lx), clamp_u16_from_i32(ty)},
                           {clamp_u16_from_i32(rx), clamp_u16_from_i32(by)}};
    return out;
}

Updated on 2026-02-03 at 13:21:55 +0000