ncvisual_rotate: unify _cw/_ccw, take radians

This commit is contained in:
nick black 2020-05-06 05:49:22 -04:00
parent 20d0048838
commit 94fcada67c
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
8 changed files with 78 additions and 69 deletions

View File

@ -9,9 +9,8 @@ rearrangements of Notcurses.
has been retained as a deprecated alias. It will be removed by 1.6/2.0.
* `ncvisual_from_rgba()` and `ncvisual_from_bgra()` have been added to
support creation of `ncvisual`s from memory, requiring no file.
* `ncvisual_rotate_cw()` and `ncvisual_rotate_ccw()` have been added, having
the same semantics as `ncplane_rotate_cw()` and `ncplane_rotate_ccw()`.
They will likely be augmented in the future to support arbitrary rotations.
* `ncvisual_rotate()` has been added, supporting rotations of arbitrary
radians on `ncvisual` objects.
* `ncvisual_from_plane()` has been added to support "promotion" of an
`ncplane` to an `ncvisual`. The source plane may contain only spaces,
half blocks, and full blocks.

View File

@ -2436,9 +2436,10 @@ struct ncplane* ncvisual_plane(struct ncvisual* ncv);
// of the UTF8 text.
char* ncvisual_subtitle(const struct ncvisual* ncv);
// Rotate the visual π/2 radians clockwise or counterclockwise.
int ncvisual_rotate_cw(struct ncvisual* n);
int ncvisual_rotate_ccw(struct ncvisual* n);
// Rotate the visual 'rads' radians. If we own the bound plane, it is resized
// to fit the rotated visual, if necessary. Only M_PI/2 and -M_PI/2 are
// supported at the moment, but this will change FIXME.
int ncvisual_rotate(struct ncvisual* n, double rads);
```
It is also possible to seed an `ncvisual` directly from memory, without involving

View File

@ -46,9 +46,7 @@ typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
**struct ncplane* ncvisual_plane(struct ncvisual* ncv);**
**int ncvisual_rotate_cw(struct ncvisual* n);**
**int ncvisual_rotate_ccw(struct ncvisual* n);**
**int ncvisual_rotate(struct ncvisual* n, double rads);**
**char* ncvisual_subtitle(const struct ncvisual* ncv);**
@ -76,8 +74,10 @@ is necessary. The resulting plane will be ceil(**rows**/2) rows, and **cols**
columns. It will not be necessary to call **ncvisual_decode**, but it is
still necessary to call **ncvisual_render**.
Both **ncvisual_rotate_cw** and **ncvisual_rotate_ccw** execute a rotation of
π/2 radians, in the clockwise or counterclockwise direction respectively.
**ncvisual_rotate** executes a rotation of **rads** radians, in the clockwise
(positive) or counterclockwise (negative) direction. If the **ncvisual** owns
(created) its underlying **ncplane**, that plane will be resized as necessary
to display the entirety of the rotated visual.
**ncvisual_subtitle** will return a UTF-8-encoded subtitle corresponding to
the current frame if such a subtitle was decoded. Note that a subtitle might
@ -102,6 +102,11 @@ Multimedia decoding requires that Notcurses be built with either FFmpeg or
OpenImageIO support. What formats can be decoded is totally dependent on the
linked library. OpenImageIO does not support subtitles.
# BUGS
**ncvisual_rotate** currently supports only **M_PI**/2 and -**M_PI**/2
radians for **rads**, but this will change soon.
# SEE ALSO
**notcurses(3)**,

View File

@ -84,14 +84,9 @@ namespace ncpp
Plane* get_plane () const noexcept;
bool rotate_cw () const NOEXCEPT_MAYBE
bool rotate (double rads) const NOEXCEPT_MAYBE
{
return error_guard (ncvisual_rotate_cw (visual), -1);
}
bool rotate_ccw () const noexcept
{
return error_guard (ncvisual_rotate_ccw (visual), -1);
return error_guard (ncvisual_rotate (visual, rads), -1);
}
private:

View File

@ -1136,10 +1136,10 @@ API struct ncplane* ncplane_below(struct ncplane* n);
API int ncplane_rotate_cw(struct ncplane* n);
API int ncplane_rotate_ccw(struct ncplane* n);
// Rotate the visual π/2 radians clockwise or counterclockwise. This cannot
// be performed on arbitrary planes, because glyphs cannot be arbitrarily rotated.
API int ncvisual_rotate_cw(struct ncvisual* n);
API int ncvisual_rotate_ccw(struct ncvisual* n);
// Rotate the visual 'rads' radians. If we own the bound plane, it is resized
// to fit the rotated visual, if necessary. Only M_PI/2 and -M_PI/2 are
// supported at the moment, but this will change FIXME.
API int ncvisual_rotate(struct ncvisual* n, double rads);
// Retrieve the current contents of the cell under the cursor. The EGC is
// returned, or NULL on error. This EGC must be free()d by the caller. The

View File

@ -429,8 +429,7 @@ int ncplane_format(struct ncplane* n, int ystop, int xstop, uint32_t attrword);
int ncplane_stain(struct ncplane* n, int ystop, int xstop, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr);
int ncplane_rotate_cw(struct ncplane* n);
int ncplane_rotate_ccw(struct ncplane* n);
int ncvisual_rotate_cw(struct ncvisual* n);
int ncvisual_rotate_ccw(struct ncvisual* n);
int ncvisual_rotate(struct ncvisual* n, double rads);
void ncplane_translate(const struct ncplane* src, const struct ncplane* dst, int* y, int* x);
bool ncplane_translate_abs(const struct ncplane* n, int* y, int* x);
typedef enum {

View File

@ -1,3 +1,4 @@
#include <math.h>
#include <string.h>
#include "version.h"
@ -131,43 +132,9 @@ int ncvisual_setplane(ncvisual* ncv, ncplane* n){
return ret;
}
int ncvisual_rotate_cw(struct ncvisual* ncv){
if(ncv->data == NULL){
return -1;
}
ncplane* n = ncvisual_plane(ncv);
ncplane* newp = rotate_plane(n);
if(newp == NULL){
return -1;
}
//fprintf(stderr, "stride: %d height: %d width: %d\n", ncv->rowstride, ncv->dstheight, ncv->dstwidth);
assert(ncv->rowstride / 4 >= ncv->dstwidth);
uint32_t* data = static_cast<uint32_t*>(malloc(ncv->dstheight * ncv->dstwidth * 4));
if(data == NULL){
ncplane_destroy(newp);
return -1;
}
// targy <- x, targx <- ncv->dstheight - y - 1
for(int targy = 0 ; targy < ncv->dstwidth ; ++targy){
for(int targx = 0 ; targx < ncv->dstheight ; ++targx){
const int x = targy;
const int y = ncv->dstheight - 1 - targx;
//fprintf(stderr, "CW: %d/%d (%08x) -> %d/%d (stride: %d)\n", y, x, ncv->data[y * (ncv->rowstride / 4) + x], targy, targx, ncv->rowstride);
data[targy * ncv->dstheight + targx] = ncv->data[y * (ncv->rowstride / 4) + x];
//fprintf(stderr, "wrote %08x to %d (%d)\n", data[targy * ncv->dstheight + targx], targy * ncv->dstheight + targx, (targy * ncv->dstheight + targx) * 4);
}
}
int ret = ncplane_destroy(n);
ncvisual_set_data(ncv, data, true);
int tmp = ncv->dstwidth;
ncv->dstwidth = ncv->dstheight;
ncv->dstheight = tmp;
ncv->rowstride = ncv->dstwidth * 4;
ncv->ncp = newp;
return ret;
}
int ncvisual_rotate_ccw(struct ncvisual* ncv){
// pi/2 rads counterclockwise
static int
ncvisual_rotate_ccw(struct ncvisual* ncv){
if(ncv->data == NULL){
return -1;
}
@ -205,6 +172,48 @@ int ncvisual_rotate_ccw(struct ncvisual* ncv){
return ret;
}
int ncvisual_rotate(struct ncvisual* ncv, double rads){
if(rads == -M_PI / 2){
return ncvisual_rotate_ccw(ncv);
}
if(rads != M_PI / 2){
return -1;
}
if(ncv->data == NULL){
return -1;
}
ncplane* n = ncvisual_plane(ncv);
ncplane* newp = rotate_plane(n);
if(newp == NULL){
return -1;
}
//fprintf(stderr, "stride: %d height: %d width: %d\n", ncv->rowstride, ncv->dstheight, ncv->dstwidth);
assert(ncv->rowstride / 4 >= ncv->dstwidth);
uint32_t* data = static_cast<uint32_t*>(malloc(ncv->dstheight * ncv->dstwidth * 4));
if(data == NULL){
ncplane_destroy(newp);
return -1;
}
// targy <- x, targx <- ncv->dstheight - y - 1
for(int targy = 0 ; targy < ncv->dstwidth ; ++targy){
for(int targx = 0 ; targx < ncv->dstheight ; ++targx){
const int x = targy;
const int y = ncv->dstheight - 1 - targx;
//fprintf(stderr, "CW: %d/%d (%08x) -> %d/%d (stride: %d)\n", y, x, ncv->data[y * (ncv->rowstride / 4) + x], targy, targx, ncv->rowstride);
data[targy * ncv->dstheight + targx] = ncv->data[y * (ncv->rowstride / 4) + x];
//fprintf(stderr, "wrote %08x to %d (%d)\n", data[targy * ncv->dstheight + targx], targy * ncv->dstheight + targx, (targy * ncv->dstheight + targx) * 4);
}
}
int ret = ncplane_destroy(n);
ncvisual_set_data(ncv, data, true);
int tmp = ncv->dstwidth;
ncv->dstwidth = ncv->dstheight;
ncv->dstheight = tmp;
ncv->rowstride = ncv->dstwidth * 4;
ncv->ncp = newp;
return ret;
}
ncvisual* ncvisual_from_rgba(notcurses* nc, const void* rgba, int rows,
int rowstride, int cols){
if(rowstride % 4){

View File

@ -1,4 +1,5 @@
#include "main.h"
#include <cmath>
#include <vector>
void RotateCW(struct notcurses* nc, struct ncplane* n) {
@ -113,16 +114,16 @@ TEST_CASE("Rotate") {
REQUIRE(ncv);
CHECK(dimx * dimy / 8 <= ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_cw(ncv));
CHECK(0 == ncvisual_rotate(ncv, M_PI/2));
CHECK(dimx * dimy / 8 <= ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_cw(ncv));
CHECK(0 == ncvisual_rotate(ncv, M_PI/2));
CHECK(dimx * dimy / 8 <= ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_cw(ncv));
CHECK(0 == ncvisual_rotate(ncv, M_PI/2));
CHECK(dimx * dimy / 8 <= ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_cw(ncv));
CHECK(0 == ncvisual_rotate(ncv, M_PI/2));
CHECK(dimx * dimy / 8 <= ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
@ -135,16 +136,16 @@ TEST_CASE("Rotate") {
REQUIRE(ncv);
CHECK(dimx * dimy / 4 == ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_ccw(ncv));
CHECK(0 == ncvisual_rotate(ncv, -M_PI/2));
CHECK(dimx * dimy / 4 == ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_ccw(ncv));
CHECK(0 == ncvisual_rotate(ncv, -M_PI/2));
CHECK(dimx * dimy / 4 == ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_ccw(ncv));
CHECK(0 == ncvisual_rotate(ncv, -M_PI/2));
CHECK(dimx * dimy / 4 == ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_rotate_ccw(ncv));
CHECK(0 == ncvisual_rotate(ncv, -M_PI/2));
CHECK(dimx * dimy / 4 == ncvisual_render(ncv, 0, 0, -1, -1));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);