OpenGL Path Rendering with Offscreen Capture of Multisampled Buffer for Antialiased Image – Updated Code

November 3, 2015 - Leave a Response

Here is the updated code (full c/c++) code compiled using MS Visual C++ 2010

Enjoy,

Gonen

nvpr_industrial_demo

Real working example of OpenGL NVIDIA Path Rendering Extension with Offscreen Rendering and Image Capture

October 28, 2015 - Leave a Response

Hi Guys. I must start with a Thank to Mark Kilgard and his colleagues for NV_Path_Rendering. I started my NVPR journey by reading all the available professional material by NVIDIA, NV_Path_Rendering and Mark Kilgard’s posts, slideshares and example code (search Mark Kilgard github, nvprsdk). In this blog I provide the Full source code of a C/C++ program that needs only GLUT or freeglut and has the following features:

  • On screen rendering (glut window)
  • Off screen rendering using Frame Buffer and Render Buffer Objects (Render Buffers, FBO, Stencil and Depth Support)
  • Capture RGBA 8888 Rendered Images to hard disk
  • Support Affine 2D and Affine 3D transform for a Loaded PATH from file (GL_AFFINE_2D_NV, GL_AFFINE_3D_NV)
  • Support Reading Path objects from FILES in SVG and PostScript format (GL_PATH_FORMAT_SVG_NV, GL_PATH_FORMAT_PS_NV)
  • Support for Enable and Disable Anti aliasing (GL_POLYGON_SMOOTH, GL_LINE_SMOOTH, GL_POINT_SMOOTH)
  • Support for Enable and Disable Multi Sampling (GL_MULTISAMPLE)
  • Enable and Disable Alpha Blending (GL_BLEND)
  • On/Off Path Fill and Stroke
  • Set Path Fill Color
  • Set Path Stroke Color
  • Set Different Path Fill Rules (GL_INVERT, GL_COUNT_UP_NV, GL_COUNT_DOWN_NV, or GL_PATH_FILL_MODE_NV)
  • More Features

PLEASE NOTE: MY CODE IS BASED UPON THE ORIGINAL CODE BY NvprSdk at this HTTP ADDRESS https://github.com/markkilgard/NVprSDK

My code was tested on Windows 7, 32Bit, Visual C++ 2010. Here are some example images:

The command:

-alpha-blending -no-stroke -path-svg demo1.ascii.txt -c 600 600 -fc 1 0 0 0.5 -af3d 1 0 0 0 1 0 0 0 1 110 120 0 -dup -fc 0 0 1 0.8 -af2d 1.1 0 0 0.9 20 20 -dup -offscreen-canvas 600 600 -offscreen -capture imageFileName

Here is the input demo1.ascii.txt file:

M 100,180 L 40,10 L 190,120 L 10,120 L 160,10 z
M 300 300 C 100 400,100 200,300 100,500 200,500 400,300 300 Z

The Image on disk:

imageFileName-600x600x32bpp

Here is the full Source Code (demo.cpp) for your usage:

//
// (Loaded) Path Duplication (Step and Repeat) Example:
// ---------------------------------------------------------
// -no-stroke -path-svg demo1.ascii.txt -fc 1 0 0 0.5 
// -af 1 0 0 1 100 100 -dup -fc 0 1 0 0.5 -af 1 0.1 -0.1 1 100 100 
// -alpha-blending -dup -path-ps demo3.ascii.txt
// ---------------------------------------------------------
// affine 2d example:
// -affine2d 1 0 0 1 0 0
// ---------------------------------------------------------
// affine3d example:
// -affine3d 1 0 0 0 1 0 0 0 1 0 0 0
// ---------------------------------------------------------
// Offscreen and Onscreen render and capture to FILE
// -alpha-blending -no-stroke -path-svg demo1.ascii.txt -c 700 700 
// -fc 1 0 0 0.5 -af3d 1 0 0 0 1 0 0 0 1 110 120 0 -dup 
// -fc 0 0 1 0.8 -af2d 1.1 0 0 0.9 20 20 -dup 
// -offscreen-canvas 400 400 -offscreen -capture imageFileName
//
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __APPLE__
# include <GLUT/glut.h> // Apple puts GLUT header in non-standard location
#else
# include <GL/glut.h>
# ifdef _WIN32
# include <windows.h> // for wglGetProcAddress
# else
# include <GL/glx.h> // for glXGetProcAddress
# endif
#endif
#define GL_MULTISAMPLE 0x809D
#include <GL/megalui.h>
#ifndef GLAPIENTRY
# ifdef _WIN32
// Windows uses x86 Pascal (standard call) rather than C calling conventions for OpenGL entry points
# define GLAPIENTRYP __stdcall *
# else
# define GLAPIENTRYP *
# endif
#endif
// Only include the minimal EXT_direct_state_access API used by this example for brevity.
#ifndef GL_EXT_direct_state_access
typedef void (GLAPIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode);
typedef void (GLAPIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
typedef void (GLAPIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum matrixMode, GLfloat x, GLfloat y, GLfloat z);
typedef void (GLAPIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum matrixMode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
#endif
#ifndef __APPLE__
// Direct state acces (DSA) matrix manipulation
PFNGLMATRIXLOADIDENTITYEXTPROC glMatrixLoadIdentityEXT = NULL;
PFNGLMATRIXORTHOEXTPROC glMatrixOrthoEXT = NULL;
PFNGLMATRIXTRANSLATEFEXTPROC glMatrixTranslatefEXT = NULL;
PFNGLMATRIXROTATEFEXTPROC glMatrixRotatefEXT = NULL;
#endif
static void GL_CALL_CALLBACK() { 
 fprintf(stderr, "GL error\n");
}
#define GL_CALL(x) {x {unsigned int glErrorCode = glGetError(); if (glErrorCode!=GL_NO_ERROR) { fprintf(stderr, "Error: OpenGL Error(%d) After %s\n", glErrorCode, #x);GL_CALL_CALLBACK();}}}
// ASSUMES there is 'unsigned int glErrorCode = 0;' in scope
#define GL_CALL2(x) {x {glErrorCode = glGetError(); if (glErrorCode!=GL_NO_ERROR) { fprintf(stderr, "Error: OpenGL Error(%d) After %s\n", glErrorCode, #x);GL_CALL_CALLBACK();}}}
typedef void (GLAPIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer);
typedef void (GLAPIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer);
typedef void (GLAPIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
typedef GLenum (GLAPIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target);
typedef void (GLAPIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint* framebuffers);
typedef void (GLAPIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint* renderbuffers);
typedef void (GLAPIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef void (GLAPIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (GLAPIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (GLAPIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer);
typedef void (GLAPIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target,GLenum attachment, GLuint texture,GLint level,GLint layer);
typedef void (GLAPIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint* framebuffers);
typedef void (GLAPIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint* renderbuffers);
typedef void (GLAPIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target);
typedef void (GLAPIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint* params);
typedef void (GLAPIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint* params);
typedef GLboolean (GLAPIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer);
typedef GLboolean (GLAPIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer);
typedef void (GLAPIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (GLAPIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = NULL;
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = NULL;
PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = NULL;
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = NULL;
PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL;
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = NULL;
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = NULL;
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = NULL;
PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = NULL;
PFNGLISFRAMEBUFFERPROC glIsFramebuffer = NULL;
PFNGLISRENDERBUFFERPROC glIsRenderbuffer = NULL;
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = NULL;
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL;
// OFFSCREEN RENDERING ENGINE
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
#define GL_RENDERBUFFER 0x8D41
#define GL_FRAMEBUFFER 0x8D40
#define GL_DEPTH_ATTACHMENT 0x8D00
#define GL_STENCIL_ATTACHMENT 0x8D20
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_DEPTH24_STENCIL8 0x88F0
// Implement an Off Screen Render Buffer
class RenderBuffer {
public:
 virtual ~RenderBuffer();
 // Mandatory: call 'onceAfterConstruction' immediately after calling my constructor
 RenderBuffer( const unsigned int W, 
 const unsigned int H, 
 const float zNear, 
 const float zFar, 
 const float fovYAngle, 
 const unsigned int colorAttachmentIndex = GL_COLOR_ATTACHMENT0, 
 const bool enableDepthAndStencilBuffer = true
 );
 virtual int onceAfterConstruction(); // Mandatory: call me immediately after calling my constructor
 // @return an RGBA 32bpp buffer image. User should free this buffer.
 // Return image has size width() * height() * channels()
 // call me before you call off() because I need to be bound to a framebuffer
 unsigned char* getImage(unsigned char *bufferOutput = 0, size_t *outBufferSize = 0) const;
 float* getDepthImage(float *bufferOutput = 0, size_t *outBufferSize = 0) const;
 void on(); // activate me before opengl rendering 
 void off(); // deactivate me after opengl rendering 
 bool isOn() const;
 int getAttachmentIndex() const;
 bool isValid() const;
 virtual unsigned int width() const;
 virtual unsigned int widthstep() const;
 virtual unsigned int height() const;
 virtual unsigned int channels() const;
 virtual unsigned int pixelSizedInternalFormat() const; // Help: http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml
 
protected:
 virtual void reshape(int w, int h, float zNear, float zFar);
 // IMAGE FORMAT
 unsigned int mW, mWidthstep, mH;
 unsigned int mNumChannels;
 unsigned int mPixelSizedInternalFormat; 
 float mZNear, mZFar;
 // OTHER
 unsigned int mFboId;
 unsigned int mRboId; // depth+stencil
 unsigned int mTextureId; 
 unsigned int mColorAttachmentIndex;
 bool mIsOn;
 float mFovYAngle;
 bool mEnableDepthAndStencilBuffer;
};
RenderBuffer::~RenderBuffer() {
 if (mFboId>0)
 GL_CALL(glDeleteFramebuffers(1, &mFboId);)
 if (mTextureId>0)
 GL_CALL(glDeleteTextures(1, &mTextureId);)
 if (mRboId>0)
 GL_CALL(glDeleteRenderbuffers(1, &mRboId);)
}
bool RenderBuffer::isOn() const {
 return mIsOn;
}
RenderBuffer::RenderBuffer(
 const unsigned int W, 
 const unsigned int H, 
 const float zNear, 
 const float zFar, 
 const float fovYAngle, 
 const unsigned int colorAttachmentIndex, 
 const bool enableDepthAndStencilBuffer
 ) 
 : 
 mW(W), mH(H), 
 mZNear(zNear), mZFar(zFar),
 mColorAttachmentIndex(colorAttachmentIndex), mNumChannels(4), 
 mFboId(0), mTextureId(0), mRboId(0), mIsOn(false), 
 mFovYAngle(fovYAngle), mPixelSizedInternalFormat(GL_RGBA8),
 mEnableDepthAndStencilBuffer(enableDepthAndStencilBuffer)
{
 mWidthstep = mW*mNumChannels;
 GL_CALL(glGenTextures(1, &mTextureId);)
 GL_CALL(glBindTexture(GL_TEXTURE_2D, mTextureId);) // bind for definition
 GL_CALL(glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);) // select modulate to mix texture with color for shading
 GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);)
 GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);)
 GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mW, mH, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);)
 GL_CALL(glBindTexture(GL_TEXTURE_2D, 0);)
if (mEnableDepthAndStencilBuffer) {
 // Attach a renderbuffer to depth attachment point
 GL_CALL(glGenRenderbuffers(1, &mRboId);)
 GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, mRboId);)
 GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mW, mH);) 
 GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0);)
 } 
 // create a framebuffer object
 GL_CALL(glGenFramebuffers(1, &mFboId);)
 GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, mFboId);)
 GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, mColorAttachmentIndex, GL_TEXTURE_2D, mTextureId, 0);) // attach the texture to FBO color attachment point
