kopia lustrzana https://github.com/pimoroni/pimoroni-pico
add support for clipping rectangles to graphics libary, add point and rect types
rodzic
5a7125f803
commit
8345197a83
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1 +1 @@
|
|||
add_library(pico_graphics font_data.cpp pico_graphics.cpp)
|
||||
add_library(pico_graphics types.cpp font_data.cpp pico_graphics.cpp)
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Ładowanie…
Reference in New Issue