SkillAgentSearch skills...

Bsdraw

Source code for 4 principal types of charts/graphs, drawed by fragment and vertex shaders. +Examples

Install / Use

/learn @elijivp/Bsdraw
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

BSDRAW

Source code for 4 principal types of graphs, drawed by fragment and vertex shaders.

  • Technology: Qt widgets, inherits QOpenGLWidget (Qt>=5) or QGLWidget (Qt<5) class.
  • Shaders: generated as source code and compiled after initializeGl stage on first show.
  • Compatibility: tested on qt4.8, qt5.5, qt5.12, qt5.14(win/linux), qt6.3(linux). GLSL version 1.30+
  • Features: fast, cross-platform, universal.
  • Note: main define called BSGLSLVER prepends each shader with string "#version %BSGLSLVER%". All shaders in bsdraw are compatible with glsl 130, but by default BSGLSLVER is not set. So if you have any issues, add DEFINES+=BSGLSLVER=130 in your .pro file. All examples are taken from the project "example".

Overview

DrawIntensity class allows you to create rectangular 2D draws. Inherits DrawQWidget

overview_2d_1.png

This example is supplemented with overpattern feature - shader posteffect over drawed data.

Mechanics: 2d data converts into 2d texture, palette converts into 2d texture; Palette texture applies to data texture, shader overpattern applies to result

<details><summary>Code snippet (see end of the page for includes)</summary><p>
LINES = 14;
SAMPLES = 20;
PORTIONS = 1;

overpattern_t dpms[] = {  
// row 1
overpattern_off(),
overpattern_thrs_minus(OP_CONTOUR, 0.5f, 0.0f, 3),
overpattern_any(OP_CONTOUR, 0.0f, 3),

// row 2
overpattern_any(OP_LINELEFTBOTTOM, 0.0f),
overpattern_any(OP_DOTLEFTBOTTOM, 1.0f, 3),
overpattern_any(OP_LINELEFTBOTTOM, 0.4f, 2),

// row 3
overpattern_any(OP_LINEBOTTOM, 0.0f, 7),
overpattern_any(OP_SQUARES, 0.0f),
overpattern_any(OP_DOTCONTOUR, 0.0f, 3),

// row 4
overpattern_any(OP_CIRCLEBORDERED2, 0.0f, -20),
overpattern_any(OP_CIRCLESMOOTH, 0.0f, 4),
overpattern_thrs_plus(OP_CIRCLESMOOTH, 0.9f, 0.0f),

// row 5
overpattern_thrs_plus(OP_FILL, 0.8f, 0.0f),
overpattern_any(OP_DOT, 0.0f, 4),
overpattern_thrs_minus(OP_SHTRICHL, 0.3f, color3f(0.5f, 0.5f, 0.5f), 1),

// row 6
overpattern_any(OP_CONTOUR, color3f(0.0f, 0.0f, 0.0f),1),
overpattern_any(OP_CONTOUR, color3f(0.3f, 0.3f, 0.3f),1),
overpattern_any(OP_CONTOUR, color3f(1.0f, 1.0f, 1.0f),1)
};

const int countROWS = 6, countCOLUMNS = 3;
DrawQWidget* pdraws[countROWS][countCOLUMNS];
for (unsigned int r=0; r<countROWS; r++)
  for (unsigned int c=0; c<countCOLUMNS; c++)
  {
    pdraws[r][c] = new DrawIntensity(SAMPLES, LINES, PORTIONS);
    pdraws[r][c]->setOverpattern(dpms[r*countCOLUMNS + c]);
    pdraws[r][c]->setScalingLimitsSynced(10); // 1 point now is 10x10 pixels (minimum)
  }
// adding text as overlay for first draw
pdraws[0][0]->ovlPushBack(new OTextColored("Original", CR_XABS_YREL_NOSCALED, 5.0f, 0.9f, 12, 0x00000000, 0x11FFFFFF, 0x00000000));

for (int c=0; c<countCOLUMN; c++)
{
  QVBoxLayout* layV = new QVBoxLayout;
  for (int r=0; r<countROW; r++)
    layV->addWidget(pdraws[r][c]);
  currentHBoxLayout->addLayout(layV);
}

