[python] Add some useful functions in kandinsky

This commit is contained in:
Laury
2021-03-11 19:47:22 +01:00
parent 6b8db108d2
commit 6fcab3952f
17 changed files with 258 additions and 1 deletions

View File

@@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawCircle = "Draw a circle"
PythonDrawLine = "Draw a line"
PythonDrawString = "Display a text from pixel (x,y)"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
@@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
PythonFillCircle = "Fill a circle"
PythonFillPolygon = "Fill a polygon"
PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawCircle = "Draw a circle"
PythonDrawLine = "Draw a line"
PythonDrawString = "Display a text from pixel (x,y)"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
@@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
PythonFillCircle = "Fill a circle"
PythonFillPolygon = "Fill a polygon"
PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawCircle = "Draw a circle"
PythonDrawLine = "Draw a line"
PythonDrawString = "Display a text from pixel (x,y)"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
@@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
PythonFillCircle = "Fill a circle"
PythonFillPolygon = "Fill a polygon"
PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Cosinus hyperbolique"
PythonCount = "Compte les occurrences de x"
PythonDegrees = "Conversion de radians en degrés"
PythonDivMod = "Quotient et reste"
PythonDrawCircle = "Trace un cercle"
PythonDrawLine = "Trace une ligne"
PythonDrawString = "Affiche un texte au pixel (x,y)"
PythonErf = "Fonction d'erreur"
PythonErfc = "Fonction d'erreur complémentaire"
@@ -52,6 +54,8 @@ PythonEval = "Evalue l'expression en argument "
PythonExp = "Fonction exponentielle"
PythonExpm1 = "Calcul de exp(x)-1"
PythonFabs = "Valeur absolue"
PythonFillCircle = "Remplit un cercle"
PythonFillPolygon = "Remplit un polygone"
PythonFillRect = "Remplit un rectangle"
PythonFloat = "Conversion en flottant"
PythonFloor = "Partie entière"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Coseno iperbolico"
PythonCount = "Conta le ricorrenze di x"
PythonDegrees = "Conversione di radianti in gradi"
PythonDivMod = "Quoziente e resto"
PythonDrawCircle = "Disegnare un cerchio"
PythonDrawLine = "Disegna una linea"
PythonDrawString = "Visualizza il testo dal pixel x,y"
PythonErf = "Funzione d'errore"
PythonErfc = "Funzione d'errore complementare"
@@ -52,6 +54,8 @@ PythonEval = "Valuta l'espressione nell'argomento "
PythonExp = "Funzione esponenziale"
PythonExpm1 = "Calcola exp(x)-1"
PythonFabs = "Valore assoluto"
PythonFillCircle = "Riempire un cerchio"
PythonFillPolygon = "Riempire un poligono"
PythonFillRect = "Riempie un rettangolo"
PythonFloat = "Conversione in flottanti"
PythonFloor = "Parte intera"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Cosinus hyperbolicus"
PythonCount = "Tel voorkomen van x"
PythonDegrees = "Zet x om van radialen naar graden"
PythonDivMod = "Quotiënt en rest"
PythonDrawCircle = "Teken een cirkel"
PythonDrawLine = "Teken een lijn"
PythonDrawString = "Geef een tekst weer van pixel (x,y)"
PythonErf = "Error functie"
PythonErfc = "Complementaire error functie"
@@ -52,6 +54,8 @@ PythonEval = "Geef de geëvalueerde uitdrukking"
PythonExp = "Exponentiële functie"
PythonExpm1 = "Bereken exp(x)-1"
PythonFabs = "Absolute waarde"
PythonFillCircle = "Vul een cirkel"
PythonFillPolygon = "Vul een veelhoek"
PythonFillRect = "Vul een rechthoek bij pixel (x,y)"
PythonFloat = "Zet x om in een float"
PythonFloor = "Vloer"

View File

@@ -45,6 +45,8 @@ PythonCosh = "Cosseno hiperbólico"
PythonCount = "Contar as ocorrências de x"
PythonDegrees = "Converter x de radianos para graus"
PythonDivMod = "Quociente e resto"
PythonDrawCircle = "Desenha um círculo"
PythonDrawLine = "Desenhe uma linha"
PythonDrawString = "Mostrar o texto do pixel (x,y)"
PythonErf = "Função erro"
PythonErfc = "Função erro complementar"
@@ -52,6 +54,8 @@ PythonEval = "Devolve a expressão avaliada"
PythonExp = "Função exponencial"
PythonExpm1 = "Calcular exp(x)-1"
PythonFabs = "Valor absoluto"
PythonFillCircle = "Preencher um círculo"
PythonFillPolygon = "Preencher um polígono"
PythonFillRect = "Preencher um retângulo em (x,y)"
PythonFloat = "Converter x num flutuante"
PythonFloor = "Parte inteira"

