Add Graphics.fill/drawEllipse and move fill/drawCircle to use the same code

This commit is contained in:
Gordon Williams 2018-12-20 18:00:01 +00:00
parent dd76859049
commit 0dc0390e42
6 changed files with 261 additions and 63 deletions

View File

@ -32,6 +32,7 @@
Add setNBCellOn for RAK8211-NB (fix #1581)
Now escape chars <8 as octal, and add escape of vertical tab
Add Graphics.createArrayBuffer(... {interleavex:true}) to allow faster support for P3 LED panels
Add Graphics.fill/drawEllipse and move fill/drawCircle to use the same code
2v00 : Allow changeInterval with large (>32 bit) intervals (fix #1438)
changeInterval now changes the interval immediately when it's called inside the interval it is changing (fix #1440)

View File

@ -244,57 +244,68 @@ void graphicsDrawRect(JsGraphics *gfx, short x1, short y1, short x2, short y2) {
graphicsFillRectDevice(gfx,x1,y2,x1,y1);
}
void graphicsDrawCircle(JsGraphics *gfx, short posX, short posY, short rad) {
graphicsToDeviceCoordinates(gfx, &posX, &posY);
int radY = 0,
radX = rad;
// Decision criterion divided by 2 evaluated at radX=radX, radY=0
int decisionOver2 = 1 - radX;
while (radX >= radY) {
graphicsSetPixelDevice(gfx, radX + posX, radY + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, radY + posX, radX + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, -radX + posX, radY + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, -radY + posX, radX + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, -radX + posX, -radY + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, -radY + posX, -radX + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, radX + posX, -radY + posY, gfx->data.fgColor);
graphicsSetPixelDevice(gfx, radY + posX, -radX + posY, gfx->data.fgColor);
radY++;
if (decisionOver2 <= 0) {
// Change in decision criterion for radY -> radY+1
decisionOver2 += 2 * radY + 1;
}
else {
radX--;
// Change for radY -> radY+1, radX -> radX-1
decisionOver2 += 2 * (radY - radX) + 1;
void graphicsDrawEllipse(JsGraphics *gfx, short posX1, short posY1, short posX2, short posY2){
graphicsToDeviceCoordinates(gfx, &posX1, &posY1);
graphicsToDeviceCoordinates(gfx, &posX2, &posY2);
int posX = (posX1+posX2)/2;
int posY = (posY1+posY2)/2;
int width = (posX2-posX1)/2;
int height = (posY2-posY1)/2;
if (width<0) width=-width;
if (height<0) height=-height;
int hh = height * height;
int ww = width * width;
int hhww = hh * ww;
int x0 = width;
int dx = 0;
int y;
graphicsSetPixelDevice(gfx,posX - width,posY,gfx->data.fgColor);
graphicsSetPixelDevice(gfx,posX + width,posY,gfx->data.fgColor);
for (y = 1; y <= height; y++) {
int x1 = x0 - (dx - 1);
for(; x1> 0; x1--)
if (x1 * x1 * hh + y * y * ww <= hhww)
break;
dx = x0 - x1;
x0 = x1;
if(dx<2){
graphicsSetPixelDevice(gfx,posX - x0, posY - y,gfx->data.fgColor);
graphicsSetPixelDevice(gfx,posX + x0, posY - y,gfx->data.fgColor);
graphicsSetPixelDevice(gfx,posX - x0, posY + y,gfx->data.fgColor);
graphicsSetPixelDevice(gfx,posX + x0, posY + y,gfx->data.fgColor);
} else {
graphicsFillRectDevice(gfx,posX - x0, posY - y, posX - x0 - dx + 1, posY - y);
graphicsFillRectDevice(gfx,posX + x0, posY - y, posX + x0 + dx - 1, posY - y);
graphicsFillRectDevice(gfx,posX - x0, posY + y, posX - x0 - dx + 1, posY + y);
graphicsFillRectDevice(gfx,posX + x0, posY + y, posX + x0 + dx - 1, posY + y);
}
}
}
void graphicsFillCircle(JsGraphics *gfx, short x, short y, short rad) {
graphicsToDeviceCoordinates(gfx, &x, &y);
int radY = 0;
int decisionOver2 = 1 - rad;
while (rad >= radY) {
graphicsFillRectDevice(gfx, rad + x, radY + y, -rad + x, -radY + y);
graphicsFillRectDevice(gfx, radY + x, rad + y, -radY + x, -rad + y);
graphicsFillRectDevice(gfx, -rad + x, radY + y, rad + x, -radY + y);
graphicsFillRectDevice(gfx, -radY + x, rad + y, radY + x, -rad + y);
radY++;
if (decisionOver2 <= 0){
// Change in decision criterion for radY -> radY+1
decisionOver2 += 2 * radY + 1;
}else{
rad--;
// Change for radY -> radY+1, rad -> rad-1
decisionOver2 += 2 * (radY - rad) + 1;
}
void graphicsFillEllipse(JsGraphics *gfx, short posX1, short posY1, short posX2, short posY2){
graphicsToDeviceCoordinates(gfx, &posX1, &posY1);
graphicsToDeviceCoordinates(gfx, &posX2, &posY2);
int posX = (posX1+posX2)/2;
int posY = (posY1+posY2)/2;
int width = (posX2-posX1)/2;
int height = (posY2-posY1)/2;
if (width<0) width=-width;
if (height<0) height=-height;
int hh = height * height;
int ww = width * width;
int hhww = hh * ww;
int x0 = width;
int dx = 0;
graphicsFillRectDevice(gfx, posX - width, posY, posX + width, posY);
for (int y = 1; y <= height; y++) {
int x1 = x0 - (dx - 1);
for ( ; x1 > 0; x1--)
if (x1*x1*hh + y*y*ww <= hhww)
break;
dx = x0 - x1;
x0 = x1;
graphicsFillRectDevice(gfx, posX - x0, posY - y, posX + x0, posY - y);
graphicsFillRectDevice(gfx, posX - x0, posY + y, posX + x0, posY + y);
}
}

View File

@ -121,8 +121,8 @@ void graphicsClear(JsGraphics *gfx);
void graphicsFillRect(JsGraphics *gfx, short x1, short y1, short x2, short y2);
void graphicsFallbackFillRect(JsGraphics *gfx, short x1, short y1, short x2, short y2); // Simple fillrect - doesn't call device-specific FR
void graphicsDrawRect(JsGraphics *gfx, short x1, short y1, short x2, short y2);
void graphicsDrawCircle(JsGraphics *gfx, short posX, short posY, short rad);
void graphicsFillCircle(JsGraphics *gfx, short x, short y, short rad);
void graphicsDrawEllipse(JsGraphics *gfx, short x, short y, short x2, short y2);
void graphicsFillEllipse(JsGraphics *gfx, short x, short y, short x2, short y2);
void graphicsDrawString(JsGraphics *gfx, short x1, short y1, const char *str);
void graphicsDrawLine(JsGraphics *gfx, short x1, short y1, short x2, short y2);
void graphicsFillPoly(JsGraphics *gfx, int points, short *vertices); // may overwrite vertices...

View File

@ -421,10 +421,10 @@ void jswrap_graphics_clear(JsVar *parent) {
"name" : "fillRect",
"generate" : "jswrap_graphics_fillRect",
"params" : [
["x1","int32","The left"],
["y1","int32","The top"],
["x2","int32","The right"],
["y2","int32","The bottom"]
["x1","int32","The left X coordinate"],
["y1","int32","The top Y coordinate"],
["x2","int32","The right X coordinate"],
["y2","int32","The bottom Y coordinate"]
]
}
Fill a rectangular area in the Foreground Color
@ -441,10 +441,10 @@ void jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2) {
"name" : "drawRect",
"generate" : "jswrap_graphics_drawRect",
"params" : [
["x1","int32","The left"],
["y1","int32","The top"],
["x2","int32","The right"],
["y2","int32","The bottom"]
["x1","int32","The left X coordinate"],
["y1","int32","The top Y coordinate"],
["x2","int32","The right X coordinate"],
["y2","int32","The bottom Y coordinate"]
]
}
Draw an unfilled rectangle 1px wide in the Foreground Color
@ -470,9 +470,7 @@ void jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2) {
Draw a filled circle in the Foreground Color
*/
void jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return;
graphicsFillCircle(&gfx, (short)x,(short)y,(short)rad);
graphicsSetVar(&gfx); // gfx data changed because modified area
jswrap_graphics_fillEllipse(parent, x-rad, y-rad, x+rad, y+rad);
}
/*JSON{
@ -490,12 +488,52 @@ Draw a filled circle in the Foreground Color
Draw an unfilled circle 1px wide in the Foreground Color
*/
void jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return;
graphicsDrawCircle(&gfx, (short)x,(short)y,(short)rad);
graphicsSetVar(&gfx); // gfx data changed because modified area
jswrap_graphics_drawEllipse(parent, x-rad, y-rad, x+rad, y+rad);
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "fillEllipse",
"ifndef" : "SAVE_ON_FLASH",
"generate" : "jswrap_graphics_fillEllipse",
"params" : [
["x1","int32","The left X coordinate"],
["y1","int32","The top Y coordinate"],
["x2","int32","The right X coordinate"],
["y2","int32","The bottom Y coordinate"]
]
}
Draw a filled ellipse in the Foreground Color
*/
void jswrap_graphics_fillEllipse(JsVar *parent, int x, int y, int x2, int y2) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return;
graphicsFillEllipse(&gfx, (short)x,(short)y,(short)x2,(short)y2);
graphicsSetVar(&gfx); // gfx data changed because modified area
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "drawEllipse",
"ifndef" : "SAVE_ON_FLASH",
"generate" : "jswrap_graphics_drawEllipse",
"params" : [
["x1","int32","The left X coordinate"],
["y1","int32","The top Y coordinate"],
["x2","int32","The right X coordinate"],
["y2","int32","The bottom Y coordinate"]
]
}
Draw an ellipse in the Foreground Color
*/
void jswrap_graphics_drawEllipse(JsVar *parent, int x, int y, int x2, int y2) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return;
graphicsDrawEllipse(&gfx, (short)x,(short)y,(short)x2,(short)y2);
graphicsSetVar(&gfx); // gfx data changed because modified area
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "getPixel",

View File

@ -37,6 +37,8 @@ void jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2);
void jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2);
void jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad);
void jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad);
void jswrap_graphics_drawEllipse(JsVar *parent, int x, int y, int x2, int y2);
void jswrap_graphics_fillEllipse(JsVar *parent, int x, int y, int x2, int y2);
int jswrap_graphics_getPixel(JsVar *parent, int x, int y);
void jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color);
void jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool isForeground);