const float* data = someGeneratedData; // at least [LINES x SAMPLES x PORTIONS] of floats
for (int c=0; c<countCOLUMN; c++)
  for (int r=0; r<countROW; r++)
    pdraws[r][c]->setData(data);
</p></details>

DrawDomain class allows you to create rectangular 2D draws with regions of different size. One value on setData() method fills one region. Inherits DrawQWidget

overview_2d_2.png

All draws above have different regions but takes similar data.

Mechanics: full 2d field with region markup converts into 2d texture, region data converts into 1d texture, palette converts into 2d texture; Palette texture applies to region data texture, which applies to field texture

<details><summary>Code snippet (see end of the page for includes)</summary><p>
LINES = 200;
SAMPLES = 200;
PORTIONS = 1;

const int countROWS = 2, countCOLUMNS = 2;
DrawQWidget* pdraws[countROWS][countCOLUMNS];
// 1st row, domain 1: little crosses
{
  DrawDomain* dd = new DrawDomain(SAMPLES, LINES, PORTIONS, false, OR_LRBT, true);
  DIDomain& ddm = *dd->domain();
  const int crossRows = SAMPLES/10;
  const int crossColumns = LINES/10;
  for (int i=0; i<crossRows; i++)
  {
    for (int j=0; j<crossColumns; j++)
    {
      int r = LINES/crossRows/2 + i*LINES/crossRows;
      int c = int(SAMPLES/crossColumns/2 + j*SAMPLES/crossColumns);
      ddm.start();
      ddm.includePixel(r-1, c);
      ddm.includePixel(r, c);
      ddm.includePixel(r+1, c);
      ddm.includePixel(r, c+1);
      ddm.includePixel(r, c-1);
      ddm.finish();
    }
  }
  pdraws[0][0] = dd;
  pdraws[0][0]->setScalingLimitsSynced(2); // 1 point now is 2x2 pixels (minimum)
}
// 1st row, domain 2: chessboard
{
  DrawDomain* dd = new DrawDomain(SAMPLES, LINES, PORTIONS, false, OR_LRBT, true);
  DIDomain& ddm = *dd->domain();
  const int cc = 20;
  for (int r2=0; r2<LINES/cc/2; r2++)
  {
    for (int j=0; j<SAMPLES; j+=cc)
    {
      int r0 = r2*2*cc + ((j / cc) % 2)*cc;
      int rs[] = { r0, r0, r0+cc/2, r0+cc/2 };
      int cs[] = { j, j+cc/2, j, j+cc/2 };
      for (int i=0; i<4; i++)
      {
        ddm.start();
        for (int r=rs[i]; r<rs[i]+cc/2; r++)
          for (int c=cs[i]; c<cs[i]+cc/2; c++)
            ddm.includePixel(r, c);
        ddm.finish();
      }
    }
  }
  pdraws[0][1] = dd;
  pdraws[0][1]->setScalingLimitsSynced(2); // 1 point now is 2x2 pixels (minimum)
}
// 2nd row, domain 1: spiral
{
  DrawDomain* dd = new DrawDomain(SAMPLES, LINES, PORTIONS, false, OR_LRBT, true);
  DIDomain& ddm = *dd->domain();
  const int maxspiral = SAMPLES*6;
  const unsigned int outsider = 18500;
  const double wc = 0.15/(2.0*M_PI);
  for (int i=0; i<maxspiral; i++)
  {
    int y = qRound(LINES/2.0 + outsider*sin((i+1)*wc)/(i+1));
    int x = qRound(SAMPLES/2.0 + outsider*cos((i+1)*wc)/(i+1));
    if (y >= 0 && y < LINES && x >= 0 && x < SAMPLES)
    {
      ddm.start();
        ddm.includePixelFree(y, x);
      ddm.finish();
    }
  }
  pdraws[1][0] = dd;
  pdraws[1][0]->setScalingLimitsSynced(2); // 1 point now is 2x2 pixels (minimum)
}
// 2nd row, domain 2: multispiral
{
  DrawDomain* dd = new DrawDomain(SAMPLES, LINES, PORTIONS, false, OR_LRBT, true);
  DIDomain& ddm = *dd->domain();
  const int maxspiral = 600;
  const unsigned int outsider = 9000;
  const double wc = 5.0/(2.0*M_PI);
  for (int i=0; i<maxspiral; i++)
  {
    int y = qRound(LINES/2.0 + outsider*sin((i+1)*wc)/(i+1)), x = qRound(SAMPLES/2.0 + outsider*cos((i+1)*wc)/(i+1));
    if (y >= 0 && y < LINES && x >= 0 && x < SAMPLES && ddm.isFree(y, x))
    {
      ddm.start();
        ddm.includePixel(y, x);
      ddm.finish();
    }
  }
  pdraws[1][1] = dd;
  pdraws[1][1]->setScalingLimitsSynced(2); // 1 point now is 2x2 pixels (minimum)
}