if (mEnableDepthAndStencilBuffer) {
 // Call me only after FBO creation!
 GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mRboId);) 
 }
 // not complete, user must now call onceAfterConstruction()
}
int
RenderBuffer::onceAfterConstruction() {
 // check FBO status
 GLenum status = 0;
 GL_CALL(status = glCheckFramebufferStatus(GL_FRAMEBUFFER);)
 reshape(mW, mH, mZNear, mZFar); // once after creation
 GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0);)
 if (status != GL_FRAMEBUFFER_COMPLETE) {
 mFboId = 0;
 fprintf(stderr, "Error in 'RenderBuffer::onceAfterConstruction': Incomplete Frame Buffer Object\n");
 return 1;
 }
 return 0;
}
// @return an RGBA 32bpp buffer image. User should free this buffer.
// Return image has size width() * height() * channels()
// call me after calling on() and before you call off() because I need to be bound to a framebuffer
unsigned char* RenderBuffer::getImage(unsigned char *bufferOutput, size_t *outBufferSize) const {
 const size_t bufferSize = (size_t)mNumChannels*mW*mH;
 unsigned char *buffer = bufferOutput;
 bool bufferAllocatedHere = false;
 if (!buffer) {
 bufferAllocatedHere = true;
 buffer = (unsigned char*)malloc(bufferSize);
 }
 if (!buffer) {
 fprintf(stderr, "Error in 'RenderBuffer::getImage': Unexpected NULL output buffer\n");
 return NULL;
 }
 unsigned int glErrorCode = 0;
 GL_CALL2(glFinish();)
 GL_CALL2(glReadBuffer(mColorAttachmentIndex);) 
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 GL_CALL2(glPixelStorei(GL_PACK_ALIGNMENT, 1);)
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 GL_CALL2(glReadPixels(0, 0, mW, mH, GL_RGBA, GL_UNSIGNED_BYTE, (void*)buffer);)
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 uiSwapImageVertical(buffer, mW*mNumChannels, mH); // OpenGL Renders Images with origin at Bottom-Left
 if (outBufferSize) {
 *outBufferSize = bufferSize;
 }
 return buffer;
}
float* 
RenderBuffer::getDepthImage(float *bufferOutput, size_t *outBufferSize) const {
 if (!mEnableDepthAndStencilBuffer) {
 return NULL;
 }
 const size_t bufferSize = sizeof(float)*mW*mH;
 float *buffer = bufferOutput;
 bool bufferAllocatedHere = false;
 if (!buffer) {
 bufferAllocatedHere = true;
 buffer = (float*)malloc(bufferSize);
 }
 if (!buffer) {
 fprintf(stderr, "Error in 'RenderBuffer::getImage': Unexpected NULL output buffer\n");
 return NULL;
 }
 unsigned int glErrorCode = 0;
 GL_CALL2(glFinish();)
 GL_CALL2(glBindRenderbuffer(GL_RENDERBUFFER, mRboId);)
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 GL_CALL2(glReadPixels(0, 0, mW, mH, GL_DEPTH24_STENCIL8, GL_FLOAT, (void*)buffer);)
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 GL_CALL2(glBindRenderbuffer(GL_RENDERBUFFER, 0);)
 if (glErrorCode!=GL_NO_ERROR) { if (bufferAllocatedHere) { free(buffer); buffer = 0; } return NULL; }
 if (outBufferSize) {
 *outBufferSize = bufferSize;
 }
 return buffer;
}
void RenderBuffer::on() { // activate me before opengl rendering 
 GL_CALL(glBindTexture(GL_TEXTURE_2D, 0);)
 GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, mFboId);)
 mIsOn = true;
}
void RenderBuffer::off() { // deactivate me after opengl rendering 
 GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0);) // switch back to window-system-provided framebuffer
 mIsOn = false;
}
int RenderBuffer::getAttachmentIndex() const {
 return mColorAttachmentIndex;
}
bool RenderBuffer::isValid() const {
 return (mFboId>0);
}
unsigned int RenderBuffer::width() const {
 return mW;
}
unsigned int RenderBuffer::widthstep() const {
 return mWidthstep;
} 
unsigned int RenderBuffer::height() const {
 return mH;
}
unsigned int RenderBuffer::channels() const {
 return mNumChannels;
}
unsigned int RenderBuffer::pixelSizedInternalFormat() const {
 return mPixelSizedInternalFormat;
} 
void RenderBuffer::reshape(int w, int h, float zNear, float zFar) { // call me only after calling 'glBindFramebuffer(GL_FRAMEBUFFER, mFboId)'
 if (w!=mW || h!=mH) {
 fprintf(stderr, "Error in 'RenderBuffer::reshape': Width or Height does not match the Width and Height given in construction time\n");
 return;
 }
 mZNear = zNear;
 mZFar = zFar;
 glMatrixLoadIdentityEXT(GL_PROJECTION);
 glMatrixOrthoEXT(GL_PROJECTION, 0, mW, 0, mH, zNear, zFar);
 glMatrixLoadIdentityEXT(GL_MODELVIEW);
}
// END OF OFFSCREEN RENDERING ENGINE
// Only include the minimal NV_path_rendering API used by this example for brevity.
#ifndef GL_NV_path_rendering
#define GL_TRANSLATE_X_NV 0x908E
#define GL_TRANSLATE_Y_NV 0x908F
#define GL_TRANSLATE_2D_NV 0x9090
#define GL_TRANSLATE_3D_NV 0x9091
#define GL_AFFINE_2D_NV 0x9092
#define GL_AFFINE_3D_NV 0x9094
#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096
#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098
// Tokens
#define GL_PATH_STROKE_WIDTH_NV 0x9075
#define GL_PATH_INITIAL_END_CAP_NV 0x9077
#define GL_PATH_TERMINAL_END_CAP_NV 0x9078
#define GL_PATH_JOIN_STYLE_NV 0x9079
#define GL_PATH_MITER_LIMIT_NV 0x907A
#define GL_PATH_FILL_COVER_MODE_NV 0x9082
#define GL_PATH_STROKE_COVER_MODE_NV 0x9083
#define GL_CLOSE_PATH_NV 0x00
#define GL_MOVE_TO_NV 0x02
#define GL_LINE_TO_NV 0x04
#define GL_ROUND_NV 0x90A4
#define GL_PATH_FORMAT_SVG_NV 0x9070
#define GL_PATH_FORMAT_PS_NV 0x9071
#define GL_STANDARD_FONT_NAME_NV 0x9072
#define GL_SYSTEM_FONT_NAME_NV 0x9073
#define GL_PATH_FILL_MODE_NV 0x9080
#define GL_TRANSLATE_X_NV 0x908E
#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C
#define GL_BOLD_BIT_NV 0x01
#define GL_ITALIC_BIT_NV 0x02
#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10
#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000
#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000
#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000
#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000
#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD
#define GL_PATH_STROKE_WIDTH_NV 0x9075
#define GL_PATH_JOIN_STYLE_NV 0x9079
#define GL_MITER_REVERT_NV 0x90A7
#define GL_MITER_TRUNCATE_NV 0x90A8
#define GL_PATH_MITER_LIMIT_NV 0x907A
#define GL_PRIMARY_COLOR 0x8577
#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A
#define GL_SKIP_MISSING_GLYPH_NV 0x90A9
#define GL_USE_MISSING_GLYPH_NV 0x90AA
#define GL_TRANSLATE_2D_NV 0x9090
#define GL_COUNT_UP_NV 0x9088
#define GL_COUNT_DOWN_NV 0x9089
#define GL_CONVEX_HULL_NV 0x908B
#define GL_BOUNDING_BOX_NV 0x908D
// Command and query function types
typedef GLint (GLAPIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range);
typedef void (GLAPIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range);
typedef void (GLAPIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const GLvoid *pathString);
typedef void (GLAPIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues);
typedef void (GLAPIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const GLvoid *coords);
typedef void (GLAPIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
typedef void (GLAPIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues);
typedef void (GLAPIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues);
typedef void (GLAPIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
typedef void (GLAPIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
typedef void (GLAPIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics);
typedef void (GLAPIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing);
typedef void (GLAPIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value);
typedef void (GLAPIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value);
typedef void (GLAPIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs);
typedef void (GLAPIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask);
typedef void (GLAPIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask);
typedef void (GLAPIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode);
typedef void (GLAPIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode);
typedef void (GLAPIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const GLvoid *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
#endif
#ifdef GL_FONT_Y_MIN_BOUNDS_NV
/* Due to an error in an early NV_path_rendering specification, the
 _BIT suffix was left out of the GL_FONT_* and GL_GLYPH_* token names.
 Some versions of glext.h in Mesa have this error. Workaround... */
#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000
#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000
#endif
#ifndef __APPLE__
// Path object management
PFNGLGENPATHSNVPROC glGenPathsNV = NULL;
PFNGLDELETEPATHSNVPROC glDeletePathsNV = NULL;
// Path object specification
PFNGLPATHCOMMANDSNVPROC glPathCommandsNV = NULL;
PFNGLPATHSTRINGNVPROC glPathStringNV = NULL;
PFNGLPATHGLYPHRANGENVPROC glPathGlyphRangeNV = NULL;
PFNGLPATHGLYPHSNVPROC glPathGlyphsNV = NULL;
PFNGLTRANSFORMPATHNVPROC glTransformPathNV = NULL;
// "Stencil, then Cover" path rendering commands
PFNGLCOVERFILLPATHNVPROC glCoverFillPathNV = NULL;
PFNGLCOVERSTROKEPATHNVPROC glCoverStrokePathNV = NULL;
PFNGLSTENCILFILLPATHNVPROC glStencilFillPathNV = NULL;
PFNGLSTENCILSTROKEPATHNVPROC glStencilStrokePathNV = NULL;
// Instanced path rendering
PFNGLSTENCILFILLPATHINSTANCEDNVPROC glStencilFillPathInstancedNV = NULL;
PFNGLCOVERFILLPATHINSTANCEDNVPROC glCoverFillPathInstancedNV = NULL;
PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC glStencilStrokePathInstancedNV = NULL;
PFNGLCOVERSTROKEPATHINSTANCEDNVPROC glCoverStrokePathInstancedNV = NULL;
// Glyph metric queries
PFNGLGETPATHMETRICRANGENVPROC glGetPathMetricRangeNV = NULL;
PFNGLGETPATHSPACINGNVPROC glGetPathSpacingNV = NULL;
// Path object parameter specification
PFNGLPATHPARAMETERINVPROC glPathParameteriNV = NULL;
PFNGLPATHPARAMETERFNVPROC glPathParameterfNV = NULL;
// Color generation
PFNGLPATHCOLORGENNVPROC glPathColorGenNV = NULL;
#endif
// Multi-platform macro to get OpenGL entrypoints
#if defined(linux) || defined(sun)
# define GET_PROC_ADDRESS(name) glXGetProcAddressARB((const GLubyte *) #name)
#elif defined(vxworks)
# define GET_PROC_ADDRESS(name) rglGetProcAddress(#name)
#elif defined(__APPLE__)
# define GET_PROC_ADDRESS(name) /*nothing*/
#elif defined(_WIN32)
# define GET_PROC_ADDRESS(name) wglGetProcAddress(#name)
#else
# error unimplemented code!
#endif
#define LOAD_PROC(type, name) \
 name = (type) GET_PROC_ADDRESS(name); \
 if (!name) { \
 fprintf(stderr, "failed to GetProcAddress for %s\n", #name); \
 exit(1); \
 }
// Global variables 
 // Booleans for extension support
int hasNV_path_rendering = 0; // Is NV_path_rendering supported?
int hasEXT_direct_state_access = 0; // Is EXT_direct_state_access supported?
int hasGL_ARB_framebuffer_object = 0;
// Every Path Set Has Fill and Stroke Color, Affine Transform and Vertices
class PathInfo {
public:
 PathInfo *srcPath; // Do I just transform another source Path?
 GLuint pathObject; // OpenGL Path Number > 0
 GLuint pathLanguageType; // GL_PATH_FORMAT_SVG_NV or GL_PATH_FORMAT_PS_NV
 GLuint pathTransformType; // NONE, TRANSLATE_X_NV, TRANSLATE_Y_NV, TRANSLATE_2D_NV, TRANSLATE_3D_NV, AFFINE_2D_NV, AFFINE_3D_NV, TRANSPOSE_AFFINE_2D_NV, or TRANSPOSE_AFFINE_3D_NV
 float pathTransform[12]; // from 1-value (translate in 1D) to 12-values for AFFINE_3D_NV
 GLuint pathTransformSize; // Number of actual elements in 'transform' array (from 0..12)
 std::string pathString; // M<x>,<y> L<x>,<y> A<> C<>...Z 
 float fillColor[4]; // rgba
 float strokeColor[4]; // rgba
 GLuint fillRule; // Valid Values: GL_INVERT, GL_COUNT_UP_NV, GL_COUNT_DOWN_NV, or GL_PATH_FILL_MODE_NV
 bool doStroke;
 bool doFill;
 bool alphaBlending;
 // set using void glPathParameteriNV(uint path, enum pname, int value);
 float pathStrokeWidth; // non-negative
 GLuint pathInitialEndCap; // GL_PATH_INITIAL_END_CAP_NV enum GL_FLAT, GL_SQUARE_NV, GL_ROUND_NV, GL_TRIANGULAR_NV
 GLuint pathTerminalEndCap; // GL_PATH_TERMINAL_END_CAP_NV enum GL_FLAT, GL_SQUARE_NV, GL_ROUND_NV, GL_TRIANGULAR_NV
 GLuint pathJoinStyle; // GL_PATH_JOIN_STYLE_NV enum GL_MITER_REVERT_NV, GL_MITER_TRUNCATE_NV, GL_BEVEL_NV, GL_ROUND_NV, NONE 
 GLuint pathFillCoverMode; // GL_PATH_FILL_COVER_MODE_NV enum GL_CONVEX_HULL_NV, GL_BOUNDING_BOX_NV
 GLuint pathStrokeCoverMode;// GL_PATH_STROKE_COVER_MODE_NV enum GL_CONVEX_HULL_NV, GL_BOUNDING_BOX_NV
 float pathMiterLimit; // GL_PATH_MITER_LIMIT_NV float non-negative
PathInfo() {
 reset();
 }
PathInfo(
 const GLuint _pathObject, 
 const std::string &_pathString, 
 const float *transform = 0, 
 const GLuint _fillRule = GL_COUNT_UP_NV,
 const GLuint _pathLanguageType = GL_PATH_FORMAT_SVG_NV, 
 const GLuint _pathTransformType = GL_NONE) {
 reset();
 pathObject = _pathObject;
 pathString = _pathString;
 pathLanguageType = _pathLanguageType;
 pathTransformType = _pathTransformType;
 pathTransformSize = getTransformNumberOfElements(_pathTransformType);
 if (pathTransformSize>0 && transform) {
 memcpy(pathTransform, transform, pathTransformSize*sizeof(float));
 }
 }
 void reset() {
 alphaBlending = false;
 srcPath = 0;
 pathLanguageType = GL_PATH_FORMAT_SVG_NV;
 pathTransformType = GL_NONE;
 pathTransformSize = 0;
 pathStrokeWidth = 1.0f;
 pathInitialEndCap = GL_FLAT;
 pathTerminalEndCap = GL_FLAT;
 pathJoinStyle = GL_MITER_REVERT_NV;
 pathMiterLimit = 4;
 pathFillCoverMode = GL_CONVEX_HULL_NV;
 pathStrokeCoverMode = GL_CONVEX_HULL_NV;
 doStroke = doFill = true;
 fillColor[0]=1; fillColor[1]=1; fillColor[2]=1; fillColor[3]=1;
 strokeColor[0]=0.5f; strokeColor[1]=0.5f; strokeColor[2]=0.5f; strokeColor[3]=1.0f;
 fillRule = GL_COUNT_UP_NV;
 pathObject = 0;
 pathString.clear();
 memset(pathTransform, 0, 12*sizeof(float));
 }
 bool isReferencing() const {
 return srcPath!=0;
 }
 PathInfo& reference() {
 return *srcPath;
 }
 PathInfo* referencePtr() {
 return srcPath;
 }
 int setTransform(GLuint _pathTransformType, const float *_pathTransform) {
 int _pathTransformSize = getTransformNumberOfElements(_pathTransformType);
 if (_pathTransformSize>0 && _pathTransform) {
 this->pathTransformSize = _pathTransformSize;
 this->pathTransformType = _pathTransformType;
 memcpy(this->pathTransform, _pathTransform, this->pathTransformSize*sizeof(float));
 }
 return 0;
 }
 int getTransformNumberOfElements(const GLuint _pathTransformType) {
 int size = 0;
 if (GL_NONE == _pathTransformType) 
 size = 0;
 else if (GL_TRANSLATE_X_NV == _pathTransformType) 
 size = 1;
 else if (GL_TRANSLATE_Y_NV == _pathTransformType) 
 size = 1;
 else if (GL_TRANSLATE_2D_NV == _pathTransformType) 
 size = 2;
 else if (GL_TRANSLATE_3D_NV == _pathTransformType) 
 size = 3;
 else if (GL_AFFINE_2D_NV == _pathTransformType) 
 size = 6;
 else if (GL_AFFINE_3D_NV == _pathTransformType) 
 size = 12;
 else if (GL_TRANSPOSE_AFFINE_3D_NV == _pathTransformType) 
 size = 12;
 return size;
 }
};
std::vector<PathInfo*> 
 glblPathToRender;
bool glblRenderBufferEnabled = true;
RenderBuffer *glblRenderBuffer = 0;
bool glblOffscreenRendering = false;
bool glblOnscreenRendering = true;
int glblSamples = 0;
int glblWidth = 640;
int glblHeight = 480; // rendering window canvas size
int glblWidthOffscreen = glblWidth;
int glblHeightOffscreen = glblHeight; // non visible rendering canvas size
float glblPathTransform[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
GLuint glblPathTransformType = GL_NONE;
float glblBgcolor[4] = {0.5f,0.3f,0.2f,1.0f};
float glblFillColor[4] = {1,1,1,1};
float glblStrokeColor[4] = {0.5f,0.5f,0.5f,1.0f};
bool glblDoStroke = true;
bool glblDoFill = true;
GLuint glblFillRule = GL_COUNT_UP_NV;
bool glblAlphaBlending = false;
bool glblAnimation = false;
char glblCaptureFilename[2048]={0x0};
bool glblFitWindow = false;
/* 
 The file is an ASCII file with this format, for example:
 M 100,180 L 40,10 L 190,120 L 10,120 L 160,10 z
 M 300 300 C 100 400,100 200,300 100,500 200,500 400,300 300 Z
 Syntax:
Production command character Path command token
 ------------------------------------------------- ----------------- -------------------------------------
 moveto-argument-sequence "M" MOVE_TO_NV
 "m" RELATIVE_MOVE_TO_NV
 closepath "Z" or "z" CLOSE_PATH_NV
 lineto-argument-sequence "L" LINE_TO_NV
 "l" RELATIVE_LINE_TO_NV
 horizontal-lineto-argument-sequence "H" HORIZONTAL_LINE_TO_NV
 "h" RELATIVE_HORIZONTAL_LINE_TO_NV
 vertical-lineto-argument-sequence "V" VERTICAL_LINE_TO_NV
 "v" RELATIVE_VERTICAL_LINE_TO_NV
 quadratic-bezier-curveto-argument "Q" QUADRATIC_CURVE_TO_NV
 "q" RELATIVE_QUADRATIC_CURVE_TO_NV
 smooth-quadratic-bezier-curveto-argument-sequence "T" SMOOTH_QUADRATIC_CURVE_TO_NV
 "t" RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV
 curveto-argument "C" CUBIC_CURVE_TO_NV
 "c" RELATIVE_CUBIC_CURVE_TO_NV
 smooth-curveto-argument "S" SMOOTH_CUBIC_CURVE_TO_NV
 "s" RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV
 elliptical-arc-argument "A" ARC_TO_NV
 "a" RELATIVE_ARC_TO_NV
 'M'/'m' moveto/relative <x> <y>
 'L'/'l' lineto <x> <y>
 'H'/'h' horizontal lineto <x>
 'V'/'v' vertical lineto <y>
 'C'/'c' curveto <a> <b> <c> <d> <e> <f>
 'T'/'t' smooth quad-curve <a> <b>
 'S'/'s' smooth cubic-curve <a> <b> <c> <d>
 'Q'/'q' quadratic-bezier <a> <b> <c> <d>
 'W'/'w' conic curve <a> <b> <c> <d> <e>
 'A'/'a' elliptical-arc <a> <b> <c> <d> <e> <f> <g>
 'Z'/'z' closepath/reset
more info on: https://www.opengl.org/registry/specs/NV/path_rendering.txt
*/
static char* glNV_Path_String_ReadFromFile(const char *filename, int *error) {
 *error = 0;
 char *ret = 0; 
 FILE *f = fopen(filename, "rb");
 if (!f) {
 fprintf(stderr, "Error in 'glNV_Path_String_ReadFromFile': reading file: '%s'\n", filename);
 *error=1;
 return NULL;
 }
 fseek(f, 0, SEEK_END); 
 long size = ftell(f);
 fseek(f, 0, SEEK_SET);
 ret = (char*)malloc(size+1);
 if (!ret) {
 fprintf(stderr, "Error in '': Failed to allocate memory with size %d bytes\n", (size+1));
 *error=2;
 return 0;
 }
 long rc = fread(ret, size, 1, f);
 fclose(f);
 if (rc != 1) {
 fprintf(stderr, "Error in 'glNV_Path_String_ReadFromFile': reading file: '%s'\n", filename);
 *error=3;
 free(ret);
 ret = 0;
 return NULL;
 }
 ret[size] = 0x0;
 return ret;
}
// Initialize OpenGL functions
// GL_NV_path_rendering, GL_EXT_direct_state_access, GL_ARB_framebuffer_object
static bool glInitExtensions(void) {
 hasNV_path_rendering = glutExtensionSupported("GL_NV_path_rendering");
 hasEXT_direct_state_access = glutExtensionSupported("GL_EXT_direct_state_access");
 hasGL_ARB_framebuffer_object = glutExtensionSupported("GL_ARB_framebuffer_object");
if (!hasNV_path_rendering) {
 fprintf(stderr, "required NV_path_rendering OpenGL extension is not present\n");
 return false;
 }
 if (!hasEXT_direct_state_access) {
 fprintf(stderr, "required EXT_direct_state_access OpenGL extension is not present\n");
 return false;
 }
 if (!hasGL_ARB_framebuffer_object) {
 fprintf(stderr, "required hasGL_ARB_framebuffer_object OpenGL extension is not present\n");
 return false;
 }
if (hasGL_ARB_framebuffer_object) {
 LOAD_PROC(PFNGLBINDFRAMEBUFFERPROC , glBindFramebuffer);
 LOAD_PROC(PFNGLBINDFRAMEBUFFERPROC , glBindFramebuffer);
 LOAD_PROC(PFNGLBINDRENDERBUFFERPROC , glBindRenderbuffer);
 LOAD_PROC(PFNGLBLITFRAMEBUFFERPROC , glBlitFramebuffer);
 LOAD_PROC(PFNGLCHECKFRAMEBUFFERSTATUSPROC,glCheckFramebufferStatus);
 LOAD_PROC(PFNGLDELETEFRAMEBUFFERSPROC , glDeleteFramebuffers);
 LOAD_PROC(PFNGLDELETERENDERBUFFERSPROC , glDeleteRenderbuffers);
 LOAD_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC,glFramebufferRenderbuffer);
 LOAD_PROC(PFNGLFRAMEBUFFERTEXTURE1DPROC , glFramebufferTexture1D);
 LOAD_PROC(PFNGLFRAMEBUFFERTEXTURE2DPROC,glFramebufferTexture2D);
 LOAD_PROC(PFNGLFRAMEBUFFERTEXTURE3DPROC , glFramebufferTexture3D);
 LOAD_PROC(PFNGLFRAMEBUFFERTEXTURELAYERPROC,glFramebufferTextureLayer);
 LOAD_PROC(PFNGLGENFRAMEBUFFERSPROC , glGenFramebuffers);
 LOAD_PROC(PFNGLGENRENDERBUFFERSPROC , glGenRenderbuffers);
 LOAD_PROC(PFNGLGENERATEMIPMAPPROC , glGenerateMipmap);
 LOAD_PROC(PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC,glGetFramebufferAttachmentParameteriv);
 LOAD_PROC(PFNGLGETRENDERBUFFERPARAMETERIVPROC,glGetRenderbufferParameteriv);
 LOAD_PROC(PFNGLISFRAMEBUFFERPROC , glIsFramebuffer);
 LOAD_PROC(PFNGLISRENDERBUFFERPROC , glIsRenderbuffer);
 LOAD_PROC(PFNGLRENDERBUFFERSTORAGEPROC , glRenderbufferStorage);
 LOAD_PROC(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC,glRenderbufferStorageMultisample);
 }
 if (hasNV_path_rendering) {
 // Initialize a subset of NV_path_rendering API
 LOAD_PROC(PFNGLGENPATHSNVPROC , glGenPathsNV);
 LOAD_PROC(PFNGLDELETEPATHSNVPROC , glDeletePathsNV);
 LOAD_PROC(PFNGLPATHCOMMANDSNVPROC , glPathCommandsNV);
 LOAD_PROC(PFNGLPATHSTRINGNVPROC , glPathStringNV);
 LOAD_PROC(PFNGLPATHGLYPHRANGENVPROC , glPathGlyphRangeNV);
 LOAD_PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV);
 LOAD_PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV);
 LOAD_PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV);
 LOAD_PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV);
 LOAD_PROC(PFNGLGETPATHMETRICRANGENVPROC , glGetPathMetricRangeNV);
 LOAD_PROC(PFNGLGETPATHSPACINGNVPROC , glGetPathSpacingNV);
 LOAD_PROC(PFNGLPATHPARAMETERINVPROC , glPathParameteriNV);
 LOAD_PROC(PFNGLPATHPARAMETERFNVPROC , glPathParameterfNV);
 LOAD_PROC(PFNGLPATHCOLORGENNVPROC , glPathColorGenNV);
 LOAD_PROC(PFNGLPATHGLYPHSNVPROC , glPathGlyphsNV); 
 LOAD_PROC(PFNGLSTENCILFILLPATHNVPROC , glStencilFillPathNV);
 LOAD_PROC(PFNGLSTENCILSTROKEPATHNVPROC , glStencilStrokePathNV);
 LOAD_PROC(PFNGLCOVERFILLPATHNVPROC , glCoverFillPathNV);
 LOAD_PROC(PFNGLCOVERSTROKEPATHNVPROC , glCoverStrokePathNV);
 LOAD_PROC(PFNGLTRANSFORMPATHNVPROC , glTransformPathNV);
 }
 if (hasEXT_direct_state_access) {
 // Initialize a subset of EXT_direct_state_access API
 LOAD_PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT);
 LOAD_PROC(PFNGLMATRIXORTHOEXTPROC , glMatrixOrthoEXT);
 LOAD_PROC(PFNGLMATRIXTRANSLATEFEXTPROC , glMatrixTranslatefEXT);
 LOAD_PROC(PFNGLMATRIXROTATEFEXTPROC , glMatrixRotatefEXT);
 }
 return true;
}
const GLfloat emScale = 2048; // match TrueType convention
const GLuint templatePathObject = ~0; // Non-existant path object
GLuint glblPathObjForDemo = 42;
void initSVGpath()
{
 const char *svgPathString =
 // star
 "M 100,180 L 40,10 L 190,120 L 10,120 L 160,10 z"
 // heart
 "M 300 300 C 100 400,100 200,300 100,500 200,500 400,300 300 Z";
 // Create "star and heart" scene from SVG string
 glPathStringNV(glblPathObjForDemo, GL_PATH_FORMAT_SVG_NV, (GLsizei)strlen(svgPathString), svgPathString);
}
void initSVGPathWithHoles() {
 // External Contour CCW Internal Contour (Hole) CW Close Path
 // +----------------------------------+ +----------------------------------+ +
 const char *svgPathString = "M10,10 L50,10 L50,400 L10,400 L10,10 M20,20 L20,390 L40,390 L40,20 L20,20 Z";
 glPathStringNV(glblPathObjForDemo, GL_PATH_FORMAT_SVG_NV, (GLsizei)strlen(svgPathString), svgPathString);
}
void initPSpath()
{
 const char *psPathString =
 // star 
 "100 180 moveto"
 " 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath"
 // heart 
 " 300 300 moveto"
 " 100 400 100 200 300 100 curveto"
 " 500 200 500 400 300 300 curveto closepath";
 // Create "star and heart" scene from PostScript user path string
 glPathStringNV(glblPathObjForDemo, GL_PATH_FORMAT_PS_NV, (GLsizei)strlen(psPathString), psPathString);
}
void initDATApath()
{
 static const GLubyte pathCommands[10] =
 { GL_MOVE_TO_NV, GL_LINE_TO_NV, GL_LINE_TO_NV, GL_LINE_TO_NV,
 GL_LINE_TO_NV, GL_CLOSE_PATH_NV, 
 'M', 'C', 'C', 'Z' }; // character aliases
 static const GLshort pathCoords[12][2] =
 { {100, 180}, {40, 10}, {190, 120}, {10, 120}, {160, 10},
 {300,300}, {100,400}, {100,200}, {300,100},
 {500,200}, {500,400}, {300,300} };
 // Create "star and heart" scene from raw command and coordinate data arrays
 glPathCommandsNV(glblPathObjForDemo, 10, pathCommands, 24, GL_SHORT, pathCoords);
}
// Set a current background color
void setBackground(float bgcolor[4]) {
 glClearColor(bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3]);
}
// Set stroking parameters of the "star and heart" scene path object
void setPathParameters()
{
 glPathParameteriNV(glblPathObjForDemo, GL_PATH_JOIN_STYLE_NV, GL_ROUND_NV);
 glPathParameterfNV(glblPathObjForDemo, GL_PATH_STROKE_WIDTH_NV, 6.5);
}
void initStarAndHeartScene()
{
 initSVGpath();
 initPSpath();
 initDATApath();
 setPathParameters();
}
void glInitGraphics() {
 setBackground(glblBgcolor);
 initStarAndHeartScene();
}
static int drawGeneralPathObject(
 GLuint thePathObject, 
 int projectionWidth, 
 int projectionHeight, 
 float fillColor[4], 
 float strokeColor[4], 
 bool doFill, 
 bool doStroke,
 GLuint fillRule
 ) {
 glMatrixLoadIdentityEXT(GL_PROJECTION);
 glMatrixOrthoEXT(GL_PROJECTION, 0, projectionWidth, 0, projectionHeight, -1, 1);
 glMatrixLoadIdentityEXT(GL_MODELVIEW);
 glEnable(GL_STENCIL_TEST); 
 glStencilFunc(GL_NOTEQUAL, 0, 0x1F);
 glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
if (doFill) {
 glStencilFillPathNV(thePathObject, fillRule, 0x1F);
 glColor4f(fillColor[0], fillColor[1], fillColor[2], fillColor[3]);
 glCoverFillPathNV(thePathObject, GL_BOUNDING_BOX_NV);
 }
 if (doStroke) {
 glStencilStrokePathNV(thePathObject, 0x1, ~0);
 glColor4f(strokeColor[0], strokeColor[1], strokeColor[2], strokeColor[3]);
 glCoverStrokePathNV(thePathObject, GL_CONVEX_HULL_NV);
 }
 return 0;
}
static void drawStarAndHeart() {
 glMatrixLoadIdentityEXT(GL_PROJECTION);
 glMatrixOrthoEXT(GL_PROJECTION, 0, 500, 0, 400, -1, 1);
 glMatrixLoadIdentityEXT(GL_MODELVIEW);
glStencilFillPathNV(glblPathObjForDemo, GL_COUNT_UP_NV, 0x1F);
glEnable(GL_STENCIL_TEST);
 glStencilFunc(GL_NOTEQUAL, 0, 0x1F);
 glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
 glColor3f(1,1,0); // yellow
 glCoverFillPathNV(glblPathObjForDemo, GL_BOUNDING_BOX_NV);
glStencilStrokePathNV(glblPathObjForDemo, 0x1, ~0);
 glColor3f(1,1,1); // white
 glCoverStrokePathNV(glblPathObjForDemo, GL_CONVEX_HULL_NV);
}
static void IdleEvent(void) { 
 glutPostRedisplay();
}
static int renderScene(const int canvasWidth, const int canvasHeight) {
 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 if (glblPathToRender.empty()) {
 drawStarAndHeart();
 }
 else {
 for (size_t i=0; i<glblPathToRender.size(); ++i) {
 PathInfo &objPathInfo = *glblPathToRender[i];
 if (0 == objPathInfo.pathObject) {
 // GENERATE A PATH AND PREPARE THE PATH, ONCE
 objPathInfo.pathObject = glGenPathsNV(1);
 const char *path = 
 (objPathInfo.isReferencing() && objPathInfo.referencePtr())
 ? objPathInfo.reference().pathString.c_str()
 : objPathInfo.pathString.c_str()
 ;
 if (!path) {
 fprintf(stderr, "Error while rendering, path %d string is NULL\n", i);
 exit(1);
 }
 glPathStringNV(objPathInfo.pathObject, objPathInfo.pathLanguageType, (GLsizei)strlen(path), path);
 if (glGetError() != GL_NO_ERROR) {
 fprintf(stderr, "Error in Generating PATH from model number %d\n", i);
 exit(1);
 }
// DO WE HAVE AFFINE TRANSFORM
 const float *pathTransformValues = objPathInfo.pathTransform;
 glTransformPathNV(objPathInfo.pathObject, objPathInfo.pathObject, objPathInfo.pathTransformType, pathTransformValues);
 }
glPathParameterfNV(objPathInfo.pathObject, GL_PATH_STROKE_WIDTH_NV , objPathInfo.pathStrokeWidth);
 glPathParameterfNV(objPathInfo.pathObject, GL_PATH_MITER_LIMIT_NV , objPathInfo.pathMiterLimit);
 glPathParameteriNV(objPathInfo.pathObject, GL_PATH_INITIAL_END_CAP_NV , objPathInfo.pathInitialEndCap);
 glPathParameteriNV(objPathInfo.pathObject, GL_PATH_TERMINAL_END_CAP_NV , objPathInfo.pathTerminalEndCap);
 glPathParameteriNV(objPathInfo.pathObject, GL_PATH_JOIN_STYLE_NV , objPathInfo.pathJoinStyle);
 glPathParameteriNV(objPathInfo.pathObject, GL_PATH_FILL_COVER_MODE_NV , objPathInfo.pathFillCoverMode);
 glPathParameteriNV(objPathInfo.pathObject, GL_PATH_STROKE_COVER_MODE_NV , objPathInfo.pathStrokeCoverMode);
if (objPathInfo.alphaBlending) {
 glEnable(GL_BLEND);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 }
 else {
 glDisable(GL_BLEND);
 }
 // The Actual Path Drawing after PATH is prepared and optionally transformed
 drawGeneralPathObject(
 objPathInfo.pathObject, 
 canvasWidth, canvasHeight, 
 objPathInfo.fillColor, objPathInfo.strokeColor, 
 objPathInfo.doFill, objPathInfo.doStroke, objPathInfo.fillRule
 );
 }
 } 
 return 0;
}
static void RenderingEvent(void) {
 // DO ONCE
 if (glblOffscreenRendering && !glblRenderBuffer && glblRenderBufferEnabled) { 
 float zNear=-1, zFar=1, fovYAngle=45;
 glblRenderBuffer = new RenderBuffer(glblWidthOffscreen, glblHeightOffscreen, zNear, zFar, fovYAngle, GL_COLOR_ATTACHMENT0, true);
 glblRenderBuffer->on();
 int rc = glblRenderBuffer->onceAfterConstruction();
 glblRenderBuffer->off();
 if (rc != 0) {
 delete glblRenderBuffer;
 glblRenderBuffer = 0;
 glblRenderBufferEnabled = false;
 glblOffscreenRendering = 0;
 }
 fprintf(stderr, "Prepared an Offscreen Rendering Buffer (w,h)=(%d,%d)\n", glblWidthOffscreen, glblHeightOffscreen);
 }
 // END OF DO ONCE
// Render Offscreen
 if (glblOffscreenRendering && glblRenderBuffer) {
 glblRenderBuffer->on();
 renderScene(glblWidthOffscreen, glblHeightOffscreen);
 glblRenderBuffer->off();
 }
// Render To Screen
 if (glblOnscreenRendering) {
 renderScene(glblWidth, glblHeight);
 }
 glutSwapBuffers();
// IMAGE CAPTURE SECTION
 if (glblRenderBuffer && strlen(glblCaptureFilename) > 0) {
 char filename[2048];
 sprintf(filename, "%s-%dx%dx32bpp.raw", glblCaptureFilename, glblWidthOffscreen, glblHeightOffscreen);
 glblRenderBuffer->on();
 size_t bufferSize = 0;
 unsigned char *imgBuffer8u = glblRenderBuffer->getImage(0, &bufferSize);
 if (imgBuffer8u) {
 FILE *f = fopen(filename, "wb");
 if (f) {
 size_t fwriteResult = fwrite(imgBuffer8u, bufferSize, 1, f);
 if (fwriteResult != 1) {
 fprintf(stderr, "Error: After success with opening the file '%s', I failed to write to it\n", filename);
 }
 else {
 fprintf(stderr, "Successfully captured 32bit RAW Image with dimensions %d,%d to '%s'\n",
 glblWidthOffscreen, glblHeightOffscreen, filename);
 }
 fclose(f);
 f = 0;
 }
 else {
 fprintf(stderr, "Error: Failed to open file '%s' for writing\n", filename);
 }
 free(imgBuffer8u);
 imgBuffer8u = 0;
 }
 glblRenderBuffer->off();
 glblCaptureFilename[0] = 0x0; // clear -capture name
 }
 // END OF IMAGE CAPTURE SECTION
}
void KeyboardEvent(unsigned char c, int x, int y) {
 switch (c) {
 case 27: // Esc quits
 exit(0);
 return;
 default:
 return;
 }
 glutPostRedisplay();
}
static void menu(int choice) {
 KeyboardEvent(choice, 0, 0);
}
int main(int argc, char **argv) {
fprintf(stderr, "%s Info:\n"
 "[1] Use an NVIDIA GPU Only.\n"
 "[2] Turn off antialiasing in the display settings by setting it to \n"
 " 'Application controlled' (in the NVidia drivers).\n"
 , argv[0]
 );
// DEFAULT SETTINGS
 glDisable(GL_MULTISAMPLE);
 glDisable(GL_LINE_SMOOTH);
 glDisable(GL_POINT_SMOOTH);
 glDisable(GL_POLYGON_SMOOTH);
 
 for (int i=1; i<argc;) {
 bool handled = true;
 const int cmdIndexBeforeIncrement = i;
 if (0==strcmp("-samples", argv[i])) {
 int value = atoi(argv[i]+1);
 if (value >= 1) {
 glblSamples = value;
 fprintf(stderr, "Samples %d\n", glblSamples);
 continue;
 }
 }
 else if (0==strcmp("-skip", argv[i])) { 
 fprintf(stderr, "Skip %d elements\n", atoi(argv[i+1]));
 i += atoi(argv[i+1]);
 }
 else if (0==strcmp("-no-multisample", argv[i])) { 
 glDisable(GL_MULTISAMPLE);
 i+=1;
 }
 else if (0==strcmp("-ms", argv[i]) ||
 0==strcmp("-multisample", argv[i])
 ) { 
 glEnable(GL_MULTISAMPLE);
 i+=1;
 }
 else if (0==strcmp("-no-aa", argv[i]) ||
 0==strcmp("-no-antialiasing", argv[i])
 ) { 
 glDisable(GL_POLYGON_SMOOTH);
 glDisable(GL_LINE_SMOOTH);
 glDisable(GL_POINT_SMOOTH); 
 i+=1;
 }
 else if (0==strcmp("-aa", argv[i]) ||
 0==strcmp("-antialiasing", argv[i])
 ) { 
 glEnable(GL_POLYGON_SMOOTH);
 glEnable(GL_LINE_SMOOTH);
 glEnable(GL_POINT_SMOOTH);
 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
 i+=1;
 }
 else if (0==strcmp("-bgc", argv[i]) ||
 0==strcmp("-bgcolor", argv[i])
 ) { // rgba 
 for (int j=0; j<4; ++j) {
 glblBgcolor[j] = (float)atof(argv[i+1+j]);
 }
 fprintf(stderr, "Background Color (r,g,b,a)=(%f,%f,%f,%f)\n", glblBgcolor[0], glblBgcolor[1], glblBgcolor[2], glblBgcolor[3]);
 i+=5;
 }
 else if (0==strcmp("-fc", argv[i]) ||
 0==strcmp("-fillcolor", argv[i])
 ) { // rgba 
 for (int j=0; j<4; ++j) {
 glblFillColor[j] = (float)atof(argv[i+1+j]);
 }
 fprintf(stderr, "Fill Color (r,g,b,a)=(%f,%f,%f,%f)\n", glblFillColor[0], glblFillColor[1], glblFillColor[2], glblFillColor[3]);
 i+=5;
 }
 else if (0==strcmp("-no-stroke", argv[i])) { 
 glblDoStroke = false;
 i+=1;
 }
 else if (0==strcmp("-no-fill", argv[i])) { 
 glblDoFill = false;
 i+=1;
 }
 else if (0==strcmp("-stroke", argv[i])) { 
 glblDoStroke = true;
 i+=1;
 }
 else if (0==strcmp("-fill", argv[i])) { 
 glblDoFill = true;
 i+=1;
 }
 else if (0==strcmp("-sc", argv[i]) ||
 0==strcmp("-strokecolor", argv[i])
 ) { // rgba 
 for (int j=0; j<4; ++j) {
 glblStrokeColor[j] = (float)atof(argv[i+1+j]);
 }
 fprintf(stderr, "Stroke Color (r,g,b,a)=(%f,%f,%f,%f)\n", glblStrokeColor[0], glblStrokeColor[1], glblStrokeColor[2], glblStrokeColor[3]);
 i+=5;
 }
 else if (0==strcmp("-no-alpha-blending", argv[i])) { 
 glblAlphaBlending = false;
 i+=1;
 }
 else if (0==strcmp("-alpha-blending", argv[i])) { 
 glblAlphaBlending = true;
 i+=1;
 }
 else if (0==strcmp("-animation", argv[i])) { 
 glblAnimation = true;
 i+=1;
 }
 else if (0==strcmp("-offscreen", argv[i])) {
 glblOffscreenRendering = true;
 i+=1;
 }
 else if (0==strcmp("-no-onscreen", argv[i])) {
 glblOnscreenRendering = false;
 i+=1;
 }
 else if (0==strcmp("-capture", argv[i])) { 
 strncpy(glblCaptureFilename, argv[i+1], 2048);
 i+=2;
 }
 else if (0==strcmp("-fitwindow", argv[i])) { 
 glblFitWindow = true;
 i+=1;
 }
 else if (0==strcmp("-c", argv[i]) ||
 0==strcmp("-canvas", argv[i])
 ) {
 glblWidth = atoi(argv[i+1]);
 glblHeight = atoi(argv[i+2]);
 fprintf(stderr, "Canvas Size (%d, %d)\n", glblWidth, glblHeight);
 i+=3;
 }
 // offscreen canvas size
 else if (0==strcmp("-osc", argv[i]) ||
 0==strcmp("-offscreen-canvas", argv[i])
 ) { 
 glblWidthOffscreen = atoi(argv[i+1]);
 glblHeightOffscreen = atoi(argv[i+2]);
 fprintf(stderr, "Offscreen Canvas Size (%d, %d)\n", glblWidthOffscreen, glblHeightOffscreen);
 i+=3;
 }
 else if (0==strcmp("-path-ps", argv[i]) ||
 0==strcmp("-path-svg", argv[i])
 ) { 
 int error = 0; 
 char *buf = glNV_Path_String_ReadFromFile(argv[i+1], &error);
 if (error != 0) {
 free(buf);
 buf = 0;
 return error;
 }
 fprintf(stderr, "Success loading ascii path from '%s'\n", argv[i+1]);
 const GLuint pathFormatType = 
 (0==strcmp("-path-svg", argv[i])) 
 ? GL_PATH_FORMAT_SVG_NV 
 : GL_PATH_FORMAT_PS_NV;
 PathInfo *pi = new PathInfo(0, std::string(buf), glblPathTransform, glblFillRule, pathFormatType, glblPathTransformType);
// SAVE ALL RELEVANT GLOBAL VARIABLES
 memcpy(pi->fillColor, glblFillColor, 4*sizeof(float));
 memcpy(pi->strokeColor, glblStrokeColor, 4*sizeof(float));
 pi->doStroke = glblDoStroke;
 pi->doFill = glblDoFill;
 pi->fillRule = glblFillRule;
 pi->alphaBlending = glblAlphaBlending;
// PUSH NEW ITEM
 glblPathToRender.push_back(pi);
 free(buf);
 buf = 0;
 i+=2;
 }
 else if (0==strcmp("-fillrule-invert", argv[i])) { 
 glblFillRule = GL_INVERT;
 i+=1;
 }
 else if (0==strcmp("-fillrule-count-up", argv[i])) { 
 glblFillRule = GL_COUNT_UP_NV;
 i+=1;
 }
 else if (0==strcmp("-fillrule-count-down", argv[i])) { 
 glblFillRule = GL_COUNT_DOWN_NV;
 i+=1;
 }
 else if (0==strcmp("-fillrule-path-fill-mode", argv[i])) { 
 glblFillRule = GL_PATH_FILL_MODE_NV;
 i+=1;
 }
 else if (0==strcmp("-dup" , argv[i]) ||
 0==strcmp("-duplicate", argv[i])
 ) { 
 // Duplicate the last loaded model path and use the last 'affine' info 
 // to create a transformed-duplicate of it
 if (glblPathToRender.empty()) {
 fprintf(stderr, "Error in '%s' command: no previous Path defined\n", argv[i]);
 return 1;
 }
 // SEARCH FOR A REAL PATH AND NOT A REFERENCE PATH
 PathInfo *srcPath = 0;
 for (int j=glblPathToRender.size()-1; j>=0; --j) {
 PathInfo *p = glblPathToRender[j];
 if (!p->isReferencing()) {
 srcPath = p;
 break;
 }
 }
 if (!srcPath) {
 fprintf(stderr, "Error in 'mi' command: no previous Path defined\n");
 return 1;
 }
 // WE HAVE THE SOURCE PATH TO DUPLICATE
 PathInfo *newPathDup = new PathInfo(*srcPath); // make copy
 // CLEAR STRING
 newPathDup->pathString.clear();
 newPathDup->pathObject = 0;
 // SET SOURCE PATH
 newPathDup->srcPath = srcPath;
 // SET NEW TRANSFORM
 if (newPathDup->setTransform(glblPathTransformType, glblPathTransform) != 0) {
 delete newPathDup;
 newPathDup = 0;
 fprintf(stderr, "Error in '%s' command: no previous Path defined\n", argv[i]);
 return 1;
 }
 // SAVE ALL RELEVANT GLOBAL VARIABLES
 memcpy(newPathDup->fillColor, glblFillColor, 4*sizeof(float));
 memcpy(newPathDup->strokeColor, glblStrokeColor, 4*sizeof(float));
 newPathDup->doStroke = glblDoStroke;
 newPathDup->doFill = glblDoFill;
 newPathDup->fillRule = glblFillRule;
 newPathDup->alphaBlending = glblAlphaBlending;
// PUSH NEW REFERENCE PATH
 glblPathToRender.push_back(newPathDup);
 i+=1;
 } 
 else if (0==strcmp("-af2d", argv[i]) ||
 0==strcmp("-affine", argv[i]) ||
 0==strcmp("-affine2d", argv[i])
 ) {
 glblPathTransformType = GL_AFFINE_2D_NV;
 memset(glblPathTransform, 0, 12*sizeof(float));
 for (int j=0; j<6; ++j) {
 glblPathTransform[j] = (float)atof(argv[i+1+j]);
 }
 fprintf(stderr, "Affine Transform 2D\n"
 "[ v0 v2 0 v4 ] [%f %f %f %f]\n"
 "[ v1 v3 0 v5 ] = [%f %f %f %f]\n"
 "[ 0 0 1 0 ] [%f %f %f %f]\n"
 "[ 0 0 0 1 ] [%f %f %f %f]\n"
 , glblPathTransform[0], glblPathTransform[2], 0, glblPathTransform[4]
 , glblPathTransform[1], glblPathTransform[3], 0, glblPathTransform[5]
 , 0.0f, 0.0f, 1.0f, 0.0f
 , 0.0f, 0.0f, 0.0f, 1.0f
 );
 i+=7;
 }
 else if (0==strcmp("-af3d", argv[i]) ||
 0==strcmp("-affine3d", argv[i])
 ) {
 glblPathTransformType = GL_AFFINE_3D_NV;
 memset(glblPathTransform, 0, 12*sizeof(float));
 for (int j=0; j<12; ++j) {
 glblPathTransform[j] = (float)atof(argv[i+1+j]);
 }
 fprintf(stderr, "Affine Transform 3D\n"
 "[ v0 v3 v6 v9 ] [%f %f %f %f]\n"
 "[ v1 v4 v7 v10 ] = [%f %f %f %f]\n"
 "[ v2 v5 v8 v11 ] [%f %f %f %f]\n"
 "[ 0 0 0 1 ] [%f %f %f %f]\n"
 , glblPathTransform[0], glblPathTransform[3], glblPathTransform[6], glblPathTransform[9]
 , glblPathTransform[1], glblPathTransform[4], glblPathTransform[7], glblPathTransform[10]
 , glblPathTransform[2], glblPathTransform[5], glblPathTransform[8], glblPathTransform[11]
 , 0.0f, 0.0f, 0.0f, 1.0f
 );
 i+=13;
 }
 else {
 handled = false;
 fprintf(stderr, "Unknwon Parameter '%s'\n", argv[cmdIndexBeforeIncrement]);
 ++i;
 }
 if (handled) {
 fprintf(stderr, "Understood command (%d)...'%s'\n", i, argv[cmdIndexBeforeIncrement]);
 }
 }
 glutInitWindowSize(glblWidth, glblHeight);
 glutInit(&argc, argv);
if (glblSamples > 0) {
 char buffer[200];
 if (glblSamples == 1) 
 glblSamples = 0;
 printf("requesting %d samples\n", glblSamples);
 sprintf(buffer, "rgb stencil~4 double samples~%d", glblSamples);
 glutInitDisplayString(buffer);
 } 
 else {
 // Request a double-buffered window with at least 4 stencil bits and 8 samples per pixel.
 glutInitDisplayString("rgb stencil~4 double samples~8");
 }
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_MULTISAMPLE);
 glutCreateWindow("NV_path_rendering Engine", 0, 0, 0);