View File

@ -0,0 +1,146 @@
var g = Graphics.createArrayBuffer(16,16,8);
g.dump = _=>{
var s = "";
var b = new Uint8Array(g.buffer);
var n = 0;
for (var y=0;y<g.getHeight();y++) {
s+="\n";
for (var x=0;x<g.getWidth();x++)
s+=b[n++]?"#":".";
}
return s;
}
g.print = _=>{
print("`"+g.dump()+"`");
}
g.clear();
g.drawEllipse(0,0,14,14);
if (g.dump()!=`
.....#####......
....#.....#.....
...#.......#....
..#.........#...
.#...........#..
.#...........#..
.#...........#..
#.............#.
.#...........#..
.#...........#..
.#...........#..
..#.........#...
...#.......#....
....#.....#.....
.....#####......
................`) throw "drawEllipse circle";
g.clear();
g.fillEllipse(0,0,14,14);
if (g.dump()!=`
.......#........
....#######.....
...#########....
..###########...
.#############..
.#############..
.#############..
###############.
.#############..
.#############..
.#############..
..###########...
...#########....
....#######.....
.......#........
................`) throw "fillEllipse circle";
g.clear();
g.drawEllipse(0,0,6,14);
if (g.dump()!=`
...#............
..#.#...........
.#...#..........
.#...#..........
.#...#..........
.#...#..........
.#...#..........
#.....#.........
.#...#..........
.#...#..........
.#...#..........
.#...#..........
.#...#..........
..#.#...........
...#............
................`) throw "drawEllipse ellipse";
g.clear();
g.fillEllipse(0,0,6,14);
if (g.dump()!=`
...#............
..###...........
.#####..........
.#####..........
.#####..........
.#####..........
.#####..........
#######.........
.#####..........
.#####..........
.#####..........
.#####..........
.#####..........
..###...........
...#............
................`) throw "fillEllipse ellipse";
g.setRotation(1);
g.clear();
g.drawEllipse(0,0,6,14);
if (g.dump()!=`
....#########...
...#.........#..
..#...........#.
.#.............#
..#...........#.
...#.........#..
....#########...
................
................
................
................
................
................
................
................
................`) throw "drawEllipse ellipse rotated";
g.clear();
g.fillEllipse(0,0,6,14);
if (g.dump()!=`
........#.......
...###########..
..#############.
.###############
..#############.
...###########..
........#.......
................
................
................
................
................
................
................
................
................`) throw "fillEllipse ellipse rotated";
//g.print();
result=1;