diff --git a/python/main.py b/python/main.py index eed2d85..c23afb1 100644 --- a/python/main.py +++ b/python/main.py @@ -1,30 +1,6 @@ from scene import Scene - -white = 63 -fire1 = 6 -fire2 = 5 -fire3 = 4 -fire4 = 3 -fire5 = 2 -fire6 = 56 - -water1 = 31 -water2 = 30 -water3 = 29 -water4 = 28 - - -fireGradient = [white, white, fire1, fire1, fire2, fire3, fire4, fire3, fire4, fire5, fire6, fire6, fire6] -waterGradient = [white, white, water1, water2, water3, water4, water4] - -class Particle: - def __init__(self, x, y, velX, velY): - self.x = x - self.y = y - self.velX = velX - self.velY = velY - self.c = fireGradient[0] - self.size = 5 +from particles import Particles +from triangles import Triangles class Palette(Scene): @@ -43,102 +19,10 @@ class Palette(Scene): text(str(j * 8 + i), 11 + (i * 20), 10 + (j * 20), 63) -class Main(Scene): - - def __init__(self): - super().__init__() - self.fireParts = [] - self.waterParts = [] - for i in range(10): - self.fireParts.append(Particle(width/2, height/2, rnd(-1, 1), rnd(-1, 1))) - self.waterParts.append(Particle(width/2, height/2, rnd(-1, 1), rnd(-1, 1))) - - self.counter = 0 +scenes = [Particles(), Triangles(), Palette()] +current_scene = 0 - def update(self): - global mouseVelX - global mouseVelY - - self.counter += 1 - - for p in self.fireParts[:]: - p.x += p.velX - p.y += p.velY - p.velX += rnd(-0.3,0.3) - p.velY += rnd(-0.3,0.3) - - idx = int(p.size / 5.0 * len(fireGradient) - 1) - - p.c = fireGradient[(len(fireGradient) - 1) - idx] - - if(p.size < 1): - p.size -= 0.01 - else: - p.size -= 0.05 - if(p.size < 0): - self.fireParts.remove(p) - - for p in self.waterParts[:]: - p.x += p.velX - p.y += p.velY - p.velX += rnd(-0.3,0.3) - p.velY += rnd(-0.3,0.3) - - idx = int(p.size / 5.0 * len(waterGradient) - 1) - - p.c = waterGradient[(len(waterGradient) - 1) - idx] - - if(p.size < 1): - p.size -= 0.01 - else: - p.size -= 0.05 - if(p.size < 0): - self.waterParts.remove(p) - - - - if(mouse(0)): - rad = sin(self.counter * 0.01) * 50 - x1 = mouseX + (sin(self.counter * 0.1) * rad) - y1 = mouseY + (cos(self.counter * 0.1) * rad) - - x2 = mouseX + (sin(self.counter * 0.1 + (3.141592653)) * rad) - y2 = mouseY + (cos(self.counter * 0.1 + (3.141592653)) * rad) - for i in range(10): - self.fireParts.append(Particle(x1, y1, rnd(-1,1) + mouseVelX * 0.25, rnd(-1,1) + mouseVelY * 0.25)) - self.waterParts.append(Particle(x2, y2, rnd(-1,1) + mouseVelX * 0.25, rnd(-1,1) + mouseVelY * 0.25)) - - def draw(self): - clear(0) - for p in self.fireParts: - if(p.size <= 1): - pixel(p.x, p.y, p.c) - else: - circle(p.x, p.y, int(p.size), p.c) - - for p in self.waterParts: - if(p.size <= 1): - pixel(p.x, p.y, p.c) - else: - circle(p.x, p.y, int(p.size), p.c) - text("C to add particles, T to toggle palette", 3, 3, 59) - text("C to add particles, T to toggle palette", 2, 2, 31) - text("Num Particles: " + str(len(self.fireParts) + len(self.waterParts)), 3, 16, 59) - text("Num Particles: " + str(len(self.fireParts) + len(self.waterParts)), 2, 15, 30) - text("FPS: " + str(fps()), 3, 29, 59) - text("FPS: " + str(fps()), 2, 28, 29) - - -paletteScene = Palette() -mainScene = Main() -current_scene = mainScene - -oldMouseX = 0 -oldMouseY = 0 - -mouseVelX = 0 -mouseVelY = 0 KEY_T = 84 @@ -147,30 +31,21 @@ switch = False def update(): global switch global current_scene - global mouseVelX - global mouseVelY - global oldMouseX - global oldMouseY - - mouseVelX = (mouseX - oldMouseX) - mouseVelY = (mouseY - oldMouseY) - - oldMouseX = mouseX - oldMouseY = mouseY - if(keyp(KEY_T)): - switch = not switch - if(switch): - current_scene = paletteScene - else: - current_scene = mainScene + current_scene += 1 + if(current_scene >= len(scenes)): + current_scene = 0 if(mousep(1)): - current_scene.paused = not current_scene.paused + scenes[current_scene].paused = not scenes[current_scene].paused + + if(not scenes[current_scene].paused): + scenes[current_scene].update() + scenes[current_scene].draw() + + text(scenes[current_scene].title, 2, 15, 63) + + - if(not current_scene.paused): - current_scene.update() - current_scene.draw() - circle(mouseX, mouseY, 3, 30) diff --git a/python/particles.py b/python/particles.py new file mode 100644 index 0000000..b5a6320 --- /dev/null +++ b/python/particles.py @@ -0,0 +1,136 @@ +from scene import Scene +from triangles import draw_line + +white = 63 +fire1 = 6 +fire2 = 5 +fire3 = 4 +fire4 = 3 +fire5 = 2 +fire6 = 56 + +water1 = 31 +water2 = 30 +water3 = 29 +water4 = 28 + +fireGradient = [white, white, fire1, fire1, fire2, fire3, fire4, fire3, fire4, fire5, fire6, fire6, fire6] +waterGradient = [white, white, water1, water2, water3, water4, water4] + +oldMouseX = 0 +oldMouseY = 0 + +mouseVelX = 0 +mouseVelY = 0 + + +def tri(x1,y1,x2,y2,x3,y3,c): + draw_line(x1,y1,x2,y2,c) + draw_line(x2,y2,x3,y3,c) + draw_line(x1,y1,x3,y3,c) + +class Particle: + def __init__(self, x, y, velX, velY): + self.x = x + self.y = y + self.velX = velX + self.velY = velY + self.c = fireGradient[0] + self.size = 5 + +class Particles(Scene): + + def __init__(self): + super().__init__() + self.fireParts = [] + self.waterParts = [] + for i in range(10): + self.fireParts.append(Particle(width/2, height/2, rnd(-1, 1), rnd(-1, 1))) + self.waterParts.append(Particle(width/2, height/2, rnd(-1, 1), rnd(-1, 1))) + + self.counter = 0 + + + def update(self): + global mouseVelX + global mouseVelY + + global oldMouseX + global oldMouseY + + mouseVelX = (mouseX - oldMouseX) + mouseVelY = (mouseY - oldMouseY) + + oldMouseX = mouseX + oldMouseY = mouseY + + self.counter += 1 + + for p in self.fireParts[:]: + p.x += p.velX + p.y += p.velY + p.velX += rnd(-0.3,0.3) + p.velY += rnd(-0.3,0.3) + + idx = int(p.size / 5.0 * len(fireGradient) - 1) + + p.c = fireGradient[(len(fireGradient) - 1) - idx] + + if(p.size < 1): + p.size -= 0.01 + else: + p.size -= 0.05 + if(p.size < 0): + self.fireParts.remove(p) + + for p in self.waterParts[:]: + p.x += p.velX + p.y += p.velY + p.velX += rnd(-0.3,0.3) + p.velY += rnd(-0.3,0.3) + + idx = int(p.size / 5.0 * len(waterGradient) - 1) + + p.c = waterGradient[(len(waterGradient) - 1) - idx] + + if(p.size < 1): + p.size -= 0.01 + else: + p.size -= 0.05 + if(p.size < 0): + self.waterParts.remove(p) + + + + if(mouse(0)): + rad = sin(self.counter * 0.01) * 50 + x1 = mouseX + (sin(self.counter * 0.1) * rad) + y1 = mouseY + (cos(self.counter * 0.1) * rad) + + x2 = mouseX + (sin(self.counter * 0.1 + (3.141592653)) * rad) + y2 = mouseY + (cos(self.counter * 0.1 + (3.141592653)) * rad) + for i in range(10): + self.fireParts.append(Particle(x1, y1, rnd(-1,1) + mouseVelX * 0.25, rnd(-1,1) + mouseVelY * 0.25)) + self.waterParts.append(Particle(x2, y2, rnd(-1,1) + mouseVelX * 0.25, rnd(-1,1) + mouseVelY * 0.25)) + + def draw(self): + clear(0) + for p in self.fireParts: + if(p.size <= 1): + pixel(p.x, p.y, p.c) + else: + circle(p.x, p.y, int(p.size), p.c) + + for p in self.waterParts: + if(p.size <= 1): + pixel(p.x, p.y, p.c) + else: + circle(p.x, p.y, int(p.size), p.c) + text("C to add particles, T to toggle palette", 3, 3, 59) + text("C to add particles, T to toggle palette", 2, 2, 31) + text("Num Particles: " + str(len(self.fireParts) + len(self.waterParts)), 3, 16, 59) + text("Num Particles: " + str(len(self.fireParts) + len(self.waterParts)), 2, 15, 30) + text("FPS: " + str(fps()), 3, 29, 59) + text("FPS: " + str(fps()), 2, 28, 29) + + diff --git a/python/scene.py b/python/scene.py index 350070a..0ea6022 100644 --- a/python/scene.py +++ b/python/scene.py @@ -2,6 +2,7 @@ class Scene: def __init__(self): self.paused = False + self.title = "" def update(self): pass diff --git a/python/triangles.py b/python/triangles.py new file mode 100644 index 0000000..ade8996 --- /dev/null +++ b/python/triangles.py @@ -0,0 +1,117 @@ +from scene import Scene + +cols = [(6,8),(16,17),(31,30),(5,4),(41,40),(51,50)] + +def draw_line(x0, y0, x1, y1, c): + x0 = int(x0) + y0 = int(y0) + x1 = int(x1) + y1 = int(y1) + "Bresenham's line algorithm" + dx = abs(x1 - x0) + dy = abs(y1 - y0) + x, y = x0, y0 + sx = -1 if x0 > x1 else 1 + sy = -1 if y0 > y1 else 1 + if dx > dy: + err = dx / 2.0 + while x != x1: + pixel(x, y, c) + err -= dy + if err < 0: + y += sy + err += dx + x += sx + else: + err = dy / 2.0 + while y != y1: + pixel(x, y, c) + err -= dx + if err < 0: + x += sx + err += dy + y += sy + pixel(x, y, c) + +def lineTri(x1,y1,x2,y2,x3,y3,c): + draw_line(x1,y1,x2,y2,c) + draw_line(x2,y2,x3,y3,c) + draw_line(x1,y1,x3,y3,c) + + + +class spinTri(): + def __init__(self): + self.x = rnd(0,width) + self.y = 240 + self.r = 4 + rnd(0, 4) + self.t = rnd(0, 1) + self.a = rnd(0, 1) + self.vy = -0.5-rnd(0,1) + self.c = int(rnd(0, len(cols))) + self.colidx = 0 + self.count = rnd(0,50) + + +class Triangles(Scene): + def __init__(self): + super().__init__() + self.p = [] + self.t = 0.0 + self.count = 0 + self.tpo3 = (2 * 3.141592654)/3 + self.skip = False + + + + def draw(self): + self.skip = not self.skip + if(self.skip): + return + self.t += 0.01 + self.count += 1 + + for i in range(999): + x = rnd(0, width) + y = rnd(0, height) + c = 0 + if(rnd(0,9) < 5): + c = get_pixel(x,y-3) + else: + c = 0 + + circle(x, y, 1, c) + + for pt in self.p[:]: + pt.y += pt.vy + pt.t += 0.05 + pt.a += 0.1 + pt.count += 1 + + r = (0.8 + 0.2 * cos(pt.t * 8)) * pt.r + x1 = pt.x + r * cos(pt.a) + y1 = pt.y + r * sin(pt.a) + x2 = pt.x + r * cos(pt.a + self.tpo3) + y2 = pt.y + r * sin(pt.a + self.tpo3) + x3 = pt.x + r * cos(pt.a + self.tpo3 * 2) + y3 = pt.y + r * sin(pt.a + self.tpo3 * 2) + + if(int(pt.count) % 5 == 0): + pt.colidx += 1 + if(pt.colidx >= len(cols)): + pt.colidx = 0 + light, dark = cols[pt.colidx] + triangle(x1, y1, x2, y2, x3, y3, light) + lineTri(x1, y1, x2, y2, x3, y3, dark) + + if(pt.y < - 40): + self.p.remove(pt) + + #if(self.count % 3 == 0): + #for i in range(1): + self.p.append(spinTri()) + # if(mousep(0)): + # self.p.append(spinTri()) + + + \ No newline at end of file diff --git a/src/Graphics/Graphics.cpp b/src/Graphics/Graphics.cpp index ee85ec9..d929d3f 100644 --- a/src/Graphics/Graphics.cpp +++ b/src/Graphics/Graphics.cpp @@ -131,34 +131,37 @@ void Graphics::toggleFullScreen() { } void Graphics::bindMethods(pkpy::VM *vm) { -// vm->bind(vm->builtins, "clear(color: int)", reinterpret_cast(Clear)); -// vm->bind(vm->builtins, "pixel(x: int, y: int, color: int)", reinterpret_cast(Pixel)); -// vm->bind(vm->builtins, "circle(x: int, y: int, radius: float, color: int)", reinterpret_cast(Circle)); - - vm->bind(vm->builtins, "clear(color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){{ + vm->bind(vm->builtins, "clear(color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ int index = pkpy::py_cast(vm, args[0]); Clear(index); return vm->None; - }}); + }); - vm->bind(vm->builtins, "pixel(x: int, y: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){{ + vm->bind(vm->builtins, "pixel(x: int, y: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ float x = pkpy::py_cast(vm, args[0]); float y = pkpy::py_cast(vm, args[1]); float paletteIndex = pkpy::py_cast(vm, args[2]); this->Pixel(x, y, paletteIndex); return vm->None; - }}); + }); - vm->bind(vm->builtins, "circle(x: int, y: int, radius: float, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){{ + vm->bind(vm->builtins, "get_pixel(x: int, y: int) -> int", [this](pkpy::VM* vm, pkpy::ArgsView args){ + float x = pkpy::py_cast(vm, args[0]); + float y = pkpy::py_cast(vm, args[1]); + + return pkpy::py_var(vm, this->GetPixel(x, y)); + }); + + vm->bind(vm->builtins, "circle(x: int, y: int, radius: float, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ float x = pkpy::py_cast(vm, args[0]); float y = pkpy::py_cast(vm, args[1]); float radius = pkpy::py_cast(vm, args[2]); float paletteIndex = pkpy::py_cast(vm, args[3]); this->Circle(x, y, radius, paletteIndex); return vm->None; - }}); + }); - vm->bind(vm->builtins, "rectangle(x: int, y: int, width: int, height: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){{ + vm->bind(vm->builtins, "rectangle(x: int, y: int, width: int, height: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ float x = pkpy::py_cast(vm, args[0]); float y = pkpy::py_cast(vm, args[1]); float width = pkpy::py_cast(vm, args[2]); @@ -166,7 +169,19 @@ void Graphics::bindMethods(pkpy::VM *vm) { float paletteIndex = pkpy::py_cast(vm, args[4]); this->Rect(x, y, width, height, paletteIndex); return vm->None; - }}); + }); + + vm->bind(vm->builtins, "triangle(x1: int, y1: int, x2: int, y2: int, x3: int, y3: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ + float x1 = pkpy::py_cast(vm, args[0]); + float y1 = pkpy::py_cast(vm, args[1]); + float x2 = pkpy::py_cast(vm, args[2]); + float y2 = pkpy::py_cast(vm, args[3]); + float x3 = pkpy::py_cast(vm, args[4]); + float y3 = pkpy::py_cast(vm, args[5]); + float paletteIndex = pkpy::py_cast(vm, args[6]); + this->Triangle(x1, y1, x2, y2, x3, y3, paletteIndex); + return vm->None; + }); vm->bind(vm->builtins, "text(t: string, x: int, y: int, color: int)", [this](pkpy::VM* vm, pkpy::ArgsView args){ pkpy::PyObject* func_str = vm->builtins->attr("str"); @@ -342,6 +357,104 @@ void Graphics::v_line(int x, int y1, int y2, int paletteIndex) { } } +int Graphics::GetPixel(int x, int y) { + if(x < 0 || y < 0 || x >= m_screenWidth || y >= m_screenHeight) return 0; + + return m_virtualScreenColorBuffer[y * m_screenWidth + x]; +} + +void Graphics::fillBottomFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex) +{ + float invslope1 = (x2 - x1) / (y2 - y1); + float invslope2 = (x3 - x1) / (y3 - y1); + + float curx1 = x1; + float curx2 = x1; + + for (int scanlineY = y1; scanlineY <= y2; scanlineY++) + { + h_line((int)curx1, scanlineY, (int)curx2, paletteIndex); + curx1 += invslope1; + curx2 += invslope2; + } +} + +void Graphics::fillTopFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex) +{ + float invslope1 = (x3 - x1) / (y3 - y1); + float invslope2 = (x3 - x2) / (y3 - y2); + + float curx1 = x3; + float curx2 = x3; + + for (int scanlineY = y3; scanlineY > y1; scanlineY--) + { + h_line((int)curx1, scanlineY, (int)curx2, paletteIndex); + curx1 -= invslope1; + curx2 -= invslope2; + } +} + + +void Graphics::Triangle(int x1, int y1, int x2, int y2, int x3, int y3, int paletteIndex) +{ + int aX = x1; + int aY = y1; + int bX = x2; + int bY = y2; + int cX = x3; + int cY = y3; + + if(bY < aY){ + int t = bY; + bY = aY; + aY = t; + t = bX; + bX = aX; + aX = t; + } + + if(cY < aY){ + int t = cY; + cY = aY; + aY = t; + t = cX; + cX = aX; + aX = t; + } + + if(cY < bY){ + int t = cY; + cY = bY; + bY = t; + t = cX; + cX = bX; + bX = t; + } + + //std::cout << aX << " " << aY << " " << bX << " " << bY << " " << cX << " " << cY << "\n"; + + /* here we know that v1.y <= v2.y <= v3.y */ + /* check for trivial case of bottom-flat triangle */ + if (bY == cY) + { + fillBottomFlatTriangle(aX, aY, bX, bY, cX, cY, paletteIndex); + } + /* check for trivial case of top-flat triangle */ + else if (aY == bY) + { + fillTopFlatTriangle(aX, aY, bX, bY, cX, cY, paletteIndex); + } + else + { + /* general case - split the triangle in a topflat and bottom-flat one */ + int dX = aX + ((float)(bY - aY) / (float)(cY - aY)) * (cX - aX); + int dY = bY; + + fillBottomFlatTriangle(aX, aY, bX, bY, dX, dY, paletteIndex); + fillTopFlatTriangle(bX, bY, dX, dY, cX, cY, paletteIndex); + } +} diff --git a/src/Graphics/Graphics.h b/src/Graphics/Graphics.h index 146f260..d96ca07 100644 --- a/src/Graphics/Graphics.h +++ b/src/Graphics/Graphics.h @@ -33,7 +33,8 @@ private: void h_line(int x1, int y, int x2, int paletteIndex); void v_line(int x, int y1, int y2, int paletteIndex); - + void fillBottomFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex); + void fillTopFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex); public: // virtual screen int m_screenWidth = 0; @@ -71,6 +72,9 @@ public: void Rect(int x, int y, int width, int height, int paletteIndex); void RectBorder(int x, int y, int width, int height, int paletteIndex); void Text(std::string s, int x, int y, int paletteIndex); + void Triangle(int x1, int y1, int x2, int y2, int x3, int y3, int paletteIndex); + + int GetPixel(int x, int y); }; diff --git a/src/Pycron.cpp b/src/Pycron.cpp index 3a42cfe..79fd60d 100644 --- a/src/Pycron.cpp +++ b/src/Pycron.cpp @@ -68,6 +68,40 @@ void Pycron::bindMethods() { m_vm->bind(m_vm->builtins, "key(keycode: int) -> bool", getKeyDown); m_vm->bind(m_vm->builtins, "mousep(button: int) -> bool", getMousePressed); m_vm->bind(m_vm->builtins, "mouse(button: int) -> bool", getMouseDown); + m_vm->bind(m_vm->builtins, "debug(msg: any) -> None", [](pkpy::VM* vm, pkpy::ArgsView args){ + pkpy::PyObject* func_str = vm->builtins->attr("str"); + pkpy::PyObject* result = vm->call(func_str, args[0]); + std::string s = pkpy::py_cast(vm, result).str(); + + std::cout << s.c_str() << "\n"; + return vm->None; + }); + m_vm->bind(m_vm->builtins, "fmod(a: float, b: float) -> float", [](pkpy::VM* vm, pkpy::ArgsView args){ + float a = pkpy::py_cast(vm, args[0]); + float b = pkpy::py_cast(vm, args[1]); + + float mod; + // Handling negative values + if (a < 0) + mod = -a; + else + mod = a; + if (b < 0) + b = -b; + + // Finding mod by repeated subtraction + + while (mod >= b) + mod = mod - b; + + // Sign of result typically depends + // on sign of a. + if (a < 0) + return pkpy::py_var(vm, -mod); + + return pkpy::py_var(vm, mod); + }); + }