printf("vendor: %s\n", glGetString(GL_VENDOR));
 printf("version: %s\n", glGetString(GL_VERSION));
 printf("renderer: %s\n", glGetString(GL_RENDERER));
 printf("samples = %d\n", glutGet(GLUT_WINDOW_NUM_SAMPLES));
 printf("Executable: %d bit\n", (int)(8*sizeof(int*)));
glutIdleFunc(IdleEvent);
 glutDisplayFunc(RenderingEvent);
 glutKeyboardFunc(KeyboardEvent);
// Initialize OpenGL extensions and check for required functionality
 if (!glInitExtensions())
 return 1;
glInitGraphics();
 glutCreateMenu(menu);
 glutAddMenuEntry("[Esc] Quit", 27);
 glutAttachMenu(GLUT_RIGHT_BUTTON);
 glutMainLoop();
 return 0;
}

New C/C++ Compiler Pragma for Mathematically and Algorithm Oriented Code?

August 4, 2015 - Leave a Response

Hi Guys. I was thinking about making our code safer when we sit down and write our mathematically oriented algorithm in C and C++. I was developing and looking at so many algorithmic code in Computer Vision, Computer Graphics, Geometry Processing, Linear Algebra and Machine Learning over 15 years. Algorithmic code tends to have lots of for loops and nested for loops, like in vector and matrix processing, image and computer vision, audio and etc. In this post I suggest A NEW SET OF COMPILER PRAGMA’s for making our code safe and stating clearly our INTENTIONS about what part of the memory is expected to be touched (read-only) or written (read-write) and what values domain is being expected from a line of code. Let’s look at some examples:

Example 1 – We expect that ‘f01’ will have values from -0.5 to 0.5

float f = random(0,2);  // generates a float number between [0 .. 2] inclusive
#pragma expect float between [-0.5,0.5]
float f01 = (f/2)-0.5;  // generates a float number between [-0.5 .. 0.5]
Example 2 – We expect variable A to be <= than variable B
const float F = 2.01;
#pragma expect A lessequal B
Example 3 – We have two matrices: A for read, B for writing. We want to reach each 
value.
float *A = new float[100][100];
float *B = new float[100][100];
// The pragma Syntax
#pragma restrict [readonly | writeonly] [width,height,startColumn,startRow] …
#pragma restrict readonly A [100,100,0,0] writeonly B [98,98,1,1]
for (int r=1; r<100-1; ++r)
  for (int c=1; c<100-1; ++c)
    B[r][c] = 0.25*(A[r-1][c-1]+A[r-1][c+1]+A[r+1][c-1]+A[r+1][c+1]);
Example 4 – Make sure that we wrote each element and only once
// Are we sure that we have touched every element that we wanted to?
int W=256, H=384;
uint8_t *src = malloc(W*H);
#pragma expect writeonly src [0,W*H-1]
for (int i=0; i<w*h; ++<span=”” class=”hiddenGrammarError” pre=”” data-mce-bogus=”1″>i)
src[i] = i;

