add support for clipping rectangles to graphics libary, add point and rect types

pull/1/head
Jonathan Williamson 2021-01-18 07:58:19 +00:00
rodzic 5a7125f803
commit 8345197a83
6 zmienionych plików z 218 dodań i 85 usunięć

Wyświetl plik

@ -109,18 +109,18 @@ int main() {
uint32_t i = 0;
while(true) {
pico_display.set_pen(120, 40, 60);
pico_display.rectangle(0, 0, 240, 135);
pico_display.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= pico_display.width) shape.dx *= -1;
if(shape.x >= pico_display.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= pico_display.height) shape.dy *= -1;
if(shape.y >= pico_display.bounds.h) shape.dy *= -1;
pico_display.set_pen(shape.pen);
pico_display.circle(shape.x, shape.y, shape.r);
pico_display.circle(point(shape.x, shape.y), shape.r);
}
float led_step = fmod(i / 20.0f, M_PI * 2.0f);

Wyświetl plik

@ -86,57 +86,51 @@ int main() {
uint32_t i = 0;
while(true) {
pico_explorer.set_pen(120, 40, 60);
pico_explorer.rectangle(0, 0, pico_explorer.width, pico_explorer.height);
pico_explorer.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= pico_explorer.width) shape.dx *= -1;
if(shape.x >= pico_explorer.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= pico_explorer.height) shape.dy *= -1;
if(shape.y >= pico_explorer.bounds.h) shape.dy *= -1;
pico_explorer.set_pen(shape.pen);
pico_explorer.circle(shape.x, shape.y, shape.r);
pico_explorer.circle(point(shape.x, shape.y), shape.r);
}
pico_explorer.set_pen(255, 255, 255);
pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", 10, 10, 180);
float rv = pico_explorer.get_adc(pico_explorer.ADC0);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(rv * 140 + 50, 110, 20);
pico_explorer.circle(point(rv * 140 + 50, 110), 20);
pico_explorer.set_pen(rv * 255, 0, 0);
pico_explorer.circle(rv * 140 + 50, 110, 15);
pico_explorer.circle(point(rv * 140 + 50, 110), 15);
float gv = pico_explorer.get_adc(pico_explorer.ADC1);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(gv * 140 + 50, 160, 20);
pico_explorer.circle(point(gv * 140 + 50, 160), 20);
pico_explorer.set_pen(0, gv * 255, 0);
pico_explorer.circle(gv * 140 + 50, 160, 15);
pico_explorer.circle(point(gv * 140 + 50, 160), 15);
float bv = pico_explorer.get_adc(pico_explorer.ADC2);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(bv * 140 + 50, 210, 20);
pico_explorer.circle(point(bv * 140 + 50, 210), 20);
pico_explorer.set_pen(0, 0, bv * 255);
pico_explorer.circle(bv * 140 + 50, 210, 15);
pico_explorer.circle(point(bv * 140 + 50, 210), 15);
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, bv);
pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.FORWARD, rv);
pico_explorer.set_tone(100 + (bv * 1000), rv);
float tyoff = cos(i / 20.0f) * 50.0f - 50.0f;
rect text_box(10, 10, 150, 150);
pico_explorer.set_pen(55, 65, 75);
pico_explorer.rectangle(text_box);
text_box.deflate(10);
pico_explorer.set_clip(text_box);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.text("x: " + std::to_string(msa301.get_axis(msa301.X, 16)), 10, 190, 100);
pico_explorer.text("y: " + std::to_string(msa301.get_axis(msa301.Y, 16)), 10, 205, 100);
pico_explorer.text("z: " + std::to_string(msa301.get_axis(msa301.Z, 16)), 10, 220, 100);
uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120;
uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120;
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(xpos, ypos, 20);
pico_explorer.set_pen(255, 0, 255);
pico_explorer.circle(xpos, ypos, 15);
pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", point(text_box.x, text_box.y + tyoff), 100);
float xoff = sin(i / 20.0f) * 50.0f;
xoff += 120 - (81 / 2);
@ -151,10 +145,25 @@ int main() {
uint8_t b = *src++;
pico_explorer.set_pen(r, g, b);
pico_explorer.pixel(x + xoff, 68 - y + yoff);
pico_explorer.pixel(point(x + xoff, 68 - y + yoff));
}
}
pico_explorer.remove_clip();
pico_explorer.set_pen(255, 255, 255);
pico_explorer.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), point(10, 190), 100);
pico_explorer.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), point(10, 205), 100);
pico_explorer.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), point(10, 220), 100);
uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120;
uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120;
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(point(xpos, ypos), 20);
pico_explorer.set_pen(255, 0, 255);
pico_explorer.circle(point(xpos, ypos), 15);
/*
if(pico_display.is_pressed(pico_display.A)) {
pico_display.rectangle(0, 0, 18, 18);

Wyświetl plik

@ -1 +1 @@
add_library(pico_graphics font_data.cpp pico_graphics.cpp)
add_library(pico_graphics types.cpp font_data.cpp pico_graphics.cpp)

Wyświetl plik

@ -6,11 +6,11 @@ extern uint8_t character_widths[96];
namespace pimoroni {
void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) {
this->pen = create_pen(r, g, b);
pen = create_pen(r, g, b);
}
void PicoGraphics::set_pen(uint16_t pen) {
this->pen = pen;
void PicoGraphics::set_pen(uint16_t p) {
pen = p;
}
uint16_t PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {
@ -22,49 +22,73 @@ namespace pimoroni {
return ((p & 0xff00) >> 8) | ((p & 0xff) << 8);
}
void PicoGraphics::set_clip(const rect &r) {
clip = r;
}
void PicoGraphics::remove_clip() {
clip = bounds;
}
uint16_t* PicoGraphics::ptr(const rect &r) {
return frame_buffer + r.x + r.y * bounds.w;
}
uint16_t* PicoGraphics::ptr(const point &p) {
return frame_buffer + p.x + p.y * bounds.w;
}
uint16_t* PicoGraphics::ptr(int32_t x, int32_t y) {
return this->frame_buffer + x + y * this->width;
return frame_buffer + x + y * bounds.w;
}
void PicoGraphics::pixel(int32_t x, int32_t y) {
if(x < 0 || y < 0 || x >= this->width || y >= this->height) { return; }
*ptr(x, y) = pen;
void PicoGraphics::clear() {
rectangle(clip);
}
void PicoGraphics::pixel_span(int32_t x, int32_t y, int32_t l) {
if(x + l < 0 || y < 0 || x >= this->width || y >= this->height) { return; }
void PicoGraphics::pixel(const point &p) {
if(!clip.contains(p)) return;
*ptr(p) = pen;
}
if(x < 0) {l += x; x = 0;}
if(x + l >= this->width) {l = this->width - x;}
void PicoGraphics::pixel_span(const point &p, int32_t l) {
// check if span in bounds
if( p.x + l < clip.x || p.x >= clip.x + clip.w ||
p.y < clip.y || p.y >= clip.y + clip.h) return;
uint16_t *p = ptr(x, y);
// clamp span horizontally
point clipped = p;
if(clipped.x < clip.x) {l += clipped.x - clip.x; clipped.x = clip.x;}
if(clipped.x + l >= clip.x + clip.w) {l = clip.x + clip.w - clipped.x;}
uint16_t *dest = ptr(clipped);
while(l--) {
*p++ = pen;
*dest++ = pen;
}
}
void PicoGraphics::rectangle(int32_t x, int32_t y, int32_t width, int32_t height) {
void PicoGraphics::rectangle(const rect &r) {
// clip and/or discard depending on rectangle visibility
if(x >= this->width || y >= this->height) { return; }
if(x < 0) { width += x; x = 0; }
if(y < 0) { height += y; y = 0; }
if(width <= 0 || height <= 0) { return; }
if(x + width >= this->width) { width = this->width - x; }
if(y + height >= this->height) { height = this->height - y; }
rect clipped = r.intersection(clip);
uint16_t *p = ptr(x, y);
while(height--) {
for(uint32_t i = 0; i < width; i++) {
*p++ = this->pen;
if(clipped.empty()) return;
uint16_t *dest = ptr(clipped);
while(clipped.h--) {
// draw span of pixels for this row
for(uint32_t i = 0; i < clipped.w; i++) {
*dest++ = pen;
}
p += this->width - width; // move to next scanline
// move to next scanline
dest += bounds.w - clipped.w;
}
}
void PicoGraphics::circle(int32_t x, int32_t y, int32_t radius) {
void PicoGraphics::circle(const point &p, int32_t radius) {
// circle in screen bounds?
if(x + radius < 0 || y + radius < 0 || x - radius >= this->width || y - radius >= this->height) { return; }
rect bounds = rect(p.x - radius, p.y - radius, radius * 2, radius * 2);
if(!bounds.intersects(clip)) return;
int ox = radius, oy = 0, err = -radius;
while (ox >= oy)
@ -73,31 +97,33 @@ namespace pimoroni {
err += oy; oy++; err += oy;
pixel_span(x - ox, y + last_oy, ox * 2 + 1);
pixel_span(point(p.x - ox, p.y + last_oy), ox * 2 + 1);
if (last_oy != 0) {
pixel_span(x - ox, y - last_oy, ox * 2 + 1);
pixel_span(point(p.x - ox, p.y - last_oy), ox * 2 + 1);
}
if (err >= 0) {
if (ox != last_oy) {
pixel_span(x - last_oy, y + ox, last_oy * 2 + 1);
if (ox != 0) {
pixel_span(x - last_oy, y - ox, last_oy * 2 + 1);
}
err -= ox; ox--; err -= ox;
if(err >= 0 && ox != last_oy) {
pixel_span(point(p.x - last_oy, p.y + ox), last_oy * 2 + 1);
if (ox != 0) {
pixel_span(point(p.x - last_oy, p.y - ox), last_oy * 2 + 1);
}
err -= ox; ox--; err -= ox;
}
}
}
void PicoGraphics::character(const char c, int32_t x, int32_t y, uint8_t scale) {
void PicoGraphics::character(const char c, const point &p, uint8_t scale) {
uint8_t char_index = c - 32;
rect char_bounds(p.x, p.y, character_widths[char_index] * scale, 6 * scale);
if(!clip.intersects(char_bounds)) return;
const uint8_t *d = &font_data[char_index][0];
for(uint8_t cx = 0; cx < character_widths[char_index]; cx++) {
for(uint8_t cy = 0; cy < 6; cy++) {
if((1U << cy) & *d) {
rectangle(x + (cx * scale), y + (cy * scale), scale, scale);
rectangle(rect(p.x + (cx * scale), p.y + (cy * scale), scale, scale));
}
}
@ -105,7 +131,7 @@ namespace pimoroni {
}
}
void PicoGraphics::text(const std::string &t, int32_t x, int32_t y, int32_t wrap) {
void PicoGraphics::text(const std::string &t, const point &p, int32_t wrap) {
uint32_t co = 0, lo = 0; // character and line (if wrapping) offset
uint8_t scale = 2;
@ -119,21 +145,21 @@ namespace pimoroni {
next_space = t.length();
}
uint16_t word_length = 0;
uint16_t word_width = 0;
for(size_t j = i; j < next_space; j++) {
word_length += character_widths[t[j] - 32];
word_width += character_widths[t[j] - 32] * scale;
}
// if this word would exceed the wrap limit then
// move to the next line
if(co + word_length > wrap) {
if(co != 0 && co + word_width > wrap) {
co = 0;
lo += 7 * scale;
}
// draw word
for(size_t j = i; j < next_space; j++) {
character(t[j], x + co, y + lo, scale);
character(t[j], point(p.x + co, p.y + lo), scale);
co += character_widths[t[j] - 32] * scale;
}

Wyświetl plik

@ -1,35 +1,73 @@
#pragma once
#include <cstdint>
#include <string>
#include <algorithm>
// a tiny little graphics library for our Pico products
// supports only 16-bit (565) RGB framebuffers
namespace pimoroni {
struct rect;
struct point {
int32_t x = 0, y = 0;
point() = default;
point(int32_t x, int32_t y) : x(x), y(y) {}
inline point& operator-= (const point &a) { x -= a.x; y -= a.y; return *this; }
inline point& operator+= (const point &a) { x += a.x; y += a.y; return *this; }
point clamp(const rect &r) const;
};
struct rect {
int32_t x = 0, y = 0, w = 0, h = 0;
rect() = default;
rect(int32_t x, int32_t y, int32_t w, int32_t h) : x(x), y(y), w(w), h(h) {}
bool empty() const;
bool contains(const point &p) const;
bool contains(const rect &p) const;
bool intersects(const rect &r) const;
rect intersection(const rect &r) const;
void inflate(int32_t v);
void deflate(int32_t v);
};
class PicoGraphics {
public:
uint16_t width;
uint16_t height;
rect bounds;
rect clip;
uint16_t *frame_buffer;
uint16_t pen;
uint16_t pen;
public:
PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer)
: frame_buffer(frame_buffer), width(width), height(height) {}
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) {}
void set_pen(uint8_t r, uint8_t g, uint8_t b);
void set_pen(uint16_t p);
uint16_t create_pen(uint8_t r, uint8_t g, uint8_t b);
void set_clip(const rect &r);
void remove_clip();
uint16_t* ptr(const point &p);
uint16_t* ptr(const rect &r);
uint16_t* ptr(int32_t x, int32_t y);
void pixel(int32_t x, int32_t y);
void pixel_span(int32_t x, int32_t y, int32_t l);
void rectangle(int32_t x, int32_t y, int32_t w, int32_t h);
void circle(int32_t x, int32_t y, int32_t r);
void text(const std::string &t, int32_t x, int32_t y, int32_t wrap);
void character(const char c, int32_t x, int32_t y, uint8_t scale);
void clear();
void pixel(const point &p);
void pixel_span(const point &p, int32_t l);
void rectangle(const rect &r);
void circle(const point &p, int32_t r);
void text(const std::string &t, const point &p, int32_t wrap);
void character(const char c, const point &p, uint8_t scale);
//void polygon(std::vector);
};

Wyświetl plik

@ -0,0 +1,60 @@
#include <cstdint>
#include <algorithm>
#include "pico_graphics.hpp"
namespace pimoroni {
point point::clamp(const rect &r) const {
return point(
std::min(std::max(x, r.x), r.x + r.w),
std::min(std::max(y, r.y), r.y + r.h)
);
}
point operator- (point lhs, const point &rhs) {
lhs -= rhs;
return lhs;
}
point operator- (const point &rhs) {
return point(-rhs.x, -rhs.y);
}
point operator+ (point lhs, const point &rhs) {
lhs += rhs;
return lhs;
}
bool rect::empty() const {
return w <= 0 || h <= 0;
}
bool rect::contains(const point &p) const {
return p.x >= x && p.y >= y && p.x < x + w && p.y < y + h;
}
bool rect::contains(const rect &p) const {
return p.x >= x && p.y >= y && p.x + p.w < x + w && p.y + p.h < y + h;
}
bool rect::intersects(const rect &r) const {
return !(x > r.x + r.w || x + w < r.x || y > r.y + r.h || y + h < r.y);
}
rect rect::intersection(const rect &r) const {
return rect(std::max(x, r.x),
std::max(y, r.y),
std::min(x + w, r.x + r.w) - std::max(x, r.x),
std::min(y + h, r.y + r.h) - std::max(y, r.y));
}
void rect::inflate(int32_t v) {
x -= v; y -= v; w += v * 2; h += v * 2;
}
void rect::deflate(int32_t v) {
x += v; y += v; w -= v * 2; h -= v * 2;
}
}