aboutsummaryrefslogtreecommitdiff
path: root/demos/graphics_experiments
diff options
context:
space:
mode:
Diffstat (limited to 'demos/graphics_experiments')
-rw-r--r--demos/graphics_experiments/1.cpp (renamed from demos/graphics_experiments/1/main.cpp)3
-rw-r--r--demos/graphics_experiments/2.cpp312
-rw-r--r--demos/graphics_experiments/3.cpp144
-rw-r--r--demos/graphics_experiments/CMakeLists.txt9
4 files changed, 465 insertions, 3 deletions
diff --git a/demos/graphics_experiments/1/main.cpp b/demos/graphics_experiments/1.cpp
index 926557e7..51411f12 100644
--- a/demos/graphics_experiments/1/main.cpp
+++ b/demos/graphics_experiments/1.cpp
@@ -1,3 +1,6 @@
+// Code referred from
+// https://iq.opengenus.org/bresenham-line-drawining-algorithm/
+
#include "cru/platform/Color.hpp"
#include "cru/platform/bootstrap/Bootstrap.hpp"
#include "cru/platform/graphics/Factory.hpp"
diff --git a/demos/graphics_experiments/2.cpp b/demos/graphics_experiments/2.cpp
new file mode 100644
index 00000000..d61dc9ea
--- /dev/null
+++ b/demos/graphics_experiments/2.cpp
@@ -0,0 +1,312 @@
+// Code referred from
+// https://www.geeksforgeeks.org/scan-line-polygon-filling-using-opengl-c/
+
+#include "cru/platform/Color.hpp"
+#include "cru/platform/bootstrap/Bootstrap.hpp"
+#include "cru/platform/graphics/Factory.hpp"
+#include "cru/platform/graphics/Painter.hpp"
+#include "cru/platform/gui/UiApplication.hpp"
+#include "cru/platform/gui/Window.hpp"
+
+#include <cmath>
+#include <cstdio>
+#include <functional>
+
+#define maxHt 800
+#define maxWd 600
+#define maxVer 10000
+
+// Start from lower left corner
+typedef struct edgebucket {
+ int ymax; // max y-coordinate of edge
+ float xofymin; // x-coordinate of lowest edge point updated only in aet
+ float slopeinverse;
+} EdgeBucket;
+
+typedef struct edgetabletup {
+ // the array will give the scanline number
+ // The edge table (ET) with edges entries sorted
+ // in increasing y and x of the lower end
+
+ int countEdgeBucket; // no. of edgebuckets
+ EdgeBucket buckets[maxVer];
+} EdgeTableTuple;
+
+EdgeTableTuple EdgeTable[maxHt], ActiveEdgeTuple;
+
+// Scanline Function
+void initEdgeTable() {
+ int i;
+ for (i = 0; i < maxHt; i++) {
+ EdgeTable[i].countEdgeBucket = 0;
+ }
+
+ ActiveEdgeTuple.countEdgeBucket = 0;
+}
+
+void printTuple(EdgeTableTuple *tup) {
+ int j;
+
+ if (tup->countEdgeBucket) printf("\nCount %d-----\n", tup->countEdgeBucket);
+
+ for (j = 0; j < tup->countEdgeBucket; j++) {
+ printf(" %d+%.2f+%.2f", tup->buckets[j].ymax, tup->buckets[j].xofymin,
+ tup->buckets[j].slopeinverse);
+ }
+}
+
+void printTable() {
+ int i, j;
+
+ for (i = 0; i < maxHt; i++) {
+ if (EdgeTable[i].countEdgeBucket) printf("\nScanline %d", i);
+
+ printTuple(&EdgeTable[i]);
+ }
+}
+
+/* Function to sort an array using insertion sort*/
+void insertionSort(EdgeTableTuple *ett) {
+ int i, j;
+ EdgeBucket temp;
+
+ for (i = 1; i < ett->countEdgeBucket; i++) {
+ temp.ymax = ett->buckets[i].ymax;
+ temp.xofymin = ett->buckets[i].xofymin;
+ temp.slopeinverse = ett->buckets[i].slopeinverse;
+ j = i - 1;
+
+ while ((temp.xofymin < ett->buckets[j].xofymin) && (j >= 0)) {
+ ett->buckets[j + 1].ymax = ett->buckets[j].ymax;
+ ett->buckets[j + 1].xofymin = ett->buckets[j].xofymin;
+ ett->buckets[j + 1].slopeinverse = ett->buckets[j].slopeinverse;
+ j = j - 1;
+ }
+ ett->buckets[j + 1].ymax = temp.ymax;
+ ett->buckets[j + 1].xofymin = temp.xofymin;
+ ett->buckets[j + 1].slopeinverse = temp.slopeinverse;
+ }
+}
+
+void storeEdgeInTuple(EdgeTableTuple *receiver, int ym, int xm, float slopInv) {
+ // both used for edgetable and active edge table..
+ // The edge tuple sorted in increasing ymax and x of the lower end.
+ (receiver->buckets[(receiver)->countEdgeBucket]).ymax = ym;
+ (receiver->buckets[(receiver)->countEdgeBucket]).xofymin = (float)xm;
+ (receiver->buckets[(receiver)->countEdgeBucket]).slopeinverse = slopInv;
+
+ // sort the buckets
+ insertionSort(receiver);
+
+ (receiver->countEdgeBucket)++;
+}
+
+void storeEdgeInTable(int x1, int y1, int x2, int y2) {
+ float m, minv;
+ int ymaxTS, xwithyminTS, scanline; // ts stands for to store
+
+ if (x2 == x1) {
+ minv = 0.000000;
+ } else {
+ m = ((float)(y2 - y1)) / ((float)(x2 - x1));
+
+ // horizontal lines are not stored in edge table
+ if (y2 == y1) return;
+
+ minv = (float)1.0 / m;
+ printf("\nSlope string for %d %d & %d %d: %f", x1, y1, x2, y2, minv);
+ }
+
+ if (y1 > y2) {
+ scanline = y2;
+ ymaxTS = y1;
+ xwithyminTS = x2;
+ } else {
+ scanline = y1;
+ ymaxTS = y2;
+ xwithyminTS = x1;
+ }
+ // the assignment part is done..now storage..
+ storeEdgeInTuple(&EdgeTable[scanline], ymaxTS, xwithyminTS, minv);
+}
+
+void removeEdgeByYmax(EdgeTableTuple *Tup, int yy) {
+ int i, j;
+ for (i = 0; i < Tup->countEdgeBucket; i++) {
+ if (Tup->buckets[i].ymax == yy) {
+ printf("\nRemoved at %d", yy);
+
+ for (j = i; j < Tup->countEdgeBucket - 1; j++) {
+ Tup->buckets[j].ymax = Tup->buckets[j + 1].ymax;
+ Tup->buckets[j].xofymin = Tup->buckets[j + 1].xofymin;
+ Tup->buckets[j].slopeinverse = Tup->buckets[j + 1].slopeinverse;
+ }
+ Tup->countEdgeBucket--;
+ i--;
+ }
+ }
+}
+
+void updatexbyslopeinv(EdgeTableTuple *Tup) {
+ int i;
+
+ for (i = 0; i < Tup->countEdgeBucket; i++) {
+ (Tup->buckets[i]).xofymin =
+ (Tup->buckets[i]).xofymin + (Tup->buckets[i]).slopeinverse;
+ }
+}
+
+void ScanlineFill(const std::function<void(float x1, float y1, float x2,
+ float y2)> &draw_line) {
+ /* Follow the following rules:
+ 1. Horizontal edges: Do not include in edge table
+ 2. Horizontal edges: Drawn either on the bottom or on the top.
+ 3. Vertices: If local max or min, then count twice, else count
+ once.
+ 4. Either vertices at local minima or at local maxima are drawn.*/
+
+ int i, j, x1, ymax1, x2, ymax2, FillFlag = 0, coordCount;
+
+ // we will start from scanline 0;
+ // Repeat until last scanline:
+ for (i = 0; i < maxHt; i++) // 4. Increment y by 1 (next scan line)
+ {
+ // 1. Move from ET bucket y to the
+ // AET those edges whose ymin = y (entering edges)
+ for (j = 0; j < EdgeTable[i].countEdgeBucket; j++) {
+ storeEdgeInTuple(&ActiveEdgeTuple, EdgeTable[i].buckets[j].ymax,
+ EdgeTable[i].buckets[j].xofymin,
+ EdgeTable[i].buckets[j].slopeinverse);
+ }
+ printTuple(&ActiveEdgeTuple);
+
+ // 2. Remove from AET those edges for
+ // which y=ymax (not involved in next scan line)
+ removeEdgeByYmax(&ActiveEdgeTuple, i);
+
+ // sort AET (remember: ET is presorted)
+ insertionSort(&ActiveEdgeTuple);
+
+ printTuple(&ActiveEdgeTuple);
+
+ // 3. Fill lines on scan line y by using pairs of x-coords from AET
+ j = 0;
+ FillFlag = 0;
+ coordCount = 0;
+ x1 = 0;
+ x2 = 0;
+ ymax1 = 0;
+ ymax2 = 0;
+ while (j < ActiveEdgeTuple.countEdgeBucket) {
+ if (coordCount % 2 == 0) {
+ x1 = (int)(ActiveEdgeTuple.buckets[j].xofymin);
+ ymax1 = ActiveEdgeTuple.buckets[j].ymax;
+ if (x1 == x2) {
+ /* three cases can arrive-
+ 1. lines are towards top of the intersection
+ 2. lines are towards bottom
+ 3. one line is towards top and other is towards bottom
+ */
+ if (((x1 == ymax1) && (x2 != ymax2)) ||
+ ((x1 != ymax1) && (x2 == ymax2))) {
+ x2 = x1;
+ ymax2 = ymax1;
+ }
+
+ else {
+ coordCount++;
+ }
+ }
+
+ else {
+ coordCount++;
+ }
+ } else {
+ x2 = (int)ActiveEdgeTuple.buckets[j].xofymin;
+ ymax2 = ActiveEdgeTuple.buckets[j].ymax;
+
+ FillFlag = 0;
+
+ // checking for intersection...
+ if (x1 == x2) {
+ /*three cases can arrive-
+ 1. lines are towards top of the intersection
+ 2. lines are towards bottom
+ 3. one line is towards top and other is towards bottom
+ */
+ if (((x1 == ymax1) && (x2 != ymax2)) ||
+ ((x1 != ymax1) && (x2 == ymax2))) {
+ x1 = x2;
+ ymax1 = ymax2;
+ } else {
+ coordCount++;
+ FillFlag = 1;
+ }
+ } else {
+ coordCount++;
+ FillFlag = 1;
+ }
+
+ if (FillFlag) {
+ // drawing actual lines...
+
+ draw_line(x1, i, x2, i);
+
+ // printf("\nLine drawn from %d,%d to %d,%d",x1,i,x2,i);
+ }
+ }
+
+ j++;
+ }
+
+ // 5. For each nonvertical edge remaining in AET, update x for new y
+ updatexbyslopeinv(&ActiveEdgeTuple);
+ }
+
+ printf("\nScanline filling complete");
+}
+
+int edges[] = {50, 50, 100, 40, 150, 150, 100, 80, 40, 90, 50, 50};
+
+void drawPolyDino(const std::function<void(float x1, float y1, float x2,
+ float y2)> &draw_line) {
+ for (int i = 0; i < sizeof(edges) / sizeof(edges[0]) - 3; i += 2) {
+ draw_line(edges[i], edges[i + 1], edges[i + 2], edges[i + 3]);
+ storeEdgeInTable(edges[i], edges[i + 1], edges[i + 2],
+ edges[i + 3]); // storage of edges in edge table.
+ }
+}
+
+void drawDino(const std::function<void(float x1, float y1, float x2, float y2)>
+ &draw_line) {
+ initEdgeTable();
+ drawPolyDino(draw_line);
+ printf("\nTable");
+ printTable();
+
+ ScanlineFill(draw_line); // actual calling of scanline filling..
+}
+
+int main(int argc, char **argv) {
+ auto application = cru::platform::bootstrap::CreateUiApplication();
+ auto window = application->CreateWindow();
+
+ auto brush = application->GetGraphicsFactory()->CreateSolidColorBrush(
+ cru::platform::colors::black);
+
+ window->SetClientSize(cru::platform::Size(400, 200));
+
+ window->PaintEvent()->AddHandler([window, &brush](nullptr_t) {
+ auto painter = window->BeginPaint();
+ auto draw_line = [&painter, &brush](float x1, float y1, float x2,
+ float y2) {
+ painter->DrawLine({x1, y1}, {x2, y2}, brush.get(), 1);
+ };
+
+ drawDino(draw_line);
+ });
+
+ window->SetVisibility(cru::platform::gui::WindowVisibilityType::Show);
+
+ return application->Run();
+}
diff --git a/demos/graphics_experiments/3.cpp b/demos/graphics_experiments/3.cpp
new file mode 100644
index 00000000..2526d5dd
--- /dev/null
+++ b/demos/graphics_experiments/3.cpp
@@ -0,0 +1,144 @@
+// Code referred from
+// https://www.geeksforgeeks.org/line-clipping-set-1-cohen-sutherland-algorithm/
+
+// C++ program to implement Cohen Sutherland algorithm
+// for line clipping.
+#include <iostream>
+using namespace std;
+
+// Defining region codes
+const int INSIDE = 0; // 0000
+const int LEFT = 1; // 0001
+const int RIGHT = 2; // 0010
+const int BOTTOM = 4; // 0100
+const int TOP = 8; // 1000
+
+// Defining x_max, y_max and x_min, y_min for
+// clipping rectangle. Since diagonal points are
+// enough to define a rectangle
+const int x_max = 10;
+const int y_max = 8;
+const int x_min = 4;
+const int y_min = 4;
+
+// Function to compute region code for a point(x, y)
+int computeCode(double x, double y)
+{
+ // initialized as being inside
+ int code = INSIDE;
+
+ if (x < x_min) // to the left of rectangle
+ code |= LEFT;
+ else if (x > x_max) // to the right of rectangle
+ code |= RIGHT;
+ if (y < y_min) // below the rectangle
+ code |= BOTTOM;
+ else if (y > y_max) // above the rectangle
+ code |= TOP;
+
+ return code;
+}
+
+// Implementing Cohen-Sutherland algorithm
+// Clipping a line from P1 = (x2, y2) to P2 = (x2, y2)
+void cohenSutherlandClip(double x1, double y1,
+ double x2, double y2)
+{
+ // Compute region codes for P1, P2
+ int code1 = computeCode(x1, y1);
+ int code2 = computeCode(x2, y2);
+
+ // Initialize line as outside the rectangular window
+ bool accept = false;
+
+ while (true) {
+ if ((code1 == 0) && (code2 == 0)) {
+ // If both endpoints lie within rectangle
+ accept = true;
+ break;
+ }
+ else if (code1 & code2) {
+ // If both endpoints are outside rectangle,
+ // in same region
+ break;
+ }
+ else {
+ // Some segment of line lies within the
+ // rectangle
+ int code_out;
+ double x, y;
+
+ // At least one endpoint is outside the
+ // rectangle, pick it.
+ if (code1 != 0)
+ code_out = code1;
+ else
+ code_out = code2;
+
+ // Find intersection point;
+ // using formulas y = y1 + slope * (x - x1),
+ // x = x1 + (1 / slope) * (y - y1)
+ if (code_out & TOP) {
+ // point is above the clip rectangle
+ x = x1 + (x2 - x1) * (y_max - y1) / (y2 - y1);
+ y = y_max;
+ }
+ else if (code_out & BOTTOM) {
+ // point is below the rectangle
+ x = x1 + (x2 - x1) * (y_min - y1) / (y2 - y1);
+ y = y_min;
+ }
+ else if (code_out & RIGHT) {
+ // point is to the right of rectangle
+ y = y1 + (y2 - y1) * (x_max - x1) / (x2 - x1);
+ x = x_max;
+ }
+ else if (code_out & LEFT) {
+ // point is to the left of rectangle
+ y = y1 + (y2 - y1) * (x_min - x1) / (x2 - x1);
+ x = x_min;
+ }
+
+ // Now intersection point x, y is found
+ // We replace point outside rectangle
+ // by intersection point
+ if (code_out == code1) {
+ x1 = x;
+ y1 = y;
+ code1 = computeCode(x1, y1);
+ }
+ else {
+ x2 = x;
+ y2 = y;
+ code2 = computeCode(x2, y2);
+ }
+ }
+ }
+ if (accept) {
+ cout << "Line accepted from " << x1 << ", "
+ << y1 << " to " << x2 << ", " << y2 << endl;
+ // Here the user can add code to display the rectangle
+ // along with the accepted (portion of) lines
+ }
+ else
+ cout << "Line rejected" << endl;
+}
+
+// Driver code
+int main()
+{
+ // First Line segment
+ // P11 = (5, 5), P12 = (7, 7)
+ cohenSutherlandClip(5, 5, 7, 7);
+
+ // Second Line segment
+ // P21 = (7, 9), P22 = (11, 4)
+ cohenSutherlandClip(7, 9, 11, 4);
+
+ // Third Line segment
+ // P31 = (1, 5), P32 = (4, 1)
+ cohenSutherlandClip(1, 5, 4, 1);
+
+ return 0;
+}
+
diff --git a/demos/graphics_experiments/CMakeLists.txt b/demos/graphics_experiments/CMakeLists.txt
index cc0ce99a..2eb99ede 100644
--- a/demos/graphics_experiments/CMakeLists.txt
+++ b/demos/graphics_experiments/CMakeLists.txt
@@ -1,4 +1,7 @@
-add_executable(cru_graphics_experiment_1
- 1/main.cpp
-)
+add_executable(cru_graphics_experiment_1 1.cpp)
target_link_libraries(cru_graphics_experiment_1 PRIVATE cru_demo_base)
+
+add_executable(cru_graphics_experiment_2 2.cpp)
+target_link_libraries(cru_graphics_experiment_2 PRIVATE cru_demo_base)
+
+add_executable(cru_graphics_experiment_3 3.cpp)