View File

@@ -50,6 +50,8 @@ PythonCommandCount = "list.count(x)"
PythonCommandCountWithoutArg = ".count(\x11)"
PythonCommandDegrees = "degrees(x)"
PythonCommandDivMod = "divmod(a,b)"
PythonCommandDrawCircle = "draw_circle(x,y,r,color)"
PythonCommandDrawLine = "draw_line(x1,y1,x2,y2,color)"
PythonCommandDrawString = "draw_string(\"text\",x,y)"
PythonCommandConstantE = "e"
PythonCommandErf = "erf(x)"
@@ -59,6 +61,8 @@ PythonCommandExp = "exp(x)"
PythonCommandExpComplex = "exp(z)"
PythonCommandExpm1 = "expm1(x)"
PythonCommandFabs = "fabs(x)"
PythonCommandFillCircle = "fill_circle(x,y,r,color)"
PythonCommandFillPolygon = "fill_polygon([(x1,y1),...],color)"
PythonCommandFillRect = "fill_rect(x,y,width,height,color)"
PythonCommandFloat = "float(x)"
PythonCommandFloor = "floor(x)"

View File

@@ -192,7 +192,11 @@ const ToolboxMessageTree KandinskyModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSetPixel, I18n::Message::PythonSetPixel),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect)
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawLine, I18n::Message::PythonDrawLine),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawCircle, I18n::Message::PythonDrawCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillCircle, I18n::Message::PythonFillCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon)
};
const ToolboxMessageTree IonModuleChildren[] = {
@@ -305,6 +309,8 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCosh, I18n::Message::PythonCosh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDegrees, I18n::Message::PythonDegrees),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDivMod, I18n::Message::PythonDivMod),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawCircle, I18n::Message::PythonDrawCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawLine, I18n::Message::PythonDrawLine),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantE, I18n::Message::PythonConstantE, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandErf, I18n::Message::PythonErf),
@@ -313,6 +319,8 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExp, I18n::Message::PythonExp),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExpm1, I18n::Message::PythonExpm1),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFabs, I18n::Message::PythonFabs),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillCircle, I18n::Message::PythonFillCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloat, I18n::Message::PythonFloat),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloor, I18n::Message::PythonFloor),

View File

@@ -2,8 +2,10 @@ SFLAGS += -Ikandinsky/include
kandinsky_src += $(addprefix kandinsky/src/,\
color.cpp \
context_circle.cpp \
context_line.cpp \
context_pixel.cpp \
context_polygon.cpp \
context_rect.cpp \
context_text.cpp \
font.cpp \

View File

@@ -23,11 +23,19 @@ public:
// Line. Not anti-aliased.
void drawLine(KDPoint p1, KDPoint p2, KDColor c);
// Circle
void drawCircle(KDPoint c, KDCoordinate r, KDColor color);
void fillCircle(KDPoint c, KDCoordinate r, KDColor color);
// Rect
void fillRect(KDRect rect, KDColor color);
void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer);
void blendRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer);
void strokeRect(KDRect rect, KDColor color);
//Polygon
static const int k_polygonMaxNumberOfPoints = 32;
void fillPolygon(KDCoordinate pointsX[], KDCoordinate pointsY[], int numberOfPoints, KDColor color);
protected:
KDContext(KDPoint origin, KDRect clippingRect) :
m_origin(origin),

View File