for (int c=0; c<countCOLUMN; c++)
{
  QVBoxLayout* layV = new QVBoxLayout;
  for (int r=0; r<countROW; r++)
    layV->addWidget(pdraws[r][c]);
  currentHBoxLayout->addLayout(layV);
}

const float* data = someGeneratedData; // at least [LINES x SAMPLES x PORTIONS] of floats
for (int c=0; c<countCOLUMN; c++)
  for (int r=0; r<countROW; r++)
    pdraws[r][c]->setData(data);
</p></details>

Portions its about how many draws/graphs painted in one draw. 2 or more portions in 2D draw means colormeshing (mixing) data in each pixel

overview_2d_3.png

At the bottom of picture DrawRecorder class allows you to create 2D draws from 1D data. Each setData() call appends 1D data array to current 2D image as new line. Inherits DrawQWidget

<details><summary>Code snippet (see end of the page for includes)</summary><p>
LINES = 120;
SAMPLES = 400;
PORTIONS = 3;

const int countDraws= 3;
DrawQWidget* pdraws[countDraws];
pdraws[0] = new DrawIntensity(SAMPLES, LINES/PORTIONS, PORTIONS, OR_LRBT, SP_COLUMN_TB_COLORSPLIT);
pdraws[0]->setDataPalette(&paletteRGB);
pdraws[0]->setDataPaletteDiscretion(true);
pdraws[1] = new DrawIntensity(SAMPLES, LINES, PORTIONS);
pdraws[1]->setDataPalette(&paletteRGB);
pdraws[2] = new DrawRecorder(SAMPLES, LINES, 1000, PORTIONS);
pdraws[2]->setDataPalette(&paletteRGB);

for (int i=0; i<countDraws; i++)
  this->layout()->addWidget(pdraws[i]);

const float* data = someGeneratedData; // at least [LINES x SAMPLES x PORTIONS] of floats
for (int i=0; i<countDraws; i++)
  pdraws[i]->setData(data);
</p></details>

DrawGraph class allows you to create 1D graphs and histograms. Inherits DrawQWidget

Histograms with 3 portions displayed below

overview_1d_histogram_1.png

histograms above differ in the ordering of data draw. Histogram bar separation created by overpattern

<details><summary>Code snippet (see end of the page for includes)</summary><p>
SAMPLES = 80;
PORTIONS = 3;

const int countDraws = 4;
DrawQWidget* pdraws[countDraws];

pdraws[0] = new DrawGraph(SAMPLES, PORTIONS, graphopts_t::goHistogram(PR_VALUEAROUND));
pdraws[0]->setOverpattern(overpattern_thrs_plus(OP_LINELEFTTOP, 0.0f, color3f(0.3f,0.3f,0.3f)));
pdraws[0]->setScalingLimitsSynced(10); // 1 point now is 10x10 pixels (minimum)

pdraws[1] = new DrawGraph(SAMPLES, PORTIONS, graphopts_t::goHistogramCrossMin(PR_VALUEAROUND));
pdraws[1]->setOverpattern(overpattern_thrs_plus(OP_LINELEFTTOP, 0.0f, color3f(0.3f,0.3f,0.3f)));
pdra
View on GitHub
GitHub Stars7
CategoryDevelopment
Updated1y ago
Forks0

Languages

C++

Security Score

75/100

Audited on Jan 7, 2025

No findings