Computer vision Based Child Road-Safety Educational System

July 5, 2015 - Leave a Response

I am so concerned with the Lives of children and Road Safety. I wish more children will understand how to act in and near roads. I have designed and did not develop yet a budget computerized system based on Image Projector, Basic Electronics, a Laptop Computer and computer-vision software that can help young children practice Road-Safety scenarios in a safe, simulated environment.

The system can be hand-held and constructed in half an hour in every classroom that has floor and basic electricity or electric-generator where there is no electricity. The system contains a mat with sensors, two pedestrian/car crossing lights, laptop with camera, an Image Projector and a software running computer-vision software and OpenGL-based real-time rendering system.

I urge companies and governments to try and analyze the system I devised and maybe implement it as a program in kindergarten and schools.

Gonen

Here is the full system description as a 2.5 Dimension drawing:child-road-safety-educational-system-in-classroom [gonen raveh July 2015][gonen.raveh@gmail.com]

 

Download link of image in high resolution: High Res Image

Target-aware Book and Text Viewer and Reader application

January 5, 2014 - Leave a Response

What if our digital text reader or viewing program like iOS GoodReader, AcrobatReader and etc could highlight parts of the text on each page, according to our preferences. For example: the app could let the user select from Plain mode, Critical Information (numbers, dates, places, people), Environment (the places, walls, paint, day/night, colors, fog, and etc). This valuable feature could allow people to go fast over the highlighted text and automatically the page-summary would pop-out of the page. Business Scenario you have 30 seconds to go over the details of a 2-page text you wrote last week. You ask your viewer to highlight only Dates and Deadlines Information. Highschool and university could go over a technical text and ask only to highlight Formulas, Derivations and Conclusions. What do you think?

What is being used in my Library? Link-aware C/C++/Objective-C Linking process of Third Party Libraries

December 31, 2013 - Leave a Response

What if we could design an API within a C/C++ library, we developed, that “knows” who linked against it and what exported functions and C++ classes have been used?

I think that most of the people and groups, commercial and open source would love this feature because it enables them to know what functions are being used by their customers. A library designer may find, for example, that although he worked very hard at implementing 40 complicated functions, the users of this library use only 4 functions and the simplest ones.

Using tools such as CLANG and LLVM C/C++/Objective-C, we can design a simple protocol:

1. The library can decide to export a “C” function with the following signature: void whoIsUsingMeAtCompileTime() and optionally another function: void whoIsUsingMeAtLinkTime().

When the compiler toolchain finds such functions in a library, it can generate a report named <libraryname>.xml or JSON with the list of functions, symbols, structs, C++ classes and typedefs that were used by the caller. This file can later be moderated by the user/organization before it is send to the HTTPS/Mail address of the library provider.

 

Stereolithography (3D Printing) Algorithms and Thoughts

February 19, 2013 - 14 Responses

Playing with 3D Printing Algorithmic Concepts. Let’s start with what we (the world) already have: lot’s of software, open source and commercial, lot’s of file formats, but STL is still dominant and … lots of 3D Printers. This week, I was looking into the algorithms (Math, Computer Science) behind the world of 3D Printing. As an excersize, I started to explore the mechanics of taking a digital model of an object from an STL file (ThingVerse, F16 Model) and generating slices from this model so a printer could print. Here is a slide of the basic process:

Image

The input: F16.stl (ascii or binary format)
. The output: an ascii or binary file with a set of slices where each slice has a set of line segments. a line segment has 2 coordinates {p0, p1} with {x,y,z} values. Before we dive into the math (simple) and algorithmic issues, let’s see the images: the 3D model, the slices on a 2D image, side-by-side from left-to-right top-to-bottom, the 3D Model constructed by multiple adjacent slices:

Image

Image

Image

The Main Algorithm (Pseudo Code)

v3 -> a vector with 3 floats {x,y,z}

LineSegment -> {v3 point0, point1}

Plane -> {v3 normal, float distance}

Triangle -> {v3 vertices[3], normal}

TriangleMesh -> {vector of Triangle}

read-stl-file -> generate a TriangleMesh ‘TM’

nSlices -> compute-number-of-slices using slice-size and 3D Model aabb

for each slice

– fix-a-cutting-plane using the slice-index

– for each Triangle in TriangleMesh

– – intersect(Triangle, Plane) -> find zero or two intersection points

– – take only the second case and generate a Line Segment from two points

– – Gather all Line Segment for this slice in a vector

output all line segments to an Ascii or Binary file for viewing or manipulating

That’s all. Here are a set of slides that explain the Triangle-Plane-Intersection mecahnics:

ImageImageImageImageImage

Here is the full source code that compiles under Microsoft Visual C++ 2010. Just create a solution and add this code as main.cpp, download the open source Opengl Mathematics c++ library (Header files only) from http://glm.g-truc.net/. After you build it, download any STL file you want.

Adjusting Parameters: use the command line parameters:

-slicesize <size>

-model <stl file name>

-ascii (the default is a binary STL file)

-eulerangles rx ry rz (to rotate the 3D Model before slicing starts)

Please note that this is a demonstration code (and documented) and not a bullet-proof one. It is targeted as an educational and exploratory tool. I hope you have fun with it.

Some more output from the program {Stanford Bunny, Spaceship}

ImageImageImage

Spaceship:

ImageImageImage

The Full Source Code (C++):

#include <fstream>
#include <string>
#include <vector>
#include <math.h>

#include “glm/glm/glm.hpp”
#include “glm/glm/gtc/matrix_transform.hpp”
#include “glm/glm/gtc/type_ptr.hpp”

#define DEG_TO_RAD(x) (x*0.0174532925199f)
using namespace std;

struct v3 {
    float x, y, z;
    v3(float _x=0, float _y=0, float _z=0) : x(_x), y(_y), z(_z) {}
    float dotproduct(const v3 &v) const { return x*v.x + y*v.y + z*v.z; }
    void transform(const glm::mat4 &mat) { glm::vec4 v = glm::vec4(x, y, z, 1.0f), vt; vt = mat*v; x=vt.x; y=vt.y; z=vt.z;}
    v3& operator-=(const v3 &pt) { x-=pt.x; y-=pt.y; z-=pt.z; return *this;    }
    v3 operator-(const v3 &pt) { return v3(x-pt.x, y-pt.y, z-pt.z); }
    v3 operator+(const v3 &pt) { return v3(x+pt.x, y+pt.y, z+pt.z); }
    v3 operator/(float a) { return v3(x/a, y/a, z/a); }
    v3 operator*(float a) { return v3(x*a, y*a, z*a); }
    float normalize() const { return sqrt(x*x+y*y+z*z); }
};
v3 operator-(const v3 &a, const v3 &b) {return v3(a.x-b.x, a.y-b.y, a.z-b.z); }
v3 operator+(const v3 &a, const v3 &b) {return v3(a.x+b.x, a.y+b.y, a.z+b.z); }

struct LineSegment {
    LineSegment(v3 p0=v3(), v3 p1=v3()) { v[0]=p0; v[1]=p1; }
    v3 v[2];
};
class Plane {
public:
    Plane() : mDistance(0) {}
    float distance() const { return mDistance; }
    float distanceToPoint(const v3 &vertex) const {    return vertex.dotproduct(mNormal) – mDistance; }
    void setNormal(v3 normal) { mNormal = normal; }
    void setDistance(float distance) { mDistance = distance; }
protected:
    v3        mNormal;    // normalized Normal-Vector of the plane
    float   mDistance;  // shortest distance from plane to Origin
};
struct Triangle    {
    Triangle(v3 n, v3 v0, v3 v1, v3 v2) : normal(n) { v[0]=v0; v[1]=v1; v[2]=v2; }
    Triangle& operator-=(const v3 &pt) { v[0]-=pt; v[1]-=pt; v[2]-=pt; return *this;}
    void transform(const glm::mat4 &mat) { v[0].transform(mat); v[1].transform(mat); v[2].transform(mat);}
    // @return -1 = all triangle is on plane back side
    //          0 = plane intersects the triangle
    //          1 = all triangle is on plane front side
    //         -2 = error in function
    int intersectPlane(const Plane &plane, LineSegment &ls) const {
        // a triangle has 3 vertices that construct 3 line segments
        size_t cntFront=0, cntBack=0;
        for (size_t j=0; j<3; ++j) {
            float distance = plane.distanceToPoint(v[j]);
            if (distance<0) ++cntBack;
            else ++cntFront;
        }
        if (3 == cntBack) {
            return -1;
        }
        else if (3 == cntFront) {
            return 1;
        }
        size_t lines[] = {0,1,1,2,2,0}; // CCW Triangle
        std::vector<v3> intersectPoints;
        for (size_t i=0; i<3; ++i) {
            const v3 &a = v[lines[i*2+0]];
            const v3 &b = v[lines[i*2+1]];
            const float da = plane.distanceToPoint(a);
            const float db = plane.distanceToPoint(b);
            if (da*db<0) {
                const float s = da/(da-db); // intersection factor (between 0 and 1)
                v3 bMinusa = b-a;
                intersectPoints.push_back(a+bMinusa*s);
            }
            else if (0==da) { // plane falls exactly on one of the three Triangle vertices
                if (intersectPoints.size()<2)
                    intersectPoints.push_back(a);
            }
            else if (0==db) { // plane falls exactly on one of the three Triangle vertices
                if (intersectPoints.size()<2)
                    intersectPoints.push_back(b);
            }
        }
        if (2==intersectPoints.size()) {
            // Output the intersecting line segment object
            ls.v[0]=intersectPoints[0];
            ls.v[1]=intersectPoints[1];
            return 0;
        }
        return -2;        
    }
    v3 v[3], normal;
};
class TriangleMesh {
public:
    TriangleMesh() : bottomLeftVertex(999999,999999,999999), upperRightVertex(-999999,-999999,-999999) {}
    size_t size() const { return mesh.size(); }
    // move 3D Model coordinates to be center around COG(0,0,0)
    void normalize() {
        v3 halfBbox = (upperRightVertex – bottomLeftVertex)/2.0f;
        v3 start = bottomLeftVertex + halfBbox;
        for (size_t i=0; i<mesh.size(); ++i) {
            Triangle &triangle = mesh[i];
            triangle -= start;
        }
        bottomLeftVertex = halfBbox*-1.0f;
        upperRightVertex = halfBbox;
    }
    void push_back(const Triangle &t) {
        mesh.push_back(t);
        for (size_t i=0; i<3; ++i) {
            if (t.v[i].x < bottomLeftVertex.x) bottomLeftVertex.x = t.v[i].x;
            if (t.v[i].y < bottomLeftVertex.y) bottomLeftVertex.y = t.v[i].y;
            if (t.v[i].z < bottomLeftVertex.z) bottomLeftVertex.z = t.v[i].z;
            if (t.v[i].x > upperRightVertex.x) upperRightVertex.x = t.v[i].x;
            if (t.v[i].y > upperRightVertex.y) upperRightVertex.y = t.v[i].y;
            if (t.v[i].z > upperRightVertex.z) upperRightVertex.z = t.v[i].z;
        }
    }
    v3 meshAABBSize() const {
        return v3(upperRightVertex.x-bottomLeftVertex.x, upperRightVertex.y-bottomLeftVertex.y, upperRightVertex.z-bottomLeftVertex.z);
    }
    std::vector<Triangle>& getMesh() { return mesh; }
    const std::vector<Triangle>& getMesh() const { return mesh; }
    v3 getBottomLeftVertex() const { return bottomLeftVertex; }
    v3 getUpperRightVertex() const { return upperRightVertex; }
    // Mesh COG point should be at (0,0,0)
    int transform(const glm::mat4 &mat) {
        for (size_t i=0; i<mesh.size(); ++i) {
            Triangle &triangle = mesh[i];
            triangle.transform(mat);
        }
        return 0;
    }
protected:
    std::vector<Triangle> mesh;
    v3 bottomLeftVertex, upperRightVertex;
};

// read the given STL file name (ascii or binary is set using ‘isBinaryFormat’)
// and generate a Triangle Mesh object in output parameter ‘mesh’
int stlToMeshInMemory(const char *stlFile, TriangleMesh *mesh, bool isBinaryFormat) {    
    if (!isBinaryFormat) {
        ifstream in(stlFile);
        if (!in.good()) return 1;
        char title[80];
        std::string s0, s1;
        float n0, n1, n2, f0, f1, f2, f3, f4, f5, f6, f7, f8;
        in.read(title, 80);
        while (!in.eof()) {
            in >> s0;                                // facet || endsolid
            if (s0==”facet”) {
                in >> s1 >> n0 >> n1 >> n2;            // normal x y z
                in >> s0 >> s1;                        // outer loop
                in >> s0 >> f0 >> f1 >> f2;         // vertex x y z
                in >> s0 >> f3 >> f4 >> f5;         // vertex x y z
                in >> s0 >> f6 >> f7 >> f8;         // vertex x y z
                in >> s0;                            // endloop
                in >> s0;                            // endfacet
                // Generate a new Triangle with Normal as 3 Vertices
                Triangle t(v3(n0, n1, n2), v3(f0, f1, f2), v3(f3, f4, f5), v3(f6, f7, f8));
                mesh->push_back(t);
            }
            else if (s0==”endsolid”) {
                break;
            }
        }
        in.close();
    }
    else {
        FILE *f = fopen(stlFile, “rb”);
        if (!f) return 1;
        char title[80];
        int nFaces;
        fread(title, 80, 1, f);
        fread((void*)&nFaces, 4, 1, f);
        float v[12]; // normal=3, vertices=3*3 = 12
        unsigned short uint16;
        // Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
        for (size_t i=0; i<nFaces; ++i) {
            for (size_t j=0; j<12; ++j) {
                fread((void*)&v[j], sizeof(float), 1, f);
            }
            fread((void*)&uint16, sizeof(unsigned short), 1, f); // spacer between successive faces
            Triangle t(v3(v[0], v[1], v[2]), v3(v[3], v[4], v[5]), v3(v[6], v[7], v[8]), v3(v[9], v[10], v[11]));
            mesh->push_back(t);
        }
        fclose(f);
    }    
    mesh->normalize();
    return 0;
}

// Take an input Triangle Mesh ‘mesh’ and fill the output
// parameter ‘slicesWithLineSegments’ with line segments for
// each slice
int triMeshSlicer(
    const TriangleMesh                        *mesh,        // the const input mesh
    std::vector<std::vector<LineSegment>>    &slicesWithLineSegments,
                                                        // the result slices
    const float                                sliceSize) {// slice size in 3D Model digital units
    Plane plane;                                        // The intersection plane
    plane.setNormal(v3(0, 0, 1));                        // normal does not change during slicing
    const v3 aabb = mesh->meshAABBSize();                // as the model for it’s 3D axis-aligned bounding-box
    const size_t nSlices = 1+(int)(aabb.z/sliceSize);    // compute number of output slices
    const std::vector<Triangle> &m = mesh->getMesh();    // get a const handle to the input mesh
    const float z0 = mesh->getBottomLeftVertex().z;        // find the minimal z coordinate of the model (z0)
    for (size_t i=0; i<nSlices; ++i) {                    // start generating slices
        std::vector<LineSegment> linesegs;                // the linesegs vector for each slice
        plane.setDistance(z0+(float)i*sliceSize);        // position the plane according to slice index
        for (size_t t=0; t<m.size(); ++t) {                // iterate all mesh triangles
            const Triangle &triangle = m[t];            // get a const handle to a triangle
            LineSegment ls;                    
            if (0==triangle.intersectPlane(plane, ls)) {// the plane does intersect the triangle
                linesegs.push_back(ls);                    // push a new Line Segment object to this slice
            }
        }
        slicesWithLineSegments.push_back(linesegs);        // push this vector to the slices vector
    }
    return 0;
}

// GIV is a free viewer: http://giv.sourceforge.net/giv/
// output a single GIV ascii file with the slices, layed out in a matrix form
int    exportSingleGIVFormat(
    const std::vector<std::vector<LineSegment>> &slicesWithLineSegments,
    const v3                                    &aabbSize) {
    char filename[256];
    FILE *f=NULL;
    float dx=0, dy=0;
    f=fopen(“out.marks”, “w”);
    if (!f)    return 1;
    const size_t nSlices = slicesWithLineSegments.size();
    const size_t slicePerRow = (size_t)sqrt((float)nSlices);
    for (size_t i=0; i<nSlices; ++i) {
        const std::vector<LineSegment> &lss = slicesWithLineSegments[i];
        dx = (float)(i%slicePerRow)*(aabbSize.x*1.05f);
        dy = (float)(i/slicePerRow)*(aabbSize.y*1.05f);
        for (size_t j=0; j<lss.size(); ++j) {
            fprintf(f, “\n\n$line”);
            fprintf(f, “\n$color blue”);
            fprintf(f, “\n%f %f”, dx+lss[j].v[0].x, dy+lss[j].v[0].y);
            fprintf(f, “\n%f %f”, dx+lss[j].v[1].x, dy+lss[j].v[1].y);
        }
    }
    fclose(f);
    return 0;
}

// GIV is a free viewer: http://giv.sourceforge.net/giv/
// output a single GIV ascii file with the slices, layed out in 3D form
int    exportSingleGIVFormat3D(
    const std::vector<std::vector<LineSegment>> &slicesWithLineSegments,
    const v3                                    &aabbSize) {
    // Generate a 3D View using OpenGL Math Library
    glm::detail::tvec3<float> eulerAngles(0, 0.0f, 45.0f);
    glm::detail::tquat<float> quaternion = glm::detail::tquat<float>(DEG_TO_RAD(eulerAngles)); // This glm func wants values as radians
    float angle = glm::angle(quaternion);
    glm::detail::tvec3<float> axis = glm::axis(quaternion);

    glm::mat4 View        = glm::rotate(glm::mat4(1.0), angle, axis);
    glm::mat4 Projection  = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.f);
    glm::mat4 Model       = glm::lookAt(    
        glm::vec3(1, 1, 1),    // Eye point (where am I?)
        glm::vec3(0, 0, 0),    // Look at Center point (What am I looking at?)
        glm::vec3(0, 1, 0));// UP vector of the camera (Where is my UP vector?)
    glm::mat4 MVP = Projection * View * Model;

    // Start Output
    char filename[256];
    FILE *f=NULL;
    v3 p0, p1;
    f=fopen(“out3D.marks”, “w”);
    if (!f)    return 1;
    const size_t nSlices = slicesWithLineSegments.size();
    const size_t slicePerRow = (size_t)sqrt((float)nSlices);
    for (size_t i=0; i<nSlices; ++i) {
        const std::vector<LineSegment> &lss = slicesWithLineSegments[i];
        for (size_t j=0; j<lss.size(); ++j) {
            p0=lss[j].v[0];
            p1=lss[j].v[1];
            p0.transform(MVP);
            p1.transform(MVP);
            fprintf(f, “\n\n$line”);
            fprintf(f, “\n$color blue”);
            fprintf(f, “\n%f %f”, p0.x, p0.y);
            fprintf(f, “\n%f %f”, p1.x, p1.y);
        }
    }
    fclose(f);
    return 0;
}

int main(int argc, char **argv) {
    float    sliceSize = 1.0f;
    char    modelFileName[1024];    
    bool    isBinaryFormat = true;
    glm::detail::tvec3<float> eulerAngles(0,0,0);

    for (int i=1; i<argc;) {
        if (0==strcmp(argv[i], “-slicesize”)) {
            sliceSize = atof(argv[i+1]);
            i+=2;
        }
        else if (0==strcmp(argv[i], “-model”)) {
            strcpy(modelFileName, argv[i+1]);
            i+=2;
        }
        else if (0==strcmp(argv[i], “-ascii”)) {
            isBinaryFormat = false;
            i+=1;
        }
        else if (0==strcmp(argv[i], “-eulerangles”)) {
            eulerAngles.x = atof(argv[i+1]);
            eulerAngles.y = atof(argv[i+2]);
            eulerAngles.z = atof(argv[i+3]);
            i+=4;
        }
        else {
            ++i;
        }
    }
    // Parse the file and generate a TriangleMesh
    TriangleMesh mesh;
    if (stlToMeshInMemory(modelFileName, &mesh, isBinaryFormat)!=0)
        return 1;
    fprintf(stderr, “Mesh has %d triangles\n”, mesh.size());

    // Optional Model Rotation Around COG Point using GLM Library
    glm::mat4 mat = glm::mat4(1.0f);
    glm::detail::tquat<float> quaternion = glm::detail::tquat<float>(DEG_TO_RAD(eulerAngles)); // This glm func wants values as radians
    float angle = glm::angle(quaternion);
    glm::detail::tvec3<float> axis = glm::axis(quaternion);
    mat = glm::rotate(mat, angle, axis);
    mesh.transform(mat);

    // Generate Slices
    std::vector<std::vector<LineSegment>> slicesWithLineSegments;
    triMeshSlicer(&mesh, slicesWithLineSegments, sliceSize);
    // Output in 2D Matrix form
    exportSingleGIVFormat(slicesWithLineSegments, mesh.meshAABBSize());
    // Output in 3D Layered Form
    exportSingleGIVFormat3D(slicesWithLineSegments, mesh.meshAABBSize());
    return 0;
}

Graphical Programming Computer Vision and Linear Algebra Applications using OpenBlocks

May 30, 2012 - One Response

As a computer vision algorithm developer and an enthusiast software engineer looking almost at every major way to program algorithms, I was fascinated by the early NetLogo software and the more recent Scratch (by MIT). OpenBlocks is an open source Java software and framework for graphic programming (http://education.mit.edu/openblocks). I started my research by learning the XML language definition of OpenBlocks and generated three new block-categories for Computer Vision and a basic Linear Algebra. In this post you can see some screen capture of the different elements I have added and an example algorithm. The first algorithm is the classic Conjugate Gradient optimization algorithm. Image

Defining new blocks for Computer Vision Algorithms

Here are few screen captures of the new block-groups I have defined. The first one supports loading, saving and creating 2D images:

Image

The second block group supports the conversion between image types:

Image

The third group enable doing for-each-pixel operations on a single image or between pairs of images:

Image

The last 2 images show some basic and classical vector and linear algebra (BLAS Level 1) operations:

ImageImage

Discussion

The current status is that I only “play” with these definition to deeply understand if this graphical method of programming really changes the way we think, develop and generate automatic C++ or CUDA code from our graphical drawings. Producing the blocks is a very think-full task in order to define the basic and minimal set of blocks that span the space, like the BLAS Level 1,2 and 3 set of functions. For Computer Vision this is now defined yet by the community so we will have to wait and see if it will be OpenCV or the Khronos Group newly emerging Vision API.

The Source Code

Please feel free to email me for the full code and examples: gonen dot raveh at gmail dot com

 

Megalui: 2D/3D OpenGL & C/C++ Library for Scientific Visualizations

January 17, 2012 - Leave a Response

I recently published the first production stable version of Megalui: 2D/3D OpenGL & C/C++ Library for Scientific Visualizations. Here is the projects website: https://sourceforge.net/projects/megalui/Image

On the Ability of Young Children to Explain and Program the Behavior of a Virtual Robot using the Robot-Park Tangible Programming Language

December 20, 2011 - Leave a Response

Here is the Full PowerPoint Presentation in English of my Master Thesis. Joint work with my adviser professor David Mioduser.