@@ -0,0 +1,49 @@
#include <kandinsky/context.h>
#include <assert.h>
#include <math.h>
void KDContext::drawCircle(KDPoint c, KDCoordinate r, KDColor color) {
//The used algorithm is the Bresenham's arc tracing algorithm
KDCoordinate x, y, m;
x = 0;
y = r;
m = 5 - 4*r;
while(x <= y) {
setPixel(c.translatedBy(KDPoint(x,y)), color);
setPixel(c.translatedBy(KDPoint(y,x)), color);
setPixel(c.translatedBy(KDPoint(-x,y)), color);
setPixel(c.translatedBy(KDPoint(-y,x)), color);
setPixel(c.translatedBy(KDPoint(x,-y)), color);
setPixel(c.translatedBy(KDPoint(y,-x)), color);
setPixel(c.translatedBy(KDPoint(-x,-y)), color);
setPixel(c.translatedBy(KDPoint(-y,-x)), color);
if(m > 0) {
y = y - 1 ;
m = m - 8*y ;
}
x = x + 1 ;
m = m + 8*x + 4 ;
}
}
void KDContext::fillCircle(KDPoint c, KDCoordinate r, KDColor color) {
KDCoordinate left = c.x()-r;
KDCoordinate right = c.x()+r;
if(left < 0) {
left = 0;
}
if(right > m_clippingRect.width()) {
right = m_clippingRect.width();
}
for(KDCoordinate x=left; x<=right; x++) {
KDCoordinate semiHeight = sqrt((r*r)-((c.x()-x)*(c.x()-x)));
fillRect(KDRect(x, c.y()-semiHeight, 1, semiHeight * 2 ), color);
}
}

View File

@@ -0,0 +1,68 @@
#include <kandinsky/context.h>
#include <assert.h>
#include <math.h>
void KDContext::fillPolygon(KDCoordinate pointsX[], KDCoordinate pointsY[], int numberOfPoints, KDColor color)
{
//The used algorithm is the scan-line algorithm
KDCoordinate top = KDCOORDINATE_MAX;
KDCoordinate bottom = KDCOORDINATE_MIN;
KDCoordinate right = m_clippingRect.width();
KDCoordinate left = 0;
for (int i = 0; i < numberOfPoints; i++) {
if (pointsY[i] < top) {
top = pointsY[i];
}
if (pointsY[i] > bottom) {
bottom = pointsY[i];
}
}
if (top < 0) {
top = 0;
}
if (bottom > m_clippingRect.height()) {
bottom = m_clippingRect.height();
}
for (int y=top; y<=bottom; y++) {
int switches=0;
KDCoordinate switchesX[KDContext::k_polygonMaxNumberOfPoints];
int lastPointIndex = numberOfPoints-1;
for (int i=0; i<numberOfPoints; i++) {
if ((pointsY[i]<y && pointsY[lastPointIndex]>=y) || (pointsY[lastPointIndex]< y && pointsY[i]>=y)) {
switchesX[switches++] = (int) round(pointsX[i]+1.0*(y-pointsY[i])/(pointsY[lastPointIndex]-pointsY[i])*(pointsX[lastPointIndex]-pointsX[i]));
}
lastPointIndex=i;
}
//Sorting switches by a bubble sort
int i=0;
while (i<switches-1) {
if (switchesX[i]>switchesX[i+1]) {
KDCoordinate temp = switchesX[i];
switchesX[i]=switchesX[i+1];
switchesX[i+1]=temp;
if (i) i--;
}
else {
i++;
}
}
for (i=0; i<switches; i+=2) {
if (switchesX[i]>=right) {
break;
}
if (switchesX[i+1]>left) {
fillRect( KDRect( switchesX[ i ] , y, switchesX[ i+1 ] - switchesX[ i ], 1 ), color ) ;
}
}
}
}

View File

@@ -373,8 +373,12 @@ Q(KEY_EXE)
// Kandinsky QSTRs
Q(kandinsky)
Q(color)
Q(draw_line)
Q(draw_string)
Q(draw_circle)
Q(fill_rect)
Q(fill_circle)
Q(fill_polygon)
Q(get_pixel)
Q(set_pixel)

View File

