Implement fl_complex_polygon() for OpenGL (#570)

This commit is contained in:
Matthias Melcher 2022-12-05 19:27:12 +01:00 committed by GitHub
parent a8923a0fd4
commit 9f92972729
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 7 deletions

View File

@ -162,7 +162,7 @@ protected:
matrix m; ///< current transformation matrix
int n; ///< For internal use by FLTK
int gap_; ///< For internal use by FLTK
enum SHAPE {NONE=0, LINE, LOOP, POLYGON, POINTS} what;
enum SHAPE {NONE=0, LINE, LOOP, POLYGON, POINTS, COMPLEX_POLYGON} what;
int rstackptr; ///< For internal use by FLTK
static const int region_stack_max = FL_REGION_STACK_SIZE - 1; ///< For internal use by FLTK
Fl_Region rstack[FL_REGION_STACK_SIZE]; ///< For internal use by FLTK

View File

@ -26,6 +26,21 @@
#include <FL/gl.h>
#include <FL/math.h>
// OpenGL does not support rednering non-convex polygons. Calling
// glBegin(GL_POLYGON); witha complex outline will create rather random
// errors, often overwrinting gaps and holes.
//
// Defining SLOW_COMPLEX_POLY will activate a line-by-line drawing method
// for complex polygons that is correct for FLTK, but also a lot slower.
//
// It's recommended that SLOW_COMPLEX_POLY is defined, but fl_begin_polygon()
// is used instead of fl_begin_complex_polygon() whenever possible.
//#undef SLOW_COMPLEX_POLY
#define SLOW_COMPLEX_POLY
#ifdef SLOW_COMPLEX_POLY
# define GAP (1e9f)
#endif
// Event though there are faster versions of the functions in OpenGL,
// we use the default FLTK implementation for compatibility in the
@ -41,7 +56,8 @@
// double Fl_OpenGL_Graphics_Driver::transform_dy(double x, double y)
void Fl_OpenGL_Graphics_Driver::begin_points() {
Fl_Graphics_Driver::begin_points();
n = 0; gap_ = 0;
what = POINTS;
glBegin(GL_POINTS);
}
@ -50,7 +66,8 @@ void Fl_OpenGL_Graphics_Driver::end_points() {
}
void Fl_OpenGL_Graphics_Driver::begin_line() {
Fl_Graphics_Driver::begin_line();
n = 0; gap_ = 0;
what = LINE;
glBegin(GL_LINE_STRIP);
}
@ -59,7 +76,8 @@ void Fl_OpenGL_Graphics_Driver::end_line() {
}
void Fl_OpenGL_Graphics_Driver::begin_loop() {
Fl_Graphics_Driver::begin_loop();
n = 0; gap_ = 0;
what = LOOP;
glBegin(GL_LINE_LOOP);
}
@ -68,7 +86,8 @@ void Fl_OpenGL_Graphics_Driver::end_loop() {
}
void Fl_OpenGL_Graphics_Driver::begin_polygon() {
Fl_Graphics_Driver::begin_polygon();
n = 0; gap_ = 0;
what = POLYGON;
glBegin(GL_POLYGON);
}
@ -77,26 +96,141 @@ void Fl_OpenGL_Graphics_Driver::end_polygon() {
}
void Fl_OpenGL_Graphics_Driver::begin_complex_polygon() {
Fl_Graphics_Driver::begin_complex_polygon();
n = 0;
what = COMPLEX_POLYGON;
#ifndef SLOW_COMPLEX_POLY
glBegin(GL_POLYGON);
#endif
}
void Fl_OpenGL_Graphics_Driver::gap() {
#ifdef SLOW_COMPLEX_POLY
// drop gaps at the start or gap after gap
if (n==0 || n==gap_) // || pnVertex==pVertexGapStart)
return;
// create a loop
XPOINT& p = xpoint[gap_];
transformed_vertex(p.x, p.y);
transformed_vertex(GAP, 0.0);
gap_ = n;
#else
glEnd();
glBegin(GL_POLYGON);
#endif
}
#ifdef SLOW_COMPLEX_POLY
// Draw a complex polygon line by line from the top to the bottom.
void Fl_OpenGL_Graphics_Driver::end_complex_polygon()
{
int i, y;
XPOINT *v0, *v1;
// don't bother if no polygon is defined
if (n < 2) return;
// make sure that we always have a closed loop by appending the first
// coordinate again as the alst coordinate
gap();
// find the bounding box for this polygon
v0 = xpoint;
float xMin = v0->x, xMax = xMin;
int yMin = v0->y, yMax = yMin;
for (i = 1; i < n; i++) {
v0++;
if (v0->x == GAP) continue;
if (v0->x <= xMin) xMin = v0->x;
if (v0->x >= xMax) xMax = v0->x;
if (v0->y <= yMin) yMin = v0->y;
if (v0->y >= yMax) yMax = v0->y;
}
int nNodes, j;
float nodeX[n-1], pixelX, swap;
// loop through the rows of the image
for (y = yMin; y < yMax; y++) {
// Build a list of all crossing points with this y axis
XPOINT *v0 = xpoint + 0, *v1 = xpoint + 1;
nNodes = 0;
for (i = 1; i < n; i++) {
j = i-1;
if (v1->x==GAP) { // skip the gap
i++; v0++; v1++;
continue;
}
if ( (v1->y < y && v0->y >= y)
|| (v0->y < y && v1->y >= y) )
{
float dy = v0->y - v1->y;
if (fabsf(dy)>.0001f) {
nodeX[nNodes++] = v1->x + ((y - v1->y) / dy) * (v0->x - v1->x);
} else {
nodeX[nNodes++] = v1->x;
}
}
v0++; v1++;
}
// sort the nodes, via a simple Bubble sort
i = 0;
while (i < nNodes-1) {
if (nodeX[i] > nodeX[i+1]) {
swap = nodeX[i];
nodeX[i] = nodeX[i+1];
nodeX[i+1] = swap;
if (i) i--;
} else {
i++;
}
}
// fill the pixels between node pairs
glBegin(GL_LINES);
for (i = 0; i < nNodes; i += 2) {
float x0 = nodeX[i];
if (x0 >= xMax)
break;
float x1 = nodeX[i+1];
if (x1 > xMin) {
if (x0 < xMin)
x0 = xMin;
if (x1 > xMax)
x1 = xMax;
glVertex2f(x0, y);
glVertex2f(x1, y);
}
}
glEnd();
}
}
#else
// FXIME: non-convex polygons are not supported yet
// use gluTess* functions to do this; search for gluBeginPolygon
void Fl_OpenGL_Graphics_Driver::end_complex_polygon() {
glEnd();
}
#endif
// remove equal points from closed path
void Fl_OpenGL_Graphics_Driver::fixloop() { }
void Fl_OpenGL_Graphics_Driver::transformed_vertex(double xf, double yf) {
#ifdef SLOW_COMPLEX_POLY
if (what==COMPLEX_POLYGON) {
Fl_Graphics_Driver::transformed_vertex(xf, yf);
} else {
glVertex2d(xf, yf);
}
#else
glVertex2d(xf, yf);
#endif
}
void Fl_OpenGL_Graphics_Driver::circle(double cx, double cy, double r) {

View File

@ -418,7 +418,7 @@ void draw_complex(ComplexShapesTest *p) {
fl_color(FL_DARK2);
fl_begin_complex_polygon();
fl_vertex(-w, 0);
fl_curve(-w+3, 0, -w+3, -h, w-3, h, w-3, 0);
fl_curve(-w+3, 0, 0, -h, 0, h, w-3, 0);
fl_vertex(w, 0);
fl_vertex(w, h); fl_vertex(-w, h);
fl_end_complex_polygon();