@@ -65,6 +65,34 @@ mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) {
return mp_const_none;
}
mp_obj_t modkandinsky_draw_line(size_t n_args, const mp_obj_t * args) {
mp_int_t x1 = mp_obj_get_int(args[0]);
mp_int_t y1 = mp_obj_get_int(args[1]);
mp_int_t x2 = mp_obj_get_int(args[2]);
mp_int_t y2 = mp_obj_get_int(args[3]);
KDPoint p1 = KDPoint(x1, y1);
KDPoint p2 = KDPoint(x2, y2);
KDColor color = MicroPython::Color::Parse(args[4]);
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
KDIonContext::sharedContext()->drawLine(p1, p2, color);
return mp_const_none;
}
mp_obj_t modkandinsky_draw_circle(size_t n_args, const mp_obj_t * args) {
mp_int_t cx = mp_obj_get_int(args[0]);
mp_int_t cy = mp_obj_get_int(args[1]);
mp_int_t r = mp_obj_get_int(args[2]);
if(r<0)
{
r = -r;
}
KDPoint center = KDPoint(cx, cy);
KDColor color = MicroPython::Color::Parse(args[3]);
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
KDIonContext::sharedContext()->drawCircle(center, r, color);
return mp_const_none;
}
mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) {
mp_int_t x = mp_obj_get_int(args[0]);
mp_int_t y = mp_obj_get_int(args[1]);
@@ -84,3 +112,49 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) {
KDIonContext::sharedContext()->fillRect(rect, color);
return mp_const_none;
}
mp_obj_t modkandinsky_fill_circle(size_t n_args, const mp_obj_t * args) {
mp_int_t cx = mp_obj_get_int(args[0]);
mp_int_t cy = mp_obj_get_int(args[1]);
mp_int_t r = mp_obj_get_int(args[2]);
if(r<0)
{
r = -r;
}
KDPoint center = KDPoint(cx, cy);
KDColor color = MicroPython::Color::Parse(args[3]);
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
KDIonContext::sharedContext()->fillCircle(center, r, color);
return mp_const_none;
}
mp_obj_t modkandinsky_fill_polygon(size_t n_args, const mp_obj_t * args) {
KDCoordinate pointsX[KDContext::k_polygonMaxNumberOfPoints];
KDCoordinate pointsY[KDContext::k_polygonMaxNumberOfPoints];
size_t itemLength;
mp_obj_t * items;
mp_obj_get_array(args[0], &itemLength, &items);
if (itemLength < 3) {
mp_raise_ValueError("polygon must have at least 3 points");
}
else if (itemLength > KDContext::k_polygonMaxNumberOfPoints) {
mp_raise_ValueError("polygon is defined by too many points");
}
for(unsigned int i=0; i<itemLength; i++)
{
mp_obj_t * coordinates;
mp_obj_get_array_fixed_n(items[i], 2, &coordinates);
pointsX[i] = mp_obj_get_int(coordinates[0]);
pointsY[i] = mp_obj_get_int(coordinates[1]);
}
KDColor color = MicroPython::Color::Parse(args[1]);
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
KDIonContext::sharedContext()->fillPolygon(pointsX, pointsY, itemLength, color);
return mp_const_none;
}

View File

@@ -4,4 +4,8 @@ mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y);
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color);
mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_draw_line(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_draw_circle(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_fill_circle(size_t n_args, const mp_obj_t *args);
mp_obj_t modkandinsky_fill_polygon(size_t n_args, const mp_obj_t *args);

View File

@@ -4,7 +4,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_color_obj, 1, 3, modkand
STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel);
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_string_obj, 3, 5, modkandinsky_draw_string);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_line_obj, 5, 5, modkandinsky_draw_line);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_circle_obj, 4, 4, modkandinsky_draw_circle);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_rect_obj, 5, 5, modkandinsky_fill_rect);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_circle_obj, 4, 4, modkandinsky_fill_circle);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_polygon_obj, 2, 2, modkandinsky_fill_polygon);
STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) },
@@ -12,7 +16,11 @@ STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_get_pixel), (mp_obj_t)&modkandinsky_get_pixel_obj },
{ MP_ROM_QSTR(MP_QSTR_set_pixel), (mp_obj_t)&modkandinsky_set_pixel_obj },
{ MP_ROM_QSTR(MP_QSTR_draw_string), (mp_obj_t)&modkandinsky_draw_string_obj },
{ MP_ROM_QSTR(MP_QSTR_draw_line), (mp_obj_t)&modkandinsky_draw_line_obj },
{ MP_ROM_QSTR(MP_QSTR_draw_circle), (mp_obj_t)&modkandinsky_draw_circle_obj },
{ MP_ROM_QSTR(MP_QSTR_fill_rect), (mp_obj_t)&modkandinsky_fill_rect_obj },
{ MP_ROM_QSTR(MP_QSTR_fill_circle), (mp_obj_t)&modkandinsky_fill_circle_obj },
{ MP_ROM_QSTR(MP_QSTR_fill_polygon), (mp_obj_t)&modkandinsky_fill_polygon_obj },
};
STATIC MP_DEFINE_CONST_DICT(modkandinsky_module_globals, modkandinsky_module_globals_table);