Repository: tyt2y3/vaserenderer
Branch: master
Commit: 93d291ccc7ff
Files: 86
Total size: 385.6 KB
Directory structure:
gitextract_m2dfrshv/
├── .gitignore
├── LICENSE
├── README.md
├── cpp/
│ ├── samples/
│ │ ├── Makefile
│ │ ├── png_readwrite.cpp
│ │ ├── polyline
│ │ ├── polyline.cpp
│ │ ├── readme.txt
│ │ ├── samples
│ │ ├── samples.cpp
│ │ ├── segment
│ │ ├── segment.cpp
│ │ └── test1_base.cpp
│ ├── vaser/
│ │ ├── agg_curve4.cpp
│ │ ├── backend.h
│ │ ├── color.h
│ │ ├── curve.cpp
│ │ ├── gradient.cpp
│ │ ├── opengl.cpp
│ │ ├── point.h
│ │ ├── polyline.cpp
│ │ ├── vaser.cpp
│ │ ├── vaser.h
│ │ └── vertex_array_holder.h
│ └── workbench/
│ ├── base.html
│ ├── dragger.js
│ ├── drawer.js
│ ├── gradient_along_path.html
│ ├── knife_cut_test
│ ├── knife_cut_test.cpp
│ ├── layout.css
│ ├── outward_vector.html
│ ├── quad_reflex.html
│ ├── raphael-min.js
│ ├── readme.txt
│ ├── triangle_knife_cut.js
│ ├── triangles_dual_knife_cut.html
│ ├── triangles_knife_cut.html
│ └── vector_arc.html
├── csharp/
│ ├── Assets/
│ │ ├── Demo/
│ │ │ ├── Demo.cs
│ │ │ ├── Demo.cs.meta
│ │ │ ├── demo.unity
│ │ │ └── demo.unity.meta
│ │ ├── Demo.meta
│ │ ├── Resources/
│ │ │ ├── Vaser/
│ │ │ │ ├── Fade.mat
│ │ │ │ ├── Fade.mat.meta
│ │ │ │ ├── Fade.shader
│ │ │ │ └── Fade.shader.meta
│ │ │ └── Vaser.meta
│ │ ├── Resources.meta
│ │ ├── Vaser/
│ │ │ ├── Gradient.cs
│ │ │ ├── Gradient.cs.meta
│ │ │ ├── Polybezier.cs
│ │ │ ├── Polybezier.cs.meta
│ │ │ ├── Polyline.cs
│ │ │ ├── Polyline.cs.meta
│ │ │ ├── Vec2Ext.cs
│ │ │ ├── Vec2Ext.cs.meta
│ │ │ ├── VertexArrayHolder.cs
│ │ │ └── VertexArrayHolder.cs.meta
│ │ └── Vaser.meta
│ ├── Docs/
│ │ └── README.md
│ ├── Packages/
│ │ └── manifest.json
│ ├── ProjectSettings/
│ │ ├── AudioManager.asset
│ │ ├── ClusterInputManager.asset
│ │ ├── DynamicsManager.asset
│ │ ├── EditorBuildSettings.asset
│ │ ├── EditorSettings.asset
│ │ ├── GraphicsSettings.asset
│ │ ├── InputManager.asset
│ │ ├── NavMeshAreas.asset
│ │ ├── Physics2DSettings.asset
│ │ ├── PresetManager.asset
│ │ ├── ProjectSettings.asset
│ │ ├── ProjectVersion.txt
│ │ ├── QualitySettings.asset
│ │ ├── TagManager.asset
│ │ ├── TimeManager.asset
│ │ ├── UnityConnectSettings.asset
│ │ ├── VFXManager.asset
│ │ └── XRSettings.asset
│ └── README.md
└── docs/
├── API.html
├── getting_started.html
├── index.html
└── style.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
#
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Mm]emoryCaptures/
# Never ignore Asset meta data
!/[Aa]ssets/**/*.meta
# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*
# Autogenerated Jetbrains Rider plugin
[Aa]ssets/Plugins/Editor/JetBrains*
# Visual Studio cache directory
.vs/
# Gradle cache directory
.gradle/
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db
# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta
# Unity3D generated file on crash reports
sysinfo.txt
# Builds
*.apk
*.unitypackage
# Crashlytics generated file
crashlytics-build.properties
# Sublime
*.sublime*
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2023 TSANG, Hao Fung (tyt2y7@gmail.com)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
> This project was started many, many years ago. The C++ implementation is a pre-OpenGL 3 relic. The C# implementation is more "modern" and uses fragment shader, which is better.
VASE renderer
About
VASE renderer version 0.42 (VASEr 0.42) is a tessellating library for rendering high quality 2D vector graphics. It is an attempt to address unconventional features in 2d graphics. It is intended for OpenGL 1.1+, but much of the code is API independent C++.
Unconventional features
Per vertex coloring and weighting
VASEr revolutionarily lets you control the color and thickness at each vertex for a polyline. This feature is unseen on any commonly available graphics library.
Linear gradient along curve
A similar feature is also available to curves. Because there are so many vertices on a curve that it is impractical to specify data on each point, VASEr lets you define a linear gradient of color and thickness along the length of a curve, giving you two more degrees of freedom.
Feathering for brush like effects
VASEr must use an outsetting polygon anyway, so it is free (as in computational cost) to scale it up, and it is called feathering in VASEr.
Premium quality anti aliasing
From left to right: raw polygon without anti aliasing,
anti aliased with outset fade polygon,
exaggerated outsetting polygon with wireframe,
same as left without wireframe.
Outset-fade polygon is the high quality, fast, portable, consistent and hassle free technique for anti aliasing. The difficulties are to calibrate the outsetting distance to achieve believable result and to perform tedious tessellation. Luckily VASEr did this for you.
Below are line rendering comparison between VASEr and Cairo and AGG:
VASEr
Cairo
AGG
There is small difference, but it is more a matter of taste than correctness. In terms of clarity VASEr is like between Cairo and AGG, and VASEr is the most crisp among the three. VASEr even do pixel alignment to 1px completely horizontal or vertical lines.
The flaw is, because the outsetting distance is resolution dependent, while the triangulation is unaffected under rotational transformation, fidelity will be lost under scale and sheer.
Articles
+
Drawing polylines by tessellation.
+
Drawing nearly perfect 2D line segments in OpenGL
Related Work
If you only need uniform color & width and only wanted a clean mesh, you can try out Polyline2D .
Acknowledgement
Bezier curve subdivision code is extracted from Anti-Grain Geometry V2.4 by Maxim Shemanarev (McSeem).
The open-source graphics library AGG inspired me a lot during my teenage.
================================================
FILE: cpp/samples/Makefile
================================================
CPP = polyline.cpp segment.cpp samples.cpp
BIN = $(CPP:.cpp=)
VASER = ../vaser/*
all: $(BIN)
polyline: polyline.cpp png_readwrite.cpp test1_base.cpp $(VASER)
fltk-config --use-gl --use-images --compile polyline.cpp
segment: segment.cpp test1_base.cpp $(VASER)
fltk-config --use-gl --compile segment.cpp
samples: samples.cpp test1_base.cpp $(VASER)
fltk-config --use-gl --compile samples.cpp
win32: $(CPP) $(VASER)
$(foreach var,$(BIN),g++ -I/usr/local/include -mwindows -DWIN32 -DUSE_OPENGL32 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -o $(var) $(var).cpp -mwindows /usr/local/lib/libfltk_gl.a -static -lpng -lz -lglu32 -lopengl32 /usr/local/lib/libfltk.a -lole32 -luuid -lcomctl32 -static-libgcc -static-libstdc++ & )
================================================
FILE: cpp/samples/png_readwrite.cpp
================================================
#include
#include
int // 0 on failure, 1 on success
save_png(const char *filename, // filename
unsigned char* buffer, // buffer of an image
int w, // width of image
int h, // height
int b) // color depth/ bytes per pixel
{
int y; // Current row
const unsigned char *ptr; // Pointer to image data
FILE *fp; // File pointer
png_structp pp; // PNG data
png_infop info; // PNG image info
if ( !filename)
{
printf("invalid filename.\n");
return 0;
}
if ( !buffer)
{
printf("null pointer buffer.\n");
return 0;
}
// Create the output file...
if ((fp = fopen(filename, "wb")) == NULL)
{
printf("Unable to create PNG image.\n");
return (0);
}
// Create the PNG image structures...
pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!pp)
{
fclose(fp);
printf("Unable to create PNG data.\n");
return (0);
}
info = png_create_info_struct(pp);
if (!info)
{
fclose(fp);
png_destroy_write_struct(&pp, 0);
printf("Unable to create PNG image information.\n");
return (0);
}
if (setjmp(png_jmpbuf(pp)))
{
fclose(fp);
png_destroy_write_struct(&pp, &info);
printf("Unable to write PNG image.\n");
return (0);
}
png_init_io(pp, fp);
png_set_compression_level(pp, Z_BEST_COMPRESSION);
png_set_IHDR(pp, info, w, h, 8,
b==3? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_set_sRGB(pp, info, PNG_sRGB_INTENT_PERCEPTUAL);
png_set_sRGB_gAMA_and_cHRM(pp, info, PNG_INFO_sRGB);
png_write_info(pp, info);
ptr = buffer;
for (y = 0; y < h; y ++)
{
png_write_row(pp, (png_byte *)ptr);
ptr += w*b;
}
png_write_end(pp, info);
png_destroy_write_struct(&pp, 0);
fclose(fp);
return (1);
}
unsigned char* // 0 on failure, address of memory buffer on success
// must *delete[]* the buffer manually after use
read_png( const char* filename, // filename
int* width, // store image width into pointer
int* height, // store image height into pointer
int* channels) // number of channels
{
int i; // Looping var
FILE *fp; // File pointer
png_structp pp; // PNG read pointer
png_infop info; // PNG info pointers
png_bytep *rows; // PNG row pointers
// Open the PNG file...
if ((fp = fopen(filename, "rb")) == NULL)
{
printf( "Unable to open file %s.\n", filename);
return 0;
}
// Setup the PNG data structures...
pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info = png_create_info_struct(pp);
if (setjmp(pp->jmpbuf))
{
printf("Unable to read. png file %s contains errors.\n", filename);
return 0;
}
// Initialize the PNG read "engine"...
png_init_io(pp, fp);
// Get the image dimensions and convert to grayscale or RGB...
png_read_info(pp, info);
if (info->color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(pp);
if (info->color_type & PNG_COLOR_MASK_COLOR)
*channels = 3;
else
*channels = 1;
if ((info->color_type & PNG_COLOR_MASK_ALPHA) || info->num_trans)
(*channels) ++;
if ( *channels != 3 && *channels != 4)
{
printf("png image other than 3 or 4 channels is not supported.\n");
return 0;
}
*width = info->width;
*height = info->height;
if (info->bit_depth < 8)
{
png_set_packing(pp);
png_set_expand(pp);
}
else if (info->bit_depth == 16)
png_set_strip_16(pp);
// Handle transparency...
if (png_get_valid(pp, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pp);
unsigned char* array = new unsigned char[(*width) * (*height) * (*channels)];
// Allocate pointers...
rows = new png_bytep[(*height)];
for (i = 0; i < (*height); i ++)
rows[i] = (png_bytep)(array + i * (*width) * (*channels));
// Read the image, handling interlacing as needed...
for (i = png_set_interlace_handling(pp); i > 0; i --)
png_read_rows(pp, rows, NULL, (*height));
#ifdef WIN32
// Some Windows graphics drivers don't honor transparency when RGB == white
if (*channels == 4) {
// Convert RGB to 0 when alpha == 0...
unsigned char* ptr = (unsigned char*) array;
for (i = (*width) * (*height); i > 0; i --, ptr += 4)
if (!ptr[3]) ptr[0] = ptr[1] = ptr[2] = 0;
}
#endif //WIN32
// Free memory
delete[] rows;
png_read_end(pp, info);
png_destroy_read_struct(&pp, &info, NULL);
fclose(fp);
return array;
}
================================================
FILE: cpp/samples/polyline.cpp
================================================
/*config.h is generated by fltk in your system
* this file is used with fltk 1.3 with gl enabled.
* compile by: fltk-config --use-gl --use-images --compile polyline.cpp
* or something like: g++ -lX11 -lGL -lpng 'polyline.cpp' -o 'polyline'
*/
#include
#include
#include
#include "config.h" //config.h must always be placed before any Fl header
#include
#include
#include
#include
#include
#include
namespace VASEr
{
struct Vec2 { double x,y;};
struct Color { float r,g,b,a;};
}
#define VASER_DEBUG
#include "../vaser/vaser.cpp"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include "png_readwrite.cpp"
using namespace VASEr;
void test_draw();
#include "test1_base.cpp"
const int buf_size=20;
Vec2 AP[buf_size];
int size_of_AP=0;
Color AC[buf_size];
double AW[buf_size];
Fl_Window* main_wnd;
Gl_Window* gl_wnd;
Fl_Slider *weight, *feathering;
Fl_Button *poly, *bezi;
Fl_Button *feather, *no_feather_at_cap, *no_feather_at_core;
Fl_Button *jt_miter, *jt_bevel, *jt_round;
Fl_Button *jc_butt, *jc_round, *jc_square, *jc_rect;
Fl_Button *jc_both, *jc_first, *jc_last, *jc_none;
Fl_Button *colored, *alphaed, *weighted;
Fl_Button *skeleton, *triangulate;
Fl_Slider *bz_ap, *bz_ag, *bz_cu;
void line_update()
{
Color cc[3];
Color grey = {.4,.4,.4, 1};
{ Color col={1 , 0, 0, 1}; cc[0]=col;}
{ Color col={.8,.8, 0, 1}; cc[1]=col;}
{ Color col={ 0, 0, 1, 1}; cc[2]=col;}
for ( int i=0; ivalue())
AC[i] = cc[i%3];
else
AC[i] = grey;
if ( alphaed->value())
AC[i].a = 0.5f;
else
AC[i].a = 1.0f;
if ( weighted->value())
{
AW[i] = weight->value() * (0.05 + double(i*2)/size_of_AP);
}
else
{
AW[i] = weight->value();
}
}
}
void line_init( int N)
{
switch (N)
{
case 2:
AP[0].x=180; AP[0].y=270;
AP[1].x=220; AP[1].y=30;
size_of_AP = 2;
break;
case 3:
AP[0].x=200; AP[0].y=100;
AP[1].x=100; AP[1].y=200;
AP[2].x=300; AP[2].y=200;
size_of_AP = 3;
break;
case 4:
AP[0].x=200; AP[0].y=50;
AP[1].x=100; AP[1].y=150;
AP[2].x=300; AP[2].y=150;
AP[3].x=200; AP[3].y=250;
size_of_AP = 4;
break;
case 5:
AP[0].x=60; AP[0].y=250;
AP[1].x=120; AP[1].y=50;
AP[2].x=180; AP[2].y=250;
AP[3].x=240; AP[3].y=50;
AP[4].x=300; AP[4].y=250;
size_of_AP = 5;
break;
case 6:
AP[0].x=280; AP[0].y=110;
AP[1].x=200; AP[1].y=50;
AP[2].x=100; AP[2].y=150;
AP[3].x=300; AP[3].y=150;
AP[4].x=200; AP[4].y=250;
AP[5].x=120; AP[5].y=190;
size_of_AP = 6;
break;
case 7:
AP[0].x=280; AP[0].y=110;
AP[1].x=200; AP[1].y=50;
AP[2].x=100; AP[2].y=50;
AP[3].x=200; AP[3].y=150;
AP[4].x=300; AP[4].y=250;
AP[5].x=200; AP[5].y=250;
AP[6].x=120; AP[6].y=190;
size_of_AP = 7;
break;
}
line_update();
gl_wnd->set_drag_target( AP, size_of_AP);
}
char get_joint_type()
{
if ( jt_miter->value())
return PLJ_miter;
else if ( jt_bevel->value())
return PLJ_bevel;
else if ( jt_round->value())
return PLJ_round;
else
return 0;
}
char get_cap_type()
{
char cap;
if ( jc_butt->value())
cap=PLC_butt;
else if ( jc_round->value())
cap=PLC_round;
else if ( jc_square->value())
cap=PLC_square;
else if ( jc_rect->value())
cap=PLC_rect;
if ( jc_both->value())
cap+=PLC_both;
else if ( jc_first->value())
cap+=PLC_first;
else if ( jc_last->value())
cap+=PLC_last;
else if ( jc_none->value())
cap+=PLC_none;
return cap;
}
void np_cb(Fl_Widget* W, void*)
{
int n=3;
sscanf( W->label(), "%d", &n);
line_init(n);
if( n<4)
{
poly->value(1);
bezi->value(0);
}
line_update();
gl_wnd->redraw();
}
void line_bezier(Fl_Widget* W, void*)
{
gl_wnd->redraw();
}
void drag_cb(Fl_Widget* W, void*)
{
line_update();
gl_wnd->redraw();
}
void exportimg_cb(Fl_Widget* W, void*)
{
Image img = renderer::get_image();
if( img.buf)
{
save_png("capture.png",img.buf,img.width,img.height,4);
free(img.buf);
}
}
void make_form()
{
Fl_Tabs* tabs = new Fl_Tabs(400,0,200,300);
{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, "config");
Fl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,"general configurations");
//weight
weight = new Fl_Value_Slider(400,50,200,20,"weight");
weight->type(FL_HOR_SLIDER);
weight->bounds(0.02,30.0);
weight->callback(drag_cb);
weight->value(8.0);
{ Fl_Group* o = new Fl_Group(400,90,200,20);
poly = new Fl_Radio_Light_Button(400,90,100,20,"polyline");
bezi = new Fl_Radio_Light_Button(500,90,100,20,"polybezier");
poly->callback(line_bezier);
bezi->callback(line_bezier);
poly->value(1);
o->end();
}
//number of points
(new Fl_Button(400,110,80,20,"2 points"))->callback(np_cb);
(new Fl_Button(480,110,20,20,"3"))->callback(np_cb);
(new Fl_Button(500,110,20,20,"4"))->callback(np_cb);
(new Fl_Button(520,110,20,20,"5"))->callback(np_cb);
(new Fl_Button(540,110,20,20,"7"))->callback(np_cb);
//test options
colored = new Fl_Light_Button(400,130,60,20,"colored");
colored->callback(drag_cb);
colored->value(1);
alphaed = new Fl_Light_Button(460,130,70,20,"alpha-ed");
alphaed->callback(drag_cb);
alphaed->value(1);
weighted = new Fl_Light_Button(530,130,70,20,"weighted");
weighted->callback(drag_cb);
skeleton = new Fl_Light_Button(400,150,80,20,"skeleton");
skeleton->value(0);
skeleton->callback(drag_cb);
triangulate = new Fl_Light_Button(480,150,120,20,"triangulation");
triangulate->value(0);
triangulate->callback(drag_cb);
//export button
Fl_Button *exportimg;
exportimg = new Fl_Button(400,170,200,20,"export image");
exportimg->callback(exportimg_cb);
tab->end();}
{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, "polyline");
Fl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,"polyline_opt");
//feathering
feather = new Fl_Light_Button(400,50,100,20,"feather");
feather->value(1);
feather->callback(drag_cb);
feathering = new Fl_Value_Slider(400,70,200,20,"feathering");
feathering->type(FL_HOR_SLIDER);
feathering->bounds(1.0,10.0);
feathering->callback(drag_cb);
feathering->value(1.0);
no_feather_at_cap = new Fl_Light_Button(430,110,170,15,"no_feather_at_cap");
no_feather_at_core = new Fl_Light_Button(430,125,170,15,"no_feather_at_core");
no_feather_at_cap ->value(0);
no_feather_at_core->value(0);
no_feather_at_cap ->callback(drag_cb);
no_feather_at_core->callback(drag_cb);
//joint type
{ Fl_Group* o = new Fl_Group(400,145,200,30);
new Fl_Box(400,145,80,15,"joint type");
jt_miter = new Fl_Radio_Light_Button(420,160,60,15,"miter");
jt_bevel = new Fl_Radio_Light_Button(480,160,60,15,"bevel");
jt_round = new Fl_Radio_Light_Button(540,160,60,15,"round");
o->end();
jt_miter->value(1);
jt_miter->callback(drag_cb);
jt_bevel->callback(drag_cb);
jt_round->callback(drag_cb);
}
//cap type
{ Fl_Group* o = new Fl_Group(400,180,200,45);
new Fl_Box(400,180,80,15,"cap type");
jc_butt = new Fl_Radio_Light_Button(440,195,80,15,"butt");
jc_round = new Fl_Radio_Light_Button(520,195,80,15,"round");
jc_square = new Fl_Radio_Light_Button(440,210,80,15,"square");
jc_rect = new Fl_Radio_Light_Button(520,210,80,15,"rect");
o->end();
jc_butt ->value(1);
jc_butt ->callback(drag_cb);
jc_round ->callback(drag_cb);
jc_square ->callback(drag_cb);
jc_rect ->callback(drag_cb);
}
{ Fl_Group* o = new Fl_Group(400,230,200,45);
new Fl_Box(400,230,80,15,"cap parts");
jc_both = new Fl_Radio_Light_Button(440,245,80,15,"both");
jc_first = new Fl_Radio_Light_Button(520,245,80,15,"first");
jc_last = new Fl_Radio_Light_Button(440,260,80,15,"last");
jc_none = new Fl_Radio_Light_Button(520,260,80,15,"none");
jc_both->value(1);
jc_both ->callback(drag_cb);
jc_first->callback(drag_cb);
jc_last ->callback(drag_cb);
jc_none ->callback(drag_cb);
}
tab->end();}
/*{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, "BZ");
Fl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,"polybezier_opt");
bz_ap = new Fl_Value_Slider(400,70,200,15,"approximation_scale");
bz_ag = new Fl_Value_Slider(400,105,200,15,"angle_tolerance");
bz_cu = new Fl_Value_Slider(400,140,200,15,"cusp_limit");
bz_ap->type(FL_HOR_SLIDER);
bz_ag->type(FL_HOR_SLIDER);
bz_cu->type(FL_HOR_SLIDER);
bz_ap->bounds(0.1,5.0); bz_ap->value(BZ_default_approximation_scale); bz_ap->callback(drag_cb);
bz_ag->bounds(0.01,0.52); bz_ag->value(BZ_default_angle_tolerance); bz_ag->callback(drag_cb);
bz_cu->bounds(0.1,5.0); bz_cu->value(BZ_default_cusp_limit); bz_cu->callback(drag_cb);
tab->end();}*/
/*{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, "TS");
Fl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,"tessellator");
tab->end();}*/
tabs->end();
}
void test_draw()
{ //main rendering
renderer::before();
polyline_opt opt={0};
tessellator_opt tess={0};
opt.feather = feather->value();
opt.feathering = feathering->value();
opt.no_feather_at_cap = no_feather_at_cap->value();
opt.no_feather_at_core = no_feather_at_core->value();
opt.joint = get_joint_type();
opt.cap = get_cap_type();
opt.tess = &tess;
tess.triangulation = triangulate->value();
Color cc={1,1,1,1};
if ( bezi->value())
{
polybezier_opt bz_opt={0};
bz_opt.poly = &opt;
//bz_opt.approximation_scale = bz_ap->value();
//bz_opt.angle_tolerance = bz_ag->value();
//bz_opt.cusp_limit = bz_cu->value();
gradient grad = {0};
gradient_stop stop[10] = {0};
grad.stops = stop;
grad.length = 10;
stop[0].t = 0.00; stop[0].type = GS_rgba; stop[0].color = AC[0];
stop[1].t = 0.33; stop[1].type = GS_rgba; stop[1].color = AC[1];
stop[2].t = 0.66; stop[2].type = GS_rgba; stop[2].color = AC[2];
stop[3].t = 1.00; stop[3].type = GS_rgba; stop[3].color = AC[3];
stop[4].t = 0.00; stop[4].type = GS_weight; stop[4].weight = AW[0];
stop[5].t = 0.33; stop[5].type = GS_weight; stop[5].weight = AW[1];
stop[6].t = 0.66; stop[6].type = GS_weight; stop[6].weight = AW[2];
stop[7].t = 1.00; stop[7].type = GS_weight; stop[7].weight = AW[3];
polybezier( AP, &grad, size_of_AP, &bz_opt);
Color black={0,0,0,1};
if ( skeleton->value())
{
polybezier( AP, cc, 1.0, size_of_AP, 0);
polyline( AP, black, 1.0, size_of_AP, 0); //control lines
}
}
else
{
polyline( AP, AC, AW, size_of_AP, &opt);
if ( skeleton->value())
polyline( AP, cc, 1.0, size_of_AP, 0);
}
renderer::after();
}
int main(int argc, char **argv)
{
main_wnd = new Fl_Window( 600,300,"VASEr - polyline and bezier example");
make_form(); //initialize
gl_wnd = new Gl_Window( 0,0,400,300); gl_wnd->end(); //create gl window
line_init(4);
main_wnd->end();
main_wnd->show();
main_wnd->redraw();
return Fl::run();
}
================================================
FILE: cpp/samples/readme.txt
================================================
FLTK 1.3.x is required http://www.fltk.org/
libpng is required for png support
================================================
FILE: cpp/samples/samples.cpp
================================================
/*config.h is generated by fltk in your system
* this file is used with fltk 1.3 with gl enabled.
* compile by: fltk-config --use-gl --compile samples.cpp
*/
#include
#include
#include
#include "config.h" //config.h must always be placed before any Fl header
#include
#include
#include
namespace VASEr
{
struct Vec2 { double x,y;};
struct Color { float r,g,b,a;};
}
#include "../vaser/vaser.cpp"
using namespace VASEr;
void test_draw();
#include "test1_base.cpp"
Fl_Window* main_wnd;
Gl_Window* gl_wnd;
int current=0;
void drag_cb(Fl_Widget* W, void*)
{
sscanf( W->label(), "sample%d", ¤t);
gl_wnd->redraw();
}
void make_form()
{
for( int i=0; i<6; i++)
{
char* buf=(char*)malloc(10);
snprintf(buf, 10, "sample%d",i);
(new Fl_Button(600,20*i,100,20,buf))->callback(drag_cb);
}
}
void sample0()
{
int size_of_AP=4;
Vec2 AP[size_of_AP];
AP[0].x=200; AP[0].y=50;
AP[1].x=100; AP[1].y=150;
AP[2].x=300; AP[2].y=150;
AP[3].x=200; AP[3].y=250;
Color col={ 0.5, 0.8, 1.0, 1};
polyline( AP, col, 8.0, size_of_AP, 0);
}
void sample1()
{
int size_of_AP=4;
Vec2 AP[size_of_AP];
AP[0].x=200; AP[0].y=50;
AP[1].x=100; AP[1].y=150;
AP[2].x=300; AP[2].y=150;
AP[3].x=200; AP[3].y=250;
Color AC[size_of_AP];
{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}
{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}
{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}
{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}
double AW[size_of_AP];
AW[0] = 1.0;
AW[1] = 15.0;
AW[2] = 15.0;
AW[3] = 1.0;
polyline( AP, AC, AW, size_of_AP, 0);
}
void sample2()
{ //spectrum
for ( int i=0; i < 20; i++)
{
Vec2 P1 = { 5+29.7*i, 187};
Vec2 P2 = { 35+29.7*i, 8};
Color C1 = { 1.0,0.0,0.5, 1.0};
Color C2 = { 0.5,0.0,1.0, 1.0};
double W1= 0.3*(i+1);
double W2= W1;
segment(P1,P2, C1,C2, W1,W2, 0);
}
}
void sample3()
{ //radial spectrum
for ( double ag=0, i=0; ag < 2*vaser_pi-0.1; ag+=vaser_pi/12, i+=1)
{
double r1 = 30.0;
double r2 = 90.0;
double tx2=r2*cos(ag);
double ty2=r2*sin(ag);
double tx1=r1*cos(ag);
double ty1=r1*sin(ag);
double Ox = 120;
double Oy = 194+97;
Vec2 P1 = { Ox+tx1,Oy-ty1};
Vec2 P2 = { Ox+tx2,Oy-ty2};
Color C1 = { 1.0,0.0,0.5, 1.0};
Color C2 = { 0.5,0.0,1.0, 1.0};
double W1= 0.3*(i+1);
double W2= W1;
polyline_opt opt={0};
opt.cap = PLC_round;
opt.joint = PLJ_round;
segment(P1,P2, C1,C2, W1,W2, &opt);
}
}
void sample4()
{
int size_of_AP=4;
Vec2 AP[size_of_AP];
AP[0].x=200; AP[0].y=50;
AP[1].x=100; AP[1].y=150;
AP[2].x=300; AP[2].y=150;
AP[3].x=200; AP[3].y=250;
Color col={ 0.5, 0.8, 1.0, 1};
polybezier( AP, col, 12.0, size_of_AP, 0);
}
void sample5()
{
int size_of_AP=4;
Vec2 AP[size_of_AP];
AP[0].x=200; AP[0].y=50;
AP[1].x=100; AP[1].y=150;
AP[2].x=300; AP[2].y=150;
AP[3].x=200; AP[3].y=250;
Color AC[size_of_AP];
{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}
{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}
{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}
{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}
double AW[size_of_AP];
AW[0] = 1.0;
AW[1] = 15.0;
AW[2] = 15.0;
AW[3] = 1.0;
gradient grad = {0};
gradient_stop stop[10] = {0};
grad.stops = stop;
grad.length = 10;
stop[0].t = 0.00; stop[0].type = GS_rgba; stop[0].color = AC[0];
stop[1].t = 0.50; stop[1].type = GS_rgba; stop[1].color = AC[2];
stop[2].t = 1.00; stop[2].type = GS_rgba; stop[2].color = AC[3];
stop[3].t = 0.00; stop[3].type = GS_weight; stop[3].weight = AW[0];
stop[4].t = 0.33; stop[4].type = GS_weight; stop[4].weight = AW[1];
stop[5].t = 0.66; stop[5].type = GS_weight; stop[5].weight = AW[2];
stop[6].t = 1.00; stop[6].type = GS_weight; stop[6].weight = AW[3];
polybezier( AP, &grad, size_of_AP, 0);
}
void sample6()
{
}
void sample7()
{
}
void sample8()
{
}
void sample9()
{
}
void test_draw()
{
renderer::before();
switch (current)
{
case 0: sample0(); break;
case 1: sample1(); break;
case 2: sample2(); break;
case 3: sample3(); break;
case 4: sample4(); break;
case 5: sample5(); break;
case 6: sample6(); break;
case 7: sample7(); break;
case 8: sample8(); break;
case 9: sample9(); break;
}
renderer::after();
}
int main(int argc, char **argv)
{
main_wnd = new Fl_Window( 700,400,"VASEr - demo");
make_form(); //initialize
gl_wnd = new Gl_Window( 0,0,600,400); gl_wnd->end(); //create gl window
main_wnd->end();
main_wnd->show();
main_wnd->redraw();
return Fl::run();
}
================================================
FILE: cpp/samples/segment.cpp
================================================
/*config.h is generated by fltk in your system
* this file is used with fltk 1.3 with gl enabled.
* compile by: fltk-config --use-gl --compile segment.cpp
* or something like: g++ -lX11 -lGL 'segment.cpp' -o 'segment'
*/
#include
#include
#include "config.h" //config.h must always be placed before any Fl header
#include
#include
#include
#include
#include
namespace VASEr
{
struct Vec2 { double x,y;};
struct Color { float r,g,b,a;};
}
#include "../vaser/vaser.cpp"
using namespace VASEr;
void test_draw();
#include "test1_base.cpp"
Fl_Window* main_wnd;
Gl_Window* gl_wnd;
Fl_Slider *start_weight, *feathering;
Fl_Button *feather, *no_feather_at_cap, *no_feather_at_core;
Fl_Button *jc_butt, *jc_round, *jc_square, *jc_rect;
Fl_Button *colored, *alphaed, *weighted;
char get_cap_type()
{
if ( jc_butt->value())
return PLC_butt;
else if ( jc_round->value())
return PLC_round;
else if ( jc_square->value())
return PLC_square;
else if ( jc_rect->value())
return PLC_rect;
else
return 0;
}
void drag_cb(Fl_Widget* W, void*)
{
gl_wnd->redraw();
}
void make_form()
{
const int LD = 606;
//weight feathering
start_weight = new Fl_Value_Slider(LD,0,200,20,"spectrum start weight");
start_weight->type(FL_HOR_SLIDER);
start_weight->bounds(0.3,30.0);
start_weight->step(0.3*20.0);
start_weight->value(0.0);
start_weight->callback(drag_cb);
feathering = new Fl_Value_Slider(LD,40,200,20,"feathering");
feathering->type(FL_HOR_SLIDER);
feathering->bounds(1.0,10.0);
feathering->callback(drag_cb);
feathering->value(1.0);
//feather, no_feather_at_cap, no_feather_at_core
feather = new Fl_Light_Button(LD,80,100,15,"feather");
no_feather_at_cap = new Fl_Light_Button(LD+50,95,150,15,"no_feather_at_cap");
no_feather_at_core = new Fl_Light_Button(LD+50,110,150,15,"no_feather_at_core");
feather ->value(1);
no_feather_at_cap ->value(0);
no_feather_at_core->value(0);
feather ->callback(drag_cb);
no_feather_at_cap ->callback(drag_cb);
no_feather_at_core->callback(drag_cb);
//cap type
{
Fl_Group* o = new Fl_Group(LD,160,200,30);
new Fl_Box(LD,160,80,15,"cap type:");
jc_butt = new Fl_Radio_Light_Button(LD+20,175,40,15,"butt");
jc_round = new Fl_Radio_Light_Button(LD+60,175,50,15,"round");
jc_square = new Fl_Radio_Light_Button(LD+110,175,50,15,"square");
jc_rect = new Fl_Radio_Light_Button(LD+160,175,40,15,"rect");
o->end();
jc_butt ->value(1);
jc_butt ->callback(drag_cb);
jc_round ->callback(drag_cb);
jc_square ->callback(drag_cb);
jc_rect ->callback(drag_cb);
}
//test options
colored = new Fl_Light_Button(LD,250,60,15,"colored");
colored->callback(drag_cb);
colored->value(1);
alphaed = new Fl_Light_Button(LD+60,250,70,15,"alpha-ed");
alphaed->callback(drag_cb);
alphaed->value(1);
weighted = new Fl_Light_Button(LD+130,250,70,15,"weighted");
weighted->callback(drag_cb);
}
void test_draw()
{
renderer::before();
polyline_opt opt={0};
opt.feather = feather->value();
opt.feathering = feathering->value();
opt.no_feather_at_cap = no_feather_at_cap->value();
opt.no_feather_at_core = no_feather_at_core->value();
opt.cap = get_cap_type();
for ( int i=0; i<20; i++)
{
Vec2 P1 = { 5+29.7*i, 187};
Vec2 P2 = { 35+29.7*i, 8};
Color C1 = { 0,0,0, 1};
Color C2 = C1;
double W1= 0.3*(i+1) + start_weight->value();
double W2= W1;
if ( colored->value())
{
Color cc1 = { 1.0,0.0,0.5, 1.0};
C1 = cc1;
Color cc2 = { 0.5,0.0,1.0, 1.0};
C2 = cc2;
}
if ( alphaed->value())
{
C1.a = 0.5f;
C2.a = 0.5f;
}
if ( weighted->value())
{
W1 = 0.1;
}
if ( opt.cap != PLC_butt)
{
double end_weight = 0.3*(20) + start_weight->value();
P1.y -= end_weight*0.5;
P2.y += end_weight*0.5;
}
segment(P1, P2, //coordinates
C1, C2, //colors
W1, W2, //weights
&opt); //extra options
}
const double pi=3.14159265;
for ( double ag=0, i=0; ag<2*pi-0.1; ag+=pi/12, i+=1)
{
double r1 = 0.0;
double r2 = 90.0;
if ( !weighted->value())
r1 = 30.0;
if ( opt.cap != PLC_butt)
{
double end_weight = 0.3*(12) + start_weight->value();
r2 -= end_weight*0.5;
}
double tx2=r2*cos(ag);
double ty2=r2*sin(ag);
double tx1=r1*cos(ag);
double ty1=r1*sin(ag);
double Ox = 120;
double Oy = 194+97;
Vec2 P1 = { Ox+tx1,Oy-ty1};
Vec2 P2 = { Ox+tx2,Oy-ty2};
Color C1 = { 0,0,0, 1};
Color C2 = C1;
double W1= 0.3*(i+1) + start_weight->value();
double W2= W1;
if ( colored->value())
{
Color cc1 = { 1.0,0.0,0.5, 1.0};
C1 = cc1;
Color cc2 = { 0.5,0.0,1.0, 1.0};
C2 = cc2;
}
if ( alphaed->value())
{
C1.a = 0.5f;
C2.a = 0.5f;
}
if ( weighted->value())
{
W1 = 0.1;
}
segment(P1, P2, //coordinates
C1, C2, //colors
W1, W2, //weights
&opt); //extra options
}
renderer::after();
}
int main(int argc, char **argv)
{
main_wnd = new Fl_Window( 606+200,194*2,"Vase Renderer - segment() example - fltk/opengl");
make_form(); //initialize
gl_wnd = new Gl_Window( 0,0,606,194*2); gl_wnd->end(); //create gl window
main_wnd->end();
main_wnd->show();
main_wnd->redraw();
return Fl::run();
}
================================================
FILE: cpp/samples/test1_base.cpp
================================================
#include "config.h"
#include
#include
#include
#include
class Gl_Window : public Fl_Gl_Window
{
//methods
void init();
void draw();
int handle(int);
void left_mouse_button_down();
void left_mouse_button_up();
void leftclick_drag();
//variables
short curx,cury, lastx,lasty;
short cur_drag; //index of point which is being draged currently
// -1 means none
int tsize;
Vec2* target;
public:
Gl_Window(int x,int y,int w,int h,const char *l=0)
: Fl_Gl_Window(x,y,w,h,l) { init();}
Gl_Window(int w,int h,const char *l=0)
: Fl_Gl_Window(w,h,l) { init();}
void set_drag_target( Vec2* target, int size_of_target)
{ this->target=target; this->tsize=size_of_target;}
};
void Gl_Window::init()
{
curx=0; cury=0; lastx=0; lasty=0;
cur_drag=-1;
tsize=0;
target=0;
}
void Gl_Window::draw()
{
if (!valid())
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho( 0,w(),h(),0,0.0f,100.0f);
glClearColor( 1.0, 1.0, 1.0, 1.0);
glClearDepth( 0.0f);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
test_draw();
}
void Gl_Window::left_mouse_button_down()
{
for ( int i=0; i=0 && cur_drag< tsize)
{
target[cur_drag].x=curx;
target[cur_drag].y=cury;
this->redraw();
}
};
int Gl_Window::handle(int event)
{
switch(event)
{
case FL_PUSH:
case FL_RELEASE:
case FL_DRAG:
case FL_MOVE:
curx = Fl::event_x();
cury = Fl::event_y();
switch(event)
{
case FL_PUSH:
if ( Fl::event_button() == FL_LEFT_MOUSE)
left_mouse_button_down();
case FL_RELEASE:
if ( Fl::event_button() == FL_LEFT_MOUSE)
left_mouse_button_up();
case FL_DRAG:
if ( (Fl::event_state()&FL_BUTTON1)==FL_BUTTON1)
leftclick_drag();
case FL_MOVE:
;
}
lastx=curx;
lasty=cury;
return 1;
default:
return Fl_Window::handle(event);
} //end of switch(event)
};
================================================
FILE: cpp/vaser/agg_curve4.cpp
================================================
//-----------------------------------------------------------------------
// The Anti-Grain Geometry Project
// A high quality rendering engine for C++
// http://antigrain.com
//
// Anti-Grain Geometry has dual licensing model. The Modified BSD
// License was first added in version v2.4 just for convenience.
// It is a simple, permissive non-copyleft free software license,
// compatible with the GNU GPL. It's well proven and recognizable.
// See http://www.fsf.org/licensing/licenses/index_html#ModifiedBSD
// for details.
//
// Note that the Modified BSD license DOES NOT restrict your rights
// if you choose the Anti-Grain Geometry Public License.
//
// Anti-Grain Geometry Public License
// ====================================================
//
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (McSeem)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//
//
// Modified BSD License
// ====================================================
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (McSeem)
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// 3. The name of the author may not be used to endorse or promote
// products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//-----------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//-----------------------------------------------------------------------
#include
#ifndef M_PI
#define M_PI 3.141592653589793238462643
#endif
double calc_sq_distance(double x1, double y1, double x2, double y2)
{
double dx = x2-x1;
double dy = y2-y1;
return dx * dx + dy * dy;
}
void recursive_bezier( double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4,
unsigned level,
double m_angle_tolerance,
double m_cusp_limit,
double m_distance_tolerance_square,
void (*add_point)(void*,double,double),
void* obj )
{
const double curve_distance_epsilon = 1e-30;
const double curve_collinearity_epsilon = 1e-30;
const double curve_angle_tolerance_epsilon = 0.01;
const int curve_recursion_limit = 32;
if(level > curve_recursion_limit)
{
return;
}
// Calculate all the mid-points of the line segments
//----------------------
double x12 = (x1 + x2) / 2;
double y12 = (y1 + y2) / 2;
double x23 = (x2 + x3) / 2;
double y23 = (y2 + y3) / 2;
double x34 = (x3 + x4) / 2;
double y34 = (y3 + y4) / 2;
double x123 = (x12 + x23) / 2;
double y123 = (y12 + y23) / 2;
double x234 = (x23 + x34) / 2;
double y234 = (y23 + y34) / 2;
double x1234 = (x123 + x234) / 2;
double y1234 = (y123 + y234) / 2;
// Try to approximate the full cubic curve by a single straight line
//------------------
double dx = x4-x1;
double dy = y4-y1;
double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));
double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));
double da1, da2, k;
switch((int(d2 > curve_collinearity_epsilon) << 1) +
int(d3 > curve_collinearity_epsilon))
{
case 0:
// All collinear OR p1==p4
//----------------------
k = dx*dx + dy*dy;
if(k == 0)
{
d2 = calc_sq_distance(x1, y1, x2, y2);
d3 = calc_sq_distance(x4, y4, x3, y3);
}
else
{
k = 1 / k;
da1 = x2 - x1;
da2 = y2 - y1;
d2 = k * (da1*dx + da2*dy);
da1 = x3 - x1;
da2 = y3 - y1;
d3 = k * (da1*dx + da2*dy);
if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1)
{
// Simple collinear case, 1---2---3---4
// We can leave just two endpoints
return;
}
if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1);
else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4);
else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);
if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1);
else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4);
else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);
}
if(d2 > d3)
{
if(d2 < m_distance_tolerance_square)
{
add_point(obj, x2,y2);
return;
}
}
else
{
if(d3 < m_distance_tolerance_square)
{
add_point(obj, x3,y3);
return;
}
}
break;
case 1:
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
add_point(obj, x23,y23);
return;
}
// Angle Condition
//----------------------
da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));
if(da1 >= M_PI) da1 = 2*M_PI - da1;
if(da1 < m_angle_tolerance)
{
add_point(obj, x2, y2);
add_point(obj, x3, y3);
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
add_point(obj, x3, y3);
return;
}
}
}
break;
case 2:
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
add_point(obj, x23, y23);
return;
}
// Angle Condition
//----------------------
da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
if(da1 >= M_PI) da1 = 2*M_PI - da1;
if(da1 < m_angle_tolerance)
{
add_point(obj, x2, y2);
add_point(obj, x3, y3);
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
add_point(obj, x2, y2);
return;
}
}
}
break;
case 3:
// Regular case
//-----------------
if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
add_point(obj, x23, y23);
return;
}
// Angle & Cusp Condition
//----------------------
k = atan2(y3 - y2, x3 - x2);
da1 = fabs(k - atan2(y2 - y1, x2 - x1));
da2 = fabs(atan2(y4 - y3, x4 - x3) - k);
if(da1 >= M_PI) da1 = 2*M_PI - da1;
if(da2 >= M_PI) da2 = 2*M_PI - da2;
if(da1 + da2 < m_angle_tolerance)
{
// Finally we can stop the recursion
//----------------------
add_point(obj, x23, y23);
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
add_point(obj, x2, y2);
return;
}
if(da2 > m_cusp_limit)
{
add_point(obj, x3, y3);
return;
}
}
}
break;
}
// Continue subdivision
//----------------------
recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1,
m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,
add_point, obj);
recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1,
m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,
add_point, obj);
}
int curve4_div(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4,
double m_approximation_scale,
double m_angle_tolerance,
double m_cusp_limit,
void (*add_point)(void*,double,double),
void* obj )
{
double m_distance_tolerance_square = 0.5 / m_approximation_scale;
m_distance_tolerance_square *= m_distance_tolerance_square;
int m_count = 0;
add_point(obj, x1, y1);
recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0,
m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,
add_point, obj);
add_point(obj, x4, y4);
}
#undef M_PI
================================================
FILE: cpp/vaser/backend.h
================================================
class backend
{
public:
static void vah_draw(vertex_array_holder& vah);
static void polyline( const Vec2*, Color, double W, int length, const polyline_opt*); //constant color and weight
};
================================================
FILE: cpp/vaser/color.h
================================================
float& Color_get( Color& C, int index)
{
switch (index)
{
case 0: return C.r;
case 1: return C.g;
case 2: return C.b;
default:return C.r;
}
}
bool Color_valid_range(float t)
{
return t>=0.0f && t<=1.0f;
}
Color Color_between( const Color& A, const Color& B, float t=0.5f)
{
if ( t<0.0f) t = 0.0f;
if ( t>1.0f) t = 1.0f;
float kt = 1.0f - t;
Color C =
{
A.r *kt + B.r *t,
A.g *kt + B.g *t,
A.b *kt + B.b *t,
A.a *kt + B.a *t
};
return C;
}
void sRGBtolinear( Color& C, bool exact=false)
{ //de-Gamma 2.2
//from: http://www.xsi-blog.com/archives/133
if (exact)
{
for ( int i=0; i<3; i++)
{
float& cc = Color_get( C,i);
if ( cc > 0.04045)
cc = pow( (cc+0.055)/1.055, 2.4);
else
cc /= 12.92;
}
}
else
{ //approximate
for ( int i=0; i<3; i++)
{
float& cc = Color_get( C,i);
cc = pow(cc,2.2);
}
}
}
void lineartosRGB( Color& C, bool exact=false)
{ //Gamma 2.2
if (exact)
{
for ( int i=0; i<3; i++)
{
float& cc = Color_get( C,i);
if ( cc > 0.0031308)
cc = 1.055 * pow(cc,1.0/2.4) - 0.055;
else
cc *= 12.92;
}
}
else
{ //approximate
for ( int i=0; i<3; i++)
{
float& cc = Color_get( C,i);
cc = pow(cc,1.0/2.2);
}
}
}
float color_max( float r,float g,float b)
{
return r>g? (g>b?r:(r>b?r:b)) : (g>b?g:b);
}
float color_min( float r, float g, float b)
{
return -color_max( -r,-g,-b);
}
void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )
{ //from: http://www.cs.rit.edu/~ncs/color/t_convert.html
// r,g,b values are from 0 to 1
// h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
float min, max, delta;
min = color_min( r, g, b );
max = color_max( r, g, b );
*v = max; // v
delta = max - min;
if( max != 0 )
*s = delta / max; // s
else {
// r = g = b = 0 // s = 0, v is undefined
*s = 0;
*h = -1;
return;
}
if( r == max )
*h = ( g - b ) / delta; // between yellow & magenta
else if( g == max )
*h = 2 + ( b - r ) / delta; // between cyan & yellow
else
*h = 4 + ( r - g ) / delta; // between magenta & cyan
*h *= 60; // degrees
if( *h < 0 )
*h += 360;
}
void HSVtoRGB( float *r, float *g, float *b, float h, float s, float v )
{
int i;
float f, p, q, t;
if( s == 0 ) {
// achromatic (grey)
*r = *g = *b = v;
return;
}
h /= 60; // sector 0 to 5
i = floor( h );
f = h - i; // factorial part of h
p = v * ( 1 - s );
q = v * ( 1 - s * f );
t = v * ( 1 - s * ( 1 - f ) );
switch( i ){
case 0:
*r = v; *g = t; *b = p; break;
case 1:
*r = q; *g = v; *b = p; break;
case 2:
*r = p; *g = v; *b = t; break;
case 3:
*r = p; *g = q; *b = v; break;
case 4:
*r = t; *g = p; *b = v; break;
default: // case 5:
*r = v; *g = p; *b = q; break;
}
}
================================================
FILE: cpp/vaser/curve.cpp
================================================
namespace VASErin
{ //VASEr internal namespace
class polyline_buffer
{
public:
std::vector P;
std::vector C;
std::vector W;
std::vector L; //length along polyline
int N;
double path_length; //total segment length
polyline_buffer(): P(),C(),W(),L()
{
N=0;
path_length=0.0;
L.push_back(0.0);
}
void point(double x, double y)
{
Vec2 V={x,y};
addvertex(V);
}
void point(Vec2 V)
{
addvertex(V);
}
static void point_cb(void* obj, double x, double y)
{
polyline_buffer* This = (polyline_buffer*)obj;
Vec2 V={x,y};
This->addvertex(V);
}
void vertex(Vec2 V, Color cc)
{
addvertex(V,&cc);
}
void vertex(Vec2 V, Color cc, double ww)
{
addvertex(V,& cc,ww);
}
void set_color(Color cc)
{
if( !C.size())
C.push_back(cc);
else
C.back()=cc;
}
void set_weight(double ww)
{
if( !W.size())
W.push_back(ww);
else
W.back()=ww;
}
void gradient( const gradient* grad)
{
gradient_apply(grad,&C[0],&W[0],&L[0],N,path_length);
}
void draw(const polyline_opt* options)
{
if( !N) return;
polyline_inopt in_options={0};
in_options.segment_length = &L[0];
polyline( &P[0],&C[0],&W[0],N,options,&in_options);
}
private:
bool addvertex(const Vec2& V, const Color* cc=0, double ww=0.0)
{
if( N && P[N-1].x==V.x && P[N-1].y==V.y)
return false; //duplicate
else
{
//point
P.push_back(V);
if( N>0)
{
double len = (Point(V)-Point(P[N-1])).length();
path_length += len;
L.push_back(len);
}
//color
if( cc)
C.push_back(*cc);
else
{
if( !C.size())
C.push_back(default_color);
else
C.push_back(C.back());
}
//weight
if( ww)
W.push_back(ww);
else
{
if( !W.size())
W.push_back(default_weight);
else
W.push_back(W.back());
}
//finally
N++;
return true;
}
}
};
struct polybezier_inopt
{
bool evaluate_only;
polyline_buffer* target;
};
void polybezier( const Vec2* P, const gradient* grad, int length, const polybezier_opt* options, const polybezier_inopt* in_options)
{
polybezier_opt opt={0};
polybezier_inopt inopt={0};
polyline_opt poly={0};
poly.joint = PLJ_bevel;
if( options) opt = *options;
if( in_options) inopt = *in_options;
if( !opt.poly) opt.poly = &poly;
polyline_buffer _buf;
polyline_buffer& buf = _buf;
if( inopt.target)
buf = *inopt.target;
const double BZ_default_approximation_scale = 0.5;
const double BZ_default_angle_tolerance = 15.0/180*vaser_pi;
const double BZ_default_cusp_limit = 5.0;
for( int i=0; istops || !gradp->length) return;
const gradient& grad = *gradp;
const gradient_stop* stop = grad.stops;
//current stops
int las_c=0, las_a=0, las_w=0, //last
cur_c=0, cur_a=0, cur_w=0, //current
nex_c=0, nex_a=0, nex_w=0; //next
double length_along=0.0;
if( grad.length>1)
for( int i=0; i= 0; nex_w--)
if( stop[nex_w].type==GS_weight && p >= stop[nex_w].t)
{ las_w=nex_w; break;}
for( nex_c=cur_c; nex_c >= 0; nex_c--)
if( (stop[nex_c].type==GS_rgba || stop[nex_c].type==GS_rgb ) &&
p >= stop[nex_c].t)
{ las_c=nex_c; break;}
for( nex_a=cur_a; nex_a >= 0; nex_a--)
if( (stop[nex_a].type==GS_rgba || stop[nex_a].type==GS_alpha ) &&
p >= stop[nex_a].t)
{ las_a=nex_a; break;}
}
#define SC(x) stop[x].color
#define Sw(x) stop[x].weight
#define Sa(x) stop[x].color.a
#define St(x) stop[x].t
if ( cur_c==las_c)
C[i] = SC(cur_c);
else
C[i] = Color_between(SC(las_c),SC(cur_c), (p-St(las_c))/(St(cur_c)-St(las_c)));
if ( cur_w==las_w)
W[i] = Sw(cur_w);
else
W[i] = grad_getstep(Sw(las_w),Sw(cur_w), p-St(las_w), St(cur_w)-St(las_w));
if ( cur_a==las_a)
C[i].a = Sa(cur_a);
else
C[i].a = grad_getstep(Sa(las_a),Sa(cur_a), p-St(las_a), St(cur_a)-St(las_a));
#undef SC
#undef Sw
#undef Sa
#undef St
}
}
} //sub namespace VASErin
================================================
FILE: cpp/vaser/opengl.cpp
================================================
/* OpenGL 1.1 renderer and backend */
namespace VASErin
{ //VASEr internal namespace
void backend::vah_draw(vertex_array_holder& vah)
{
if ( vah.count > 0) //save some effort
{
glVertexPointer(2, GL_FLOAT, 0, &vah.vert[0]);
glColorPointer (4, GL_FLOAT, 0, &vah.color[0]);
glDrawArrays (vah.glmode, 0, vah.count);
}
}
void backend::polyline( const Vec2* P, Color C, double W, int length, const polyline_opt*) //constant color and weight
{
int type=0;
if( sizeof(Vec2)==16)
type = GL_DOUBLE;
else if( sizeof (Vec2)==8)
type = GL_FLOAT;
glColor4f(C.r,C.g,C.b,C.a);
glLineWidth(W);
if( type)
{
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, type, 0, P);
glDrawArrays(GL_LINE_STRIP, 0, length);
glEnableClientState(GL_COLOR_ARRAY);
}
else
{
glBegin(GL_LINE_STRIP);
for( int i=0; i vaser_min_alw)
{
x /= L; y /= L;
}
return L;
}
void perpen() //perpendicular: anti-clockwise 90 degrees
{
double y_value=y;
y=x;
x=-y_value;
}
void follow_signs( const Point& a)
{
if ( (x>0) != (a.x>0)) x = -x;
if ( (y>0) != (a.y>0)) y = -y;
}
void follow_magnitude( const Point& a);
void follow_direction( const Point& a);
//judgements
static inline bool negligible( double M)
{
return -vaser_min_alw < M && M < vaser_min_alw;
}
bool negligible() const
{
return Point::negligible(x) && Point::negligible(y);
}
bool non_negligible() const
{
return !negligible();
}
bool is_zero() const
{
return x==0.0 && y==0.0;
}
bool non_zero() const
{
return !is_zero();
}
static bool intersecting( const Point& A, const Point& B,
const Point& C, const Point& D)
{ //return true if AB intersects CD
return signed_area(A,B,C)>0 != signed_area(A,B,D)>0;
}
//operations require 2 input points
static double distance_squared( const Point& A, const Point& B)
{
double dx=A.x-B.x;
double dy=A.y-B.y;
return (dx*dx+dy*dy);
}
static inline double distance( const Point& A, const Point& B)
{
return sqrt( distance_squared(A,B));
}
static Point midpoint( const Point& A, const Point& B)
{
return (A+B)*0.5;
}
static bool opposite_quadrant( const Point& P1, const Point& P2)
{
char P1x = P1.x>0? 1:(P1.x<0?-1:0);
char P1y = P1.y>0? 1:(P1.y<0?-1:0);
char P2x = P2.x>0? 1:(P2.x<0?-1:0);
char P2y = P2.y>0? 1:(P2.y<0?-1:0);
if ( P1x!=P2x) {
if ( P1y!=P2y)
return true;
if ( P1y==0 || P2y==0)
return true;
}
if ( P1y!=P2y) {
if ( P1x==0 || P2x==0)
return true;
}
return false;
}
//operations of 3 points
static inline bool anchor_outward_D( Point& V, const Point& b, const Point& c)
{
return (b.x*V.x - c.x*V.x + b.y*V.y - c.y*V.y) > 0;
}
static bool anchor_outward( Point& V, const Point& b, const Point& c, bool reverse=false)
{ //put the correct outward vector at V, with V placed on b, comparing distances from c
bool determinant = anchor_outward_D ( V,b,c);
if ( determinant == (!reverse)) { //when reverse==true, it means inward
//positive V is the outward vector
return false;
} else {
//negative V is the outward vector
V.x=-V.x;
V.y=-V.y;
return true; //return whether V is changed
}
}
static void anchor_inward( Point& V, const Point& b, const Point& c)
{
anchor_outward( V,b,c,true);
}
//operations of 4 points
static char intersect( const Point& P1, const Point& P2, //line 1
const Point& P3, const Point& P4, //line 2
Point& Pout, //the output point
double* ua_out=0, double* ub_out=0)
{ //Determine the intersection point of two line segments
double mua,mub;
double denom,numera,numerb;
denom = (P4.y-P3.y) * (P2.x-P1.x) - (P4.x-P3.x) * (P2.y-P1.y);
numera = (P4.x-P3.x) * (P1.y-P3.y) - (P4.y-P3.y) * (P1.x-P3.x);
numerb = (P2.x-P1.x) * (P1.y-P3.y) - (P2.y-P1.y) * (P1.x-P3.x);
if( negligible(numera) &&
negligible(numerb) &&
negligible(denom)) {
Pout.x = (P1.x + P2.x) * 0.5;
Pout.y = (P1.y + P2.y) * 0.5;
return 2; //meaning the lines coincide
}
if ( negligible(denom)) {
Pout.x = 0;
Pout.y = 0;
return 0; //meaning lines are parallel
}
mua = numera / denom;
mub = numerb / denom;
if ( ua_out) *ua_out = mua;
if ( ub_out) *ub_out = mub;
Pout.x = P1.x + mua * (P2.x - P1.x);
Pout.y = P1.y + mua * (P2.y - P1.y);
bool out1 = mua < 0 || mua > 1;
bool out2 = mub < 0 || mub > 1;
if ( out1 & out2) {
return 5; //the intersection lies outside both segments
} else if ( out1) {
return 3; //the intersection lies outside segment 1
} else if ( out2) {
return 4; //the intersection lies outside segment 2
} else {
return 1; //great
}
//http://paulbourke.net/geometry/lineline2d/
}
}; //end of class Point
/* after all,
* sizeof(Vec2)=16 sizeof(Point)=16
* Point is not heavier, just more powerful :)
*/
================================================
FILE: cpp/vaser/polyline.cpp
================================================
namespace VASErin
{ //VASEr internal namespace
/*visual testes:
* A. points (geometry test)
* 1. arbitrary polyline of only 2 point
* 2. polylines of 3 points, making arbitrary included angle
* 3. arbitrary polyline of 4 or more points
* B. colors
* 1. different colors on each point
* C. weight
* 1. varying weight
* D. other drawing options
* 1. feathering
* 2. different joint types
* 3. different cap types
* E. memory test
* 1. drawing a polyline of 1000 points
*/
/*known visual bugs: ( ",," simply means "go wild" as it is too hard to describe)
* 1. [solved]when 2 segments are exactly at 90 degrees, the succeeding line segment is reflexed.
* 1.1 [solved]when 2 segments are exactly at 180 degrees,,
* 2. [solved]when polyline is dragged, caps seem to have pseudo-podia.
* 3. [solved]when 2 segments are exactly horizontal/ vertical, both segments are reflexed.
* 3.1 [solved]when a segment is exactly horizontal/ vertical, the cap disappears
* 4. [solved]when 2 segments make < 5 degrees,,
* 4.1 [solved]when 2 segments make exactly 0 degree,,
* 5. when a segment is shorter than its own width,,
*/
/* const double vaser_actual_PPI = 96.0;
const double vaser_standard_PPI = 111.94; //the PPI I used for calibration
*/
//critical weight to do approximation rather than real joint processing
const double cri_segment_approx=1.6;
void determine_t_r ( double w, double& t, double& R)
{
//efficiency: can cache one set of w,t,R values
// i.e. when a polyline is of uniform thickness, the same w is passed in repeatedly
double f=w-static_cast(w);
/* */if ( w>=0.0 && w<1.0) {
t=0.05; R=0.768;//R=0.48+0.32*f;
} else if ( w>=1.0 && w<2.0) {
t=0.05+f*0.33; R=0.768+0.312*f;
} else if ( w>=2.0 && w<3.0){
t=0.38+f*0.58; R=1.08;
} else if ( w>=3.0 && w<4.0){
t=0.96+f*0.48; R=1.08;
} else if ( w>=4.0 && w<5.0){
t=1.44+f*0.46; R=1.08;
} else if ( w>=5.0 && w<6.0){
t=1.9+f*0.6; R=1.08;
} else if ( w>=6.0){
double ff=w-6.0;
t=2.5+ff*0.50; R=1.08;
}
/* //PPI correction
double PPI_correction = vaser_standard_PPI / vaser_actual_PPI;
const double up_bound = 1.6; //max value of w to receive correction
const double start_falloff = 1.0;
if ( w>0.0 && w0.0 && w<=1.0) {
t=0.5; r=0.0;
P2.x = P1.x = (int)P1.x+0.5;
}
} else if ( Point::negligible(DP.y) && P1.y==(int)P1.y) {
if ( w>0.0 && w<=1.0) {
t=0.5; r=0.0;
P2.y = P1.y = (int)P1.y+0.5;
}
}
}
//output t,r
if (tt) *tt = t;
if (rr) *rr = r;
//calculate T,R,C
double len = DP.normalize();
if (dist) *dist = (float)len;
if (C) *C = DP;
DP.perpen();
if (T) *T = DP*t;
if (R) *R = DP*r;
}
void same_side_of_line( Point& V, const Point& ref, const Point& a, const Point& b)
{
double sign1 = Point::signed_area( a+ref,a,b);
double sign2 = Point::signed_area( a+V, a,b);
if ( (sign1>=0) != (sign2>=0))
{
V.opposite();
}
}
struct st_polyline
//the struct to hold info for anchor_late() to perform triangluation
{
//for all joints
Point vP; //vector to intersection point
Point vR; //fading vector at sharp end
//all vP,vR are outward
//for djoint==PLJ_bevel
Point T; //core thickness of a line
Point R; //fading edge of a line
Point bR; //out stepping vector, same direction as cap
Point T1,R1; //alternate vectors, same direction as T21
//all T,R,T1,R1 are outward
//for djoint==PLJ_round
float t,r;
//for degeneration case
bool degenT; //core degenerated
bool degenR; //fade degenerated
bool pre_full; //draw the preceding segment in full
Point PT,PR;
float pt; //parameter at intersection
bool R_full_degen;
char djoint; //determined joint
// e.g. originally a joint is PLJ_miter. but it is smaller than critical angle, should then set djoint to PLJ_bevel
};
struct st_anchor
//the struct to hold memory for the working of anchor()
{
Point P[3]; //point
Color C[3]; //color
double W[3];//weight
Point cap_start, cap_end;
st_polyline SL[3];
vertex_array_holder vah;
};
void inner_arc( vertex_array_holder& hold, const Point& P,
const Color& C, const Color& C2,
float dangle, float angle1, float angle2,
float r, float r2, bool ignor_ends,
Point* apparent_P) //(apparent center) center of fan
//draw the inner arc between angle1 and angle2 with dangle at each step.
// -the arc has thickness, r is the outer radius and r2 is the inner radius,
// with color C and C2 respectively.
// in case when inner radius r2=0.0f, it gives a pie.
// -when ignor_ends=false, the two edges of the arc lie exactly on angle1
// and angle2. when ignor_ends=true, the two edges of the arc do not touch
// angle1 or angle2.
// -P is the mathematical center of the arc.
// -when apparent_P points to a valid Point (apparent_P != 0), r2 is ignored,
// apparent_P is then the apparent origin of the pie.
// -the result is pushed to hold, in form of a triangle strip
// -an inner arc is an arc which is always shorter than or equal to a half circumference
{
const double& m_pi = vaser_pi;
bool incremental=true;
if ( angle2 > angle1)
{
if ( angle2-angle1>m_pi)
{
angle2=angle2-2*m_pi;
}
}
else
{
if ( angle1-angle2>m_pi)
{
angle1=angle1-2*m_pi;
}
}
if ( angle1>angle2)
{
incremental = false; //means decremental
}
if ( incremental)
{
if ( ignor_ends)
{
int i=0;
for ( float a=angle1+dangle; a100) {
DEBUG("trapped in loop: inc,ig_end angle1=%.2f, angle2=%.2f, dangle=%.2f\n", angle1, angle2, dangle);
break;
}
}
//DEBUG( "steps=%d ",i); fflush(stdout);
}
else
{
int i=0;
for ( float a=angle1; ; a+=dangle, i++)
{
if ( a>angle2)
a=angle2;
float x=cos(a);
float y=sin(a);
INNER_ARC_PUSH
if ( a>=angle2)
break;
if ( i>100) {
DEBUG("trapped in loop: inc,end angle1=%.2f, angle2=%.2f, dangle=%.2f\n", angle1, angle2, dangle);
break;
}
}
}
}
else //decremental
{
if ( ignor_ends)
{
int i=0;
for ( float a=angle1-dangle; a>angle2; a-=dangle, i++)
{
float x=cos(a);
float y=sin(a);
INNER_ARC_PUSH
if ( i>100) {
DEBUG("trapped in loop: dec,ig_end angle1=%.2f, angle2=%.2f, dangle=%.2f\n", angle1, angle2, dangle);
break;
}
}
}
else
{
int i=0;
for ( float a=angle1; ; a-=dangle, i++)
{
if ( a100) {
DEBUG("trapped in loop: dec,end angle1=%.2f, angle2=%.2f, dangle=%.2f\n", angle1, angle2, dangle);
break;
}
}
}
}
}
void vectors_to_arc( vertex_array_holder& hold, const Point& P,
const Color& C, const Color& C2,
Point A, Point B, float dangle, float r, float r2, bool ignor_ends,
Point* apparent_P)
//triangulate an inner arc between vectors A and B,
// A and B are position vectors relative to P
{
const double& m_pi = vaser_pi;
A *= 1/r;
B *= 1/r;
if ( A.x > 1.0-vaser_min_alw) A.x = 1.0-vaser_min_alw;
if ( A.x <-1.0+vaser_min_alw) A.x =-1.0+vaser_min_alw;
if ( B.x > 1.0-vaser_min_alw) B.x = 1.0-vaser_min_alw;
if ( B.x <-1.0+vaser_min_alw) B.x =-1.0+vaser_min_alw;
float angle1 = acos(A.x);
float angle2 = acos(B.x);
if ( A.y>0){ angle1=2*m_pi-angle1;}
if ( B.y>0){ angle2=2*m_pi-angle2;}
//printf( "steps=%d ",int((angle2-angle1)/den*r));
inner_arc( hold, P, C,C2, dangle,angle1,angle2, r,r2, ignor_ends, apparent_P);
}
#ifdef VASER_DEBUG
void annotate( const Point& P, Color cc, int I=-1)
{
static int i=0;
if ( I != -1) i=I;
glBegin(GL_LINES);
glColor3f(1,0,0);
glVertex2f(P.x-4,P.y-4);
glVertex2f(P.x+4,P.y+4);
glVertex2f(P.x-4,P.y+4);
glVertex2f(P.x+4,P.y-4);
glEnd();
char str[10];
sprintf(str,"%d",i);
gl_font( FL_HELVETICA, 8);
gl_draw( str,float(P.x+2),float(P.y));
i++;
}
void annotate( const Point& P)
{
Color cc;
annotate(P,cc);
}
void draw_vector( const Point& P, const Point& V, const char* name)
{
Point P2 = P+V;
glBegin(GL_LINES);
glColor3f(1,0,0);
glVertex2f(P.x,P.y);
glColor3f(1,0.9,0.9);
glVertex2f(P2.x,P2.y);
glEnd();
if ( name)
{
glColor3f(0,0,0);
gl_font( FL_HELVETICA, 8);
gl_draw( name,float(P2.x+2),float(P2.y));
}
}
void printpoint( const Point& P, const char* name)
{
printf("%s(%.4f,%.4f) ",name,P.x,P.y);
fflush(stdout);
}
#endif
/*
Point plus_minus( const Point& a, const Point& b, bool plus)
{
if (plus) return a+b;
else return a-b;
}
Point plus_minus( const Point& a, bool plus)
{
if (plus) return a;
else return -a;
}
bool quad_is_reflexed( const Point& P0, const Point& P1, const Point& P2, const Point& P3)
{
//points:
// 1------3
// / /
// 0------2
// vector 01 parallel to 23
return Point::distance_squared(P1,P3) + Point::distance_squared(P0,P2)
> Point::distance_squared(P0,P3) + Point::distance_squared(P1,P2);
}
void push_quad_safe( vertex_array_holder& core,
const Point& P2, const Color& cc2, bool transparent2,
const Point& P3, const Color& cc3, bool transparent3)
{
//push 2 points to form a quad safely(without reflex)
Point P0 = core.get_relative_end(-2);
Point P1 = core.get_relative_end(-1);
if ( !quad_is_reflexed(P0,P1,P2,P3))
{
core.push(P2,cc2,transparent2);
core.push(P3,cc3,transparent3);
}
else
{
core.push(P3,cc3,transparent3);
core.push(P2,cc2,transparent2);
}
}*/
#define push_quad(A0,A1,A2,A3,A4,A5,A6,A7,A8) push_quad_(__LINE__,A0,A1,A2,A3,A4,A5,A6,A7,A8)
#define push_quadf(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12) push_quadf_(__LINE__,A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12)
void push_quad_( short line, vertex_array_holder& core,
const Point& P0, const Point& P1, const Point& P2, const Point& P3,
const Color& c0, const Color& c1, const Color& c2, const Color& c3)
{
if( P0.is_zero()) DEBUG("pushed P0 (0,0) at %d\n",line);
if( P1.is_zero()) DEBUG("pushed P1 (0,0) at %d\n",line);
if( P2.is_zero()) DEBUG("pushed P2 (0,0) at %d\n",line);
if( P3.is_zero()) DEBUG("pushed P3 (0,0) at %d\n",line);
//interpret P0 to P3 as triangle strip
core.push3( P0, P1, P2,
c0, c1, c2);
core.push3( P1, P2, P3,
c1, c2, c3);
}
void push_quadf_( short line, vertex_array_holder& core,
const Point& P0, const Point& P1, const Point& P2, const Point& P3,
const Color& c0, const Color& c1, const Color& c2, const Color& c3,
bool trans0, bool trans1, bool trans2, bool trans3)
{
if( P0.is_zero()) DEBUG("pushed P0 (0,0) at %d\n",line);
if( P1.is_zero()) DEBUG("pushed P1 (0,0) at %d\n",line);
if( P2.is_zero()) DEBUG("pushed P2 (0,0) at %d\n",line);
if( P3.is_zero()) DEBUG("pushed P3 (0,0) at %d\n",line);
//interpret P0 to P3 as triangle strip
core.push3( P0, P1, P2,
c0, c1, c2,
trans0, trans1, trans2);
core.push3( P1, P2, P3,
c1, c2, c3,
trans1, trans2, trans3);
}
struct st_knife_cut
{
Point T1[4]; //retained polygon, also serves as input triangle
Color C1[4]; //
Point T2[4]; //cut away polygon
Color C2[4]; //
int T1c, T2c; //count of T1 & T2
//must be 0,3 or 4
};
int triangle_knife_cut( const Point& kn1, const Point& kn2, const Point& kn_out, //knife
const Color* kC0, const Color* kC1, //color of knife
st_knife_cut& ST)//will modify for output
//see knife_cut_test for more info
{ //return number of points cut away
int points_cut_away = 0;
bool kn_colored = kC0 && kC1; //if true, use the colors of knife instead
bool std_sign = Point::signed_area( kn1,kn2,kn_out) > 0;
bool s1 = Point::signed_area( kn1,kn2,ST.T1[0])>0 == std_sign; //true means this point should be cut
bool s2 = Point::signed_area( kn1,kn2,ST.T1[1])>0 == std_sign;
bool s3 = Point::signed_area( kn1,kn2,ST.T1[2])>0 == std_sign;
int sums = int(s1)+int(s2)+int(s3);
if ( sums == 0)
{ //all 3 points are retained
ST.T1c = 3;
ST.T2c = 0;
points_cut_away = 0;
}
else if ( sums == 3)
{ //all 3 are cut away
ST.T1c = 0;
ST.T2c = 3;
ST.T2[0] = ST.T1[0];
ST.T2[1] = ST.T1[1];
ST.T2[2] = ST.T1[2];
ST.C2[0] = ST.C1[0];
ST.C2[1] = ST.C1[1];
ST.C2[2] = ST.C1[2];
points_cut_away = 3;
}
else
{
if ( sums == 2) {
s1 = !s1;
s2 = !s2;
s3 = !s3;
}
//
Point ip1,ip2, outp;
Color iC1,iC2, outC;
if ( s1) { //here assume one point is cut away
// thus only one of s1,s2,s3 is true
outp= ST.T1[0]; outC= ST.C1[0];
ip1 = ST.T1[1]; iC1 = ST.C1[1];
ip2 = ST.T1[2]; iC2 = ST.C1[2];
} else if ( s2) {
outp= ST.T1[1]; outC= ST.C1[1];
ip1 = ST.T1[0]; iC1 = ST.C1[0];
ip2 = ST.T1[2]; iC2 = ST.C1[2];
} else if ( s3) {
outp= ST.T1[2]; outC= ST.C1[2];
ip1 = ST.T1[0]; iC1 = ST.C1[0];
ip2 = ST.T1[1]; iC2 = ST.C1[1];
}
Point interP1,interP2;
Color interC1,interC2;
double ble1,kne1, ble2,kne2;
Point::intersect( kn1,kn2, ip1,outp, interP1, &kne1,&ble1);
Point::intersect( kn1,kn2, ip2,outp, interP2, &kne2,&ble2);
{ if ( kn_colored && Color_valid_range(kne1))
interC1 = Color_between( *kC0, *kC1, kne1);
else
interC1 = Color_between( iC1, outC, ble1);
}
{ if ( kn_colored && Color_valid_range(kne2))
interC2 = Color_between( *kC0, *kC1, kne2);
else
interC2 = Color_between( iC2, outC, ble2);
}
//ip2 first gives a polygon
//ip1 first gives a triangle strip
if ( sums == 1) {
//one point is cut away
ST.T1[0] = ip1; ST.C1[0] = iC1;
ST.T1[1] = ip2; ST.C1[1] = iC2;
ST.T1[2] = interP1; ST.C1[2] = interC1;
ST.T1[3] = interP2; ST.C1[3] = interC2;
ST.T1c = 4;
ST.T2[0] = outp; ST.C2[0] = outC;
ST.T2[1] = interP1; ST.C2[1] = interC1;
ST.T2[2] = interP2; ST.C2[2] = interC2;
ST.T2c = 3;
points_cut_away = 1;
} else if ( sums == 2) {
//two points are cut away
ST.T2[0] = ip1; ST.C2[0] = iC1;
ST.T2[1] = ip2; ST.C2[1] = iC2;
ST.T2[2] = interP1; ST.C2[2] = interC1;
ST.T2[3] = interP2; ST.C2[3] = interC2;
ST.T2c = 4;
ST.T1[0] = outp; ST.C1[0] = outC;
ST.T1[1] = interP1; ST.C1[1] = interC1;
ST.T1[2] = interP2; ST.C1[2] = interC2;
ST.T1c = 3;
points_cut_away = 2;
}
/*if ( (0.0-vaser_min_alw < kne1 && kne1 < 1.0+vaser_min_alw) ||
(0.0-vaser_min_alw < kne2 && kne2 < 1.0+vaser_min_alw) )
{ //highlight the wound
glBegin(GL_LINE_STRIP);
glColor3f(1,0,0);
glVertex2f(ST.T1[0].x,ST.T1[0].y);
glVertex2f(ST.T1[1].x,ST.T1[1].y);
glVertex2f(ST.T1[2].x,ST.T1[2].y);
glVertex2f(ST.T1[0].x,ST.T1[0].y);
glEnd();
if ( ST.T1c > 3)
glBegin(GL_LINE_STRIP);
glVertex2f(ST.T1[1].x,ST.T1[1].y);
glVertex2f(ST.T1[2].x,ST.T1[2].y);
glVertex2f(ST.T1[3].x,ST.T1[3].y);
glVertex2f(ST.T1[1].x,ST.T1[1].y);
glEnd();
}*/
}
return points_cut_away;
}
void vah_knife_cut( vertex_array_holder& core, //serves as both input and output
const Point& kn1, const Point& kn2, const Point& kn_out)
//perform knife cut on all triangles (GL_TRIANGLES) in core
{
st_knife_cut ST;
for ( int i=0; i MAX_ST)
{
DEBUG("vah_N_knife_cut: max N for current build is %d\n", MAX_ST);
N = MAX_ST;
}
for ( int i=0; i 0)
if ( kn_colored)
triangle_knife_cut( kn0[k], kn1[k], kn2[k],
&kC0[k],&kC1[k],
ST[p]);
else
triangle_knife_cut( kn0[k],kn1[k],kn2[k],
0,0,ST[p]);
//push retaining part
if ( ST[p].T1c > 0) {
out.push( ST[p].T1[0], ST[p].C1[0]);
out.push( ST[p].T1[1], ST[p].C1[1]);
out.push( ST[p].T1[2], ST[p].C1[2]);
if ( ST[p].T1c > 3) {
out.push( ST[p].T1[1], ST[p].C1[1]);
out.push( ST[p].T1[2], ST[p].C1[2]);
out.push( ST[p].T1[3], ST[p].C1[3]);
}
}
//store cut away part to be cut again
if ( ST[p].T2c > 0)
{
ST[p].T1[0] = ST[p].T2[0];
ST[p].T1[1] = ST[p].T2[1];
ST[p].T1[2] = ST[p].T2[2];
ST[p].C1[0] = ST[p].C2[0];
ST[p].C1[1] = ST[p].C2[1];
ST[p].C1[2] = ST[p].C2[2];
ST[p].T1c = 3;
if ( ST[p].T2c > 3)
{
ST[ST_count].T1[0] = ST[p].T2[1];
ST[ST_count].T1[1] = ST[p].T2[2];
ST[ST_count].T1[2] = ST[p].T2[3];
ST[ST_count].C1[0] = ST[p].C2[1];
ST[ST_count].C1[1] = ST[p].C2[2];
ST[ST_count].C1[2] = ST[p].C2[3];
ST[ST_count].T1c = 3;
ST_count++;
}
}
else
{
ST[p].T1c = 0;
}
}
}
}
}
const float cri_core_adapt = 0.0001f;
void anchor_late( const Vec2* P, const Color* C, st_polyline* SL,
vertex_array_holder& tris,
Point cap1, Point cap2)
{ const int size_of_P = 3;
tris.set_gl_draw_mode(GL_TRIANGLES);
Point P_0, P_1, P_2;
P_0 = Point(P[0]);
P_1 = Point(P[1]);
P_2 = Point(P[2]);
if ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)
P_0 -= cap1;
if ( SL[2].djoint==PLC_butt || SL[2].djoint==PLC_square)
P_2 -= cap2;
Point P0, P1, P2, P3, P4, P5, P6, P7;
Point P0r,P1r,P2r,P3r,P4r,P5r,P6r,P7r; //fade
P0 = P_1 + SL[1].vP;
P0r = P0 + SL[1].vR;
P1 = P_1 - SL[1].vP;
P1r = P1 - SL[1].vR;
P2 = P_1 + SL[1].T1;
P2r = P2 + SL[1].R1 + SL[0].bR;
P3 = P_0 + SL[0].T;
P3r = P3 + SL[0].R;
P4 = P_0 - SL[0].T;
P4r = P4 - SL[0].R;
P5 = P_1 + SL[1].T;
P5r = P5 + SL[1].R - SL[1].bR;
P6 = P_2 + SL[2].T;
P6r = P6 + SL[2].R;
P7 = P_2 - SL[2].T;
P7r = P7 - SL[2].R;
/* annotate( P0,C[0],0);
annotate( P1);
annotate( P2);
annotate( P3);
annotate( P4);
annotate( P5);
annotate( P6);
annotate( P7); */
int normal_line_core_joint = 1; //0:dont draw, 1:draw, 2:outer only
//consider these as inline child functions
#define normal_first_segment \
tris.push3( P3, P2, P1, \
C[0],C[1],C[1]);\
tris.push3( P1, P3, P4, \
C[1],C[0],C[0])
#define normal_last_segment \
tris.push3( P1, P5, P6, \
C[1],C[1],C[2]);\
tris.push3( P1, P6, P7, \
C[1],C[2],C[2])
Color Cpt; //color at PT
if ( SL[1].degenT || SL[1].degenR)
{
float pt = sqrt(SL[1].pt);
if ( SL[1].pre_full)
Cpt = Color_between(C[0],C[1], pt);
else
Cpt = Color_between(C[1],C[2], 1-pt);
}
if ( SL[1].degenT)
{ //degen line core
P1 = SL[1].PT;
if( SL[1].degenR)
P1r = SL[1].PR;
tris.push3( P3, P2, P1,
C[0],C[1],C[1]); //fir seg
tris.push3( P1, P5, P6,
C[1],C[1],C[2]); //las seg
if ( SL[1].pre_full)
{ tris.push3( P1, P3, P4,
C[1],C[0],C[0]);
}
else
{ tris.push3( P1, P6, P7,
C[1],C[2],C[2]);
}
}
else if ( SL[1].degenR && SL[1].pt > cri_core_adapt) //&& ! SL[1].degenT
{ //line core adapted for degenR
if ( SL[1].pre_full)
{
normal_last_segment;
//special first segment
Point P9 = SL[1].PT;
tris.push3( P3, P2, P1,
C[0],C[1],C[1]);
tris.push3( P3, P9, P1,
C[0], Cpt,C[1]);
tris.push3( P3, P9, P4,
C[0], Cpt,C[0]);
}
else
{
normal_first_segment;
//special last segment
Point P9 = SL[1].PT;
push_quad( tris,
P5, P1, P6, P9,
C[1],C[1],C[2], Cpt);
tris.push3( P7, P9, P6,
C[2], Cpt,C[2]);
/*annotate(P1,C[1],1);
annotate(P5,C[1],5);
annotate(P6,C[1],6);
annotate(P7,C[1],7);
annotate(P9,C[1],9);*/
}
}
else
{
normal_first_segment;
normal_last_segment;
#undef normal_first_segment
#undef normal_last_segment
}
if (normal_line_core_joint)
{
switch( SL[1].djoint)
{
case PLJ_miter:
tris.push3( P2, P5, P0,
C[1],C[1],C[1]);
case PLJ_bevel:
if ( normal_line_core_joint==1)
tris.push3( P2, P5, P1,
C[1],C[1],C[1]);
break;
case PLJ_round: {
vertex_array_holder strip;
strip.set_gl_draw_mode(GL_TRIANGLE_STRIP);
if ( normal_line_core_joint==1)
vectors_to_arc( strip, P_1, C[1], C[1],
SL[1].T1, SL[1].T,
get_PLJ_round_dangle(SL[1].t,SL[1].r),
SL[1].t, 0.0f, false, &P1);
else if ( normal_line_core_joint==2)
vectors_to_arc( strip, P_1, C[1], C[1],
SL[1].T1, SL[1].T,
get_PLJ_round_dangle(SL[1].t,SL[1].r),
SL[1].t, 0.0f, false, &P5);
tris.push(strip);
} break;
}
}
if ( SL[1].degenR)
{ //degen inner fade
Point P9 = SL[1].PT;
Point P9r = SL[1].PR;
//annotate(P9,C[0],9);
//annotate(P9r);
Color ccpt=Cpt;
if ( SL[1].degenT)
ccpt = C[1];
if ( SL[1].pre_full)
{ push_quadf( tris,
P9, P4, P9r, P4r,
ccpt,C[0],C[1],C[0],
0, 0, 1, 1); //fir seg
if ( !SL[1].degenT)
{
Point mid = Point::midpoint(P9,P7);
tris.push3( P1, P9, mid,
C[1], Cpt,C[1],
0, 0, 1);
tris.push3( P1, P7, mid,
C[1],C[2],C[1],
0, 0, 1);
}
}
else
{ push_quadf( tris,
P9, P7, P9r, P7r,
ccpt,C[2],C[1],C[2],
0, 0, 1, 1); //las seg
if ( !SL[1].degenT)
{
Point mid = Point::midpoint(P9,P4);
tris.push3( P1, P9, mid,
C[1], Cpt,C[1],
0, 0, 1);
tris.push3( P1, P4, mid,
C[1],C[0],C[1],
0, 0, 1);
}
}
}
else
{ //normal inner fade
push_quadf( tris,
P1, P4, P1r, P4r,
C[1],C[0],C[1],C[0],
0, 0, 1, 1); //fir seg
push_quadf( tris,
P1, P7, P1r, P7r,
C[1],C[2],C[1],C[2],
0, 0, 1, 1); //las seg
}
{ //outer fade, whether degen or normal
push_quadf( tris,
P2, P3, P2r, P3r,
C[1],C[0],C[1],C[0],
0, 0, 1, 1); //fir seg
push_quadf( tris,
P5, P6, P5r, P6r,
C[1],C[2],C[1],C[2],
0, 0, 1, 1); //las seg
switch( SL[1].djoint)
{ //line fade joint
case PLJ_miter:
push_quadf( tris,
P0, P5, P0r, P5r,
C[1],C[1],C[1],C[1],
0, 0, 1, 1);
push_quadf( tris,
P0, P2, P0r, P2r,
C[1],C[1],C[1],C[1],
0, 0, 1, 1);
break;
case PLJ_bevel:
push_quadf( tris,
P2, P5, P2r, P5r,
C[1],C[1],C[1],C[1],
0, 0, 1, 1);
break;
case PLJ_round: {
vertex_array_holder strip;
strip.set_gl_draw_mode(GL_TRIANGLE_STRIP);
Color C2 = C[1]; C2.a = 0.0f;
vectors_to_arc( strip, P_1, C[1], C2,
SL[1].T1, SL[1].T,
get_PLJ_round_dangle(SL[1].t,SL[1].r),
SL[1].t, SL[1].t+SL[1].r, false, 0);
tris.push(strip);
} break;
}
}
} //anchor_late
void anchor_cap( const Vec2* P, const Color* C, st_polyline* SL,
vertex_array_holder& tris,
Point cap1, Point cap2)
{
Point P4 = Point(P[0]) - SL[0].T;
Point P7 = Point(P[2]) - SL[2].T;
for ( int i=0,k=0; k<=1; i=2, k++)
{
vertex_array_holder cap;
Point& cur_cap = i==0? cap1:cap2;
if ( cur_cap.non_zero())
{
cap.set_gl_draw_mode(GL_TRIANGLES);
bool perform_cut = ( SL[1].degenR && SL[1].R_full_degen) &&
((k==0 && !SL[1].pre_full) ||
(k==1 && SL[1].pre_full) );
Point P3 = Point(P[i])-SL[i].T*2-SL[i].R+cur_cap;
if ( SL[i].djoint == PLC_round)
{ //round caps
{ vertex_array_holder strip;
strip.set_gl_draw_mode(GL_TRIANGLE_STRIP);
Color C2 = C[i]; C2.a = 0.0f;
Point O = Point(P[i]);
Point app_P = O+SL[i].T;
Point bR = SL[i].bR;
bR.follow_signs(cur_cap);
float dangle = get_PLJ_round_dangle(SL[i].t,SL[i].r);
vectors_to_arc( strip, O, C[i], C[i],
SL[i].T+bR, -SL[i].T+bR,
dangle,
SL[i].t, 0.0f, false, &app_P);
strip.push( O-SL[i].T,C[i]);
strip.push( app_P,C[i]);
strip.jump();
Point a1 = O+SL[i].T;
Point a2 = O+SL[i].T*(1/SL[i].t)*(SL[i].t+SL[i].r);
Point b1 = O-SL[i].T;
Point b2 = O-SL[i].T*(1/SL[i].t)*(SL[i].t+SL[i].r);
strip.push( a1,C[i]);
strip.push( a2,C2);
vectors_to_arc( strip, O, C[i], C2,
SL[i].T+bR, -SL[i].T+bR,
dangle,
SL[i].t, SL[i].t+SL[i].r, false, 0);
strip.push( b1,C[i]);
strip.push( b2,C2);
cap.push(strip);
}
if ( perform_cut)
{
Point P4k;
if ( !SL[1].pre_full)
P4k = P7; //or P7r ?
else
P4k = P4;
vah_knife_cut( cap, SL[1].PT, P4k, P3);
/*annotate(SL[1].PT,C[i],0);
annotate(P3,C[i],3);
annotate(P4k,C[i],4);*/
}
}
else //if ( SL[i].djoint == PLC_butt | SL[i].cap == PLC_square | SL[i].cap == PLC_rect)
{ //rectangle caps
Point P_cur = P[i];
bool degen_nxt=0, degen_las=0;
if ( k == 0)
if ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)
P_cur -= cap1;
if ( k == 1)
if ( SL[2].djoint==PLC_butt || SL[2].djoint==PLC_square)
P_cur -= cap2;
Point P0,P1,P2,P3,P4,P5,P6;
P0 = P_cur+SL[i].T+SL[i].R;
P1 = P0+cur_cap;
P2 = P_cur+SL[i].T;
P4 = P_cur-SL[i].T;
P3 = P4-SL[i].R+cur_cap;
P5 = P4-SL[i].R;
cap.push( P0, C[i],true);
cap.push( P1, C[i],true);
cap.push( P2, C[i]);
cap.push( P1, C[i],true);
cap.push( P2, C[i]);
cap.push( P3, C[i],true);
cap.push( P2, C[i]);
cap.push( P3, C[i],true);
cap.push( P4, C[i]);
cap.push( P3, C[i],true);
cap.push( P4, C[i]);
cap.push( P5, C[i],true);
//say if you want to use triangle strip,
// just push P0~ P5 in sequence
if ( perform_cut)
{
vah_knife_cut( cap, SL[1].PT, SL[1].PR, P3);
/*annotate(SL[1].PT,C[i],0);
annotate(SL[1].PR);
annotate(P3);
annotate(P4);*/
}
}
}
tris.push(cap);
}
} //anchor_cap
void segment_late( const Vec2* P, const Color* C, st_polyline* SL,
vertex_array_holder& tris,
Point cap1, Point cap2)
{
tris.set_gl_draw_mode(GL_TRIANGLES);
Point P_0, P_1, P_2;
P_0 = Point(P[0]);
P_1 = Point(P[1]);
if ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)
P_0 -= cap1;
if ( SL[1].djoint==PLC_butt || SL[1].djoint==PLC_square)
P_1 -= cap2;
Point P1, P2, P3, P4; //core
Point P1c,P2c,P3c,P4c; //cap
Point P1r,P2r,P3r,P4r; //fade
P1 = P_0 + SL[0].T;
P1r = P1 + SL[0].R;
P1c = P1r + cap1;
P2 = P_0 - SL[0].T;
P2r = P2 - SL[0].R;
P2c = P2r + cap1;
P3 = P_1 + SL[1].T;
P3r = P3 + SL[1].R;
P3c = P3r + cap2;
P4 = P_1 - SL[1].T;
P4r = P4 - SL[1].R;
P4c = P4r + cap2;
//core
push_quad( tris,
P1, P2, P3, P4,
C[0],C[0],C[1],C[1] );
//fade
push_quadf( tris,
P1, P1r, P3, P3r,
C[0],C[0],C[1],C[1],
0, 1, 0, 1 );
push_quadf( tris,
P2, P2r, P4, P4r,
C[0],C[0],C[1],C[1],
0, 1, 0, 1 );
//caps
for ( int j=0; j<2; j++)
{
vertex_array_holder cap;
cap.set_gl_draw_mode(GL_TRIANGLE_STRIP);
Point &cur_cap = j==0?cap1:cap2;
if( cur_cap.is_zero())
continue;
if ( SL[j].djoint == PLC_round)
{ //round cap
Color C2 = C[j]; C2.a = 0.0f;
Point O = Point(P[j]);
Point app_P = O+SL[j].T;
Point bR = SL[j].bR;
bR.follow_signs( j==0?cap1:cap2);
float dangle = get_PLJ_round_dangle(SL[j].t,SL[j].r);
vectors_to_arc( cap, O, C[j], C[j],
SL[j].T+bR, -SL[j].T+bR,
dangle,
SL[j].t, 0.0f, false, &app_P);
cap.push( O-SL[j].T,C[j]);
cap.push( app_P,C[j]);
cap.jump();
{ //fade
Point a1 = O+SL[j].T;
Point a2 = O+SL[j].T*(1/SL[j].t)*(SL[j].t+SL[j].r);
Point b1 = O-SL[j].T;
Point b2 = O-SL[j].T*(1/SL[j].t)*(SL[j].t+SL[j].r);
cap.push( a1,C[j]);
cap.push( a2,C2);
vectors_to_arc( cap, O, C[j], C2,
SL[j].T+bR, -SL[j].T+bR,
dangle,
SL[j].t, SL[j].t+SL[j].r, false, 0);
cap.push( b1,C[j]);
cap.push( b2,C2);
}
}
else //if ( SL[j].djoint == PLC_butt | SL[j].cap == PLC_square | SL[j].cap == PLC_rect)
{ //rectangle cap
Point Pj,Pjr,Pjc, Pk,Pkr,Pkc;
if ( j==0)
{
Pj = P1;
Pjr= P1r;
Pjc= P1c;
Pk = P2;
Pkr= P2r;
Pkc= P2c;
}
else
{
Pj = P3;
Pjr= P3r;
Pjc= P3c;
Pk = P4;
Pkr= P4r;
Pkc= P4c;
}
cap.push( Pkr, C[j], 1);
cap.push( Pkc, C[j], 1);
cap.push( Pk , C[j], 0);
cap.push( Pjc, C[j], 1);
cap.push( Pj , C[j], 0);
cap.push( Pjr, C[j], 1);
}
tris.push(cap);
}
/*annotate(P1,C[0],1);
annotate(P2,C[0],2);
annotate(P3,C[0],3);
annotate(P4,C[0],4);
annotate(P1c,C[0],11);
annotate(P2c,C[0],21);
annotate(P3c,C[0],31);
annotate(P4c,C[0],41);
annotate(P1r,C[0],12);
annotate(P2r,C[0],22);
annotate(P3r,C[0],32);
annotate(P4r,C[0],42);
*/
}
void segment( st_anchor& SA, const polyline_opt* options, bool cap_first, bool cap_last, char last_cap_type=-1)
{
double* weight = SA.W;
if ( !SA.P || !SA.C || !weight) return;
Point P[2]; P[0]=SA.P[0]; P[1]=SA.P[1];
Color C[2]; C[0]=SA.C[0]; C[1]=SA.C[1];
polyline_opt opt={0};
if ( options)
opt = (*options);
Point T1,T2;
Point R1,R2;
Point bR;
double t,r;
bool varying_weight = !(weight[0]==weight[1]);
Point cap_start, cap_end;
st_polyline SL[2];
for ( int i=0; i<2; i++)
{
if ( weight[i]>=0.0 && weight[i]<1.0)
{
double f=weight[i]-static_cast(weight[i]);
C[i].a *=f;
}
}
{ int i=0;
make_T_R_C( P[i], P[i+1], &T2,&R2,&bR, weight[i],opt, &r,&t,0, true);
if ( cap_first)
{
if ( opt.cap==PLC_square)
{
P[0] = Point(P[0]) - bR * (t+r);
}
cap_start = bR;
cap_start.opposite(); if ( opt.feather && !opt.no_feather_at_cap)
cap_start*=opt.feathering;
}
SL[i].djoint=opt.cap;
SL[i].t=t;
SL[i].r=r;
SL[i].T=T2;
SL[i].R=R2;
SL[i].bR=bR*0.01;
SL[i].degenT = false;
SL[i].degenR = false;
}
{ int i=1;
if ( varying_weight)
make_T_R_C( P[i-1], P[i], &T2,&R2,&bR,weight[i],opt, &r,&t,0, true);
last_cap_type = last_cap_type==-1 ? opt.cap:last_cap_type;
if ( cap_last)
{
if ( last_cap_type==PLC_square)
{
P[1] = Point(P[1]) + bR * (t+r);
}
cap_end = bR;
if ( opt.feather && !opt.no_feather_at_cap)
cap_end*=opt.feathering;
}
SL[i].djoint = last_cap_type;
SL[i].t=t;
SL[i].r=r;
SL[i].T=T2;
SL[i].R=R2;
SL[i].bR=bR*0.01;
SL[i].degenT = false;
SL[i].degenR = false;
}
segment_late( P,C,SL,SA.vah, cap_start,cap_end);
}
int anchor( st_anchor& SA, const polyline_opt* options, bool cap_first, bool cap_last)
{
polyline_opt opt={0};
if ( options)
opt = (*options);
Point* P = SA.P;
Color* C = SA.C;
double* weight = SA.W;
{ st_polyline emptySL;
SA.SL[0]=emptySL; SA.SL[1]=emptySL; SA.SL[2]=emptySL;
}
st_polyline* SL = SA.SL;
SA.vah.set_gl_draw_mode(GL_TRIANGLES);
SA.cap_start = Point();
SA.cap_end = Point();
//const double critical_angle=11.6538;
// critical angle in degrees where a miter is force into bevel
// it is _similar_ to cairo_set_miter_limit () but cairo works with ratio while VASEr works with included angle
const double cos_cri_angle=0.979386; //cos(critical_angle)
bool varying_weight = !(weight[0]==weight[1] & weight[1]==weight[2]);
double combined_weight=weight[1]+(opt.feather?opt.feathering:0.0);
if ( combined_weight < cri_segment_approx)
{
segment( SA, &opt, cap_first,false, opt.joint==PLJ_round?PLC_round:PLC_butt);
char ori_cap = opt.cap;
opt.cap = opt.joint==PLJ_round?PLC_round:PLC_butt;
SA.P[0]=SA.P[1]; SA.P[1]=SA.P[2];
SA.C[0]=SA.C[1]; SA.C[1]=SA.C[2];
SA.W[0]=SA.W[1]; SA.W[1]=SA.W[2];
segment( SA, &opt, false,cap_last, ori_cap);
return 0;
}
Point T1,T2,T21,T31; //]these are for calculations in early stage
Point R1,R2,R21,R31; //]
for ( int i=0; i<3; i++)
{ //lower the transparency for weight < 1.0
if ( weight[i]>=0.0 && weight[i]<1.0)
{
double f=weight[i];
C[i].a *=f;
}
}
{ int i=0;
Point cap1;
double r,t;
make_T_R_C( P[i], P[i+1], &T2,&R2,&cap1, weight[i],opt, &r,&t,0);
if ( varying_weight) {
make_T_R_C( P[i], P[i+1], &T31,&R31,0, weight[i+1],opt, 0,0,0);
} else {
T31 = T2;
R31 = R2;
}
Point::anchor_outward(R2, P[i+1],P[i+2] /*,inward_first->value()*/);
T2.follow_signs(R2);
SL[i].bR=cap1;
if ( cap_first)
{
if ( opt.cap==PLC_square)
{
P[0] = Point(P[0]) - cap1 * (t+r);
}
cap1.opposite(); if ( opt.feather && !opt.no_feather_at_cap)
cap1*=opt.feathering;
SA.cap_start = cap1;
}
SL[i].djoint=opt.cap;
SL[i].T=T2;
SL[i].R=R2;
SL[i].t=(float)t;
SL[i].r=(float)r;
SL[i].degenT = false;
SL[i].degenR = false;
SL[i+1].T1=T31;
SL[i+1].R1=R31;
}
if ( cap_last)
{ int i=2;
Point cap2;
double t,r;
make_T_R_C( P[i-1],P[i], 0,0,&cap2,weight[i],opt, &r,&t,0);
if ( opt.cap==PLC_square)
{
P[2] = Point(P[2]) + cap2 * (t+r);
}
SL[i].bR=cap2;
if ( opt.feather && !opt.no_feather_at_cap)
cap2*=opt.feathering;
SA.cap_end = cap2;
}
{ int i=1;
double r,t;
Point P_cur = P[i]; //current point //to avoid calling constructor repeatedly
Point P_nxt = P[i+1]; //next point
Point P_las = P[i-1]; //last point
if ( opt.cap==PLC_butt || opt.cap==PLC_square)
{
P_nxt -= SA.cap_end;
P_las -= SA.cap_start;
}
{
Point bR; float length_cur, length_nxt;
make_T_R_C( P_las, P_cur, &T1,&R1, 0, weight[i-1],opt,0,0, &length_cur);
if ( varying_weight) {
make_T_R_C( P_las, P_cur, &T21,&R21,0, weight[i],opt, 0,0,0);
} else {
T21 = T1;
R21 = R1;
}
make_T_R_C( P_cur, P_nxt, &T2,&R2,&bR, weight[i],opt, &r,&t, &length_nxt);
if ( varying_weight) {
make_T_R_C( P_cur, P_nxt, &T31,&R31,0, weight[i+1],opt, 0,0,0);
} else {
T31 = T2;
R31 = R2;
}
SL[i].T=T2;
SL[i].R=R2;
SL[i].bR=bR;
SL[i].t=(float)t;
SL[i].r=(float)r;
SL[i].degenT = false;
SL[i].degenR = false;
SL[i+1].T1=T31;
SL[i+1].R1=R31;
}
{ //2nd to 2nd last point
//find the angle between the 2 line segments
Point ln1,ln2, V;
ln1 = P_cur - P_las;
ln2 = P_nxt - P_cur;
ln1.normalize();
ln2.normalize();
Point::dot(ln1,ln2, V);
double cos_tho=-V.x-V.y;
bool zero_degree = Point::negligible(cos_tho-1);
bool d180_degree = cos_tho < -1+0.0001;
bool smaller_than_30_degree = cos_tho > 0.8660254;
char result3 = 1;
if ( (cos_tho < 0 && opt.joint==PLJ_bevel) ||
(opt.joint!=PLJ_bevel && opt.cap==PLC_round) ||
(opt.joint==PLJ_round)
)
{ //when greater than 90 degrees
SL[i-1].bR *= 0.01;
SL[i] .bR *= 0.01;
SL[i+1].bR *= 0.01;
//to solve an overdraw in bevel and round joint
}
Point::anchor_outward( T1, P_cur,P_nxt);
R1.follow_signs(T1);
Point::anchor_outward( T21, P_cur,P_nxt);
R21.follow_signs(T21);
SL[i].T1.follow_signs(T21);
SL[i].R1.follow_signs(T21);
Point::anchor_outward( T2, P_cur,P_las);
R2.follow_signs(T2);
SL[i].T.follow_signs(T2);
SL[i].R.follow_signs(T2);
Point::anchor_outward( T31, P_cur,P_las);
R31.follow_signs(T31);
{ //must do intersection
Point interP, vP;
result3 = Point::intersect( P_las+T1, P_cur+T21,
P_nxt+T31, P_cur+T2,
interP);
if ( result3) {
vP = interP - P_cur;
SL[i].vP=vP;
SL[i].vR=vP*(r/t);
} else {
SL[i].vP=SL[i].T;
SL[i].vR=SL[i].R;
DEBUG( "intersection failed: cos(angle)=%.4f, angle=%.4f(degree)\n", cos_tho, acos(cos_tho)*180/3.14159);
}
}
T1.opposite(); //]inward
R1.opposite();
T21.opposite();
R21.opposite();
T2.opposite();
R2.opposite();
T31.opposite();
R31.opposite();
//make intersections
Point PR1,PR2, PT1,PT2;
double pt1,pt2;
char result1r = Point::intersect( P_nxt-T31-R31, P_nxt+T31+R31,
P_las+T1+R1, P_cur+T21+R21, //knife1
PR1); //fade
char result2r = Point::intersect( P_las-T1-R1, P_las+T1+R1,
P_nxt+T31+R31, P_cur+T2+R2, //knife2
PR2);
bool is_result1r = result1r == 1;
bool is_result2r = result2r == 1;
//
char result1t = Point::intersect( P_nxt-T31, P_nxt+T31,
P_las+T1, P_cur+T21, //knife1_a
PT1, 0,&pt1); //core
char result2t = Point::intersect( P_las-T1, P_las+T1,
P_nxt+T31, P_cur+T2, //knife2_a
PT2, 0,&pt2);
bool is_result1t = result1t == 1;
bool is_result2t = result2t == 1;
//
bool inner_sec = Point::intersecting( P_las+T1+R1, P_cur+T21+R21,
P_nxt+T31+R31, P_cur+T2+R2);
//
if ( zero_degree)
{
bool pre_full = is_result1t;
opt.no_feather_at_cap=true;
if ( pre_full)
{
segment( SA, &opt, true,cap_last, opt.joint==PLJ_round?PLC_round:PLC_butt);
}
else
{
char ori_cap = opt.cap;
opt.cap = opt.joint==PLJ_round?PLC_round:PLC_butt;
SA.P[0]=SA.P[1]; SA.P[1]=SA.P[2];
SA.C[0]=SA.C[1]; SA.C[1]=SA.C[2];
SA.W[0]=SA.W[1]; SA.W[1]=SA.W[2];
segment( SA, &opt, true,cap_last, ori_cap);
}
return 0;
}
if ( (is_result1r | is_result2r) && !inner_sec)
{ //fade degeneration
SL[i].degenR=true;
SL[i].PT = is_result1r? PT1:PT2; //this is is_result1r!!
SL[i].PR = is_result1r? PR1:PR2;
SL[i].pt = float(is_result1r? pt1:pt2);
if ( SL[i].pt < 0)
SL[i].pt = cri_core_adapt;
SL[i].pre_full = is_result1r;
SL[i].R_full_degen = false;
Point P_nxt = P[i+1]; //override that in the parent scope
Point P_las = P[i-1];
Point PR;
if ( opt.cap==PLC_rect || opt.cap==PLC_round)
{
P_nxt += SA.cap_end;
P_las += SA.cap_start;
}
char result2;
if ( is_result1r)
{
result2 = Point::intersect( P_nxt-T31-R31, P_nxt+T31,
P_las+T1+R1, P_cur+T21+R21, //knife1
PR); //fade
}
else
{
result2 = Point::intersect( P_las-T1-R1, P_las+T1,
P_nxt+T31+R31, P_cur+T2+R2, //knife2
PR);
}
if ( result2 == 1)
{
SL[i].R_full_degen = true;
SL[i].PR = PR;
}
}
if ( is_result1t | is_result2t)
{ //core degeneration
SL[i].degenT=true;
SL[i].pre_full=is_result1t;
SL[i].PT = is_result1t? PT1:PT2;
SL[i].pt = float(is_result1t? pt1:pt2);
}
//make joint
SL[i].djoint = opt.joint;
if ( opt.joint == PLJ_miter)
if ( cos_tho >= cos_cri_angle)
SL[i].djoint=PLJ_bevel;
/*if ( varying_weight && smaller_than_30_degree)
{ //not sure why, but it appears to solve a visual bug for varing weight
Point interR,vR;
char result3 = Point::intersect( P_las-T1-R1, P_cur-T21-R21,
P_nxt-T31-R31, P_cur-T2-R2,
interR);
SL[i].vR = P_cur-interR-SL[i].vP;
annotate(interR,C[i],9);
draw_vector(P_las-T1-R1, P_cur-T21-R21 - P_las+T1+R1,"1");
draw_vector(P_nxt-T31-R31, P_cur-T2-R2 - P_nxt+T31+R31,"2");
}*/
if ( d180_degree | !result3)
{ //to solve visual bugs 3 and 1.1
//efficiency: if color and weight is same as previous and next point
// ,do not generate vertices
same_side_of_line( SL[i].R, SL[i-1].R, P_cur,P_las);
SL[i].T.follow_signs(SL[i].R);
SL[i].vP=SL[i].T;
SL[i].T1.follow_signs(SL[i].T);
SL[i].R1.follow_signs(SL[i].T);
SL[i].vR=SL[i].R;
SL[i].djoint=PLJ_miter;
}
} //2nd to 2nd last point
}
{ int i=2;
double r,t;
make_T_R_C( P[i-1],P[i], &T2,&R2,0,weight[i],opt, &r,&t,0);
same_side_of_line( R2, SL[i-1].R, P[i-1],P[i]);
T2.follow_signs(R2);
SL[i].djoint=opt.cap;
SL[i].T=T2;
SL[i].R=R2;
SL[i].t=(float)t;
SL[i].r=(float)r;
SL[i].degenT = false;
SL[i].degenR = false;
}
if( cap_first || cap_last)
anchor_cap( SA.P,SA.C, SA.SL,SA.vah, SA.cap_start,SA.cap_end);
anchor_late( SA.P,SA.C, SA.SL,SA.vah, SA.cap_start,SA.cap_end);
return 1;
} //anchor
#ifdef VASE_RENDERER_EXPER
template
class circular_array
{
const int size;
int cur; //current
T* array;
public:
circular_array(int size_) : size(size_)
{
array = new T[size];
cur = 0;
}
~circular_array()
{
delete[] array;
}
void push( T obj)
{
array[cur] = obj;
move(1);
}
int get_size() const
{ return size;}
int get_i( int i) const //get valid index relative to current
{
int des = cur + i%size;
if ( des > size-1)
{
des -= size;
}
if ( des < 0)
{
des = size+i;
}
return des;
}
void move( int i) //move current relatively
{
cur = get_i(i);
}
T& operator[] (int i) //get element at relative position
{
return array[get_i(i)];
}
};
#endif //VASE_RENDERER_EXPER
struct polyline_inopt
{
bool const_color;
bool const_weight;
bool no_cap_first;
bool no_cap_last;
bool join_first;
bool join_last;
double* segment_length; //array of length of each segment
};
void poly_point_inter( const Point* P, const Color* C, const double* W, const polyline_inopt* inopt,
Point& p, Color& c, double& w,
int at, double t)
{
#define color(I) C[inopt&&inopt->const_color?0: I]
#define weight(I) W[inopt&&inopt->const_weight?0: I]
if( t==0.0)
{
p = P[at];
c = color(at);
w = weight(at);
}
else if( t==1.0)
{
p = P[at+1];
c = color(at+1);
w = weight(at+1);
}
else
{
p = (P[at]+P[at+1]) * t;
c = Color_between(color(at),color(at+1), t);
w = (weight(at)+weight(at+1)) * t;
}
#undef color
#undef weight
}
void polyline_approx(
const Vec2* points,
const Color* C,
const double* W,
int length,
const polyline_opt* opt,
const polyline_inopt* inopt)
{
const Point* P=(Point*)points;
bool cap_first= inopt? !inopt->no_cap_first :true;
bool cap_last= inopt? !inopt->no_cap_last :true;
double* seg_len=inopt? inopt->segment_length: 0;
st_anchor SA1,SA2;
vertex_array_holder vcore; //curve core
vertex_array_holder vfadeo; //outer fade
vertex_array_holder vfadei; //inner fade
vcore.set_gl_draw_mode(GL_TRIANGLE_STRIP);
vfadeo.set_gl_draw_mode(GL_TRIANGLE_STRIP);
vfadei.set_gl_draw_mode(GL_TRIANGLE_STRIP);
if( length<2)
return;
#define color(I) C[inopt&&inopt->const_color?0: I]
#define weight(I) W[inopt&&inopt->const_weight?0: I]
for( int i=1; ifeather && !opt->no_feather_at_core)
r*=opt->feathering;
Point V=P[i]-P[i-1];
V.perpen();
V.normalize();
Point F=V*r;
V*=t;
vcore.push( P[i]+V, color(i));
vcore.push( P[i]-V, color(i));
vfadeo.push( P[i]+V, color(i));
vfadeo.push( P[i]+V+F, color(i), 1);
vfadei.push( P[i]-V, color(i));
vfadei.push( P[i]-V-F, color(i), 1);
}
Point P_las,P_fir;
Color C_las,C_fir;
double W_las,W_fir;
poly_point_inter( P,C,W,inopt, P_las,C_las,W_las, length-2, 0.5);
{
double t,r;
determine_t_r(W_las,t,r);
if ( opt && opt->feather && !opt->no_feather_at_core)
r*=opt->feathering;
Point V=P[length-1]-P[length-2];
V.perpen();
V.normalize();
Point F=V*r;
V*=t;
vcore.push( P_las+V, C_las);
vcore.push( P_las-V, C_las);
vfadeo.push( P_las+V, C_las);
vfadeo.push( P_las+V+F, C_las, 1);
vfadei.push( P_las-V, C_las);
vfadei.push( P_las-V-F, C_las, 1);
}
//first caps
{
poly_point_inter( P,C,W,inopt, P_fir,C_fir,W_fir, 0, inopt&&inopt->join_first? 0.5:0.0);
SA1.P[0] = P_fir;
SA1.P[1] = P[1];
SA1.C[0] = C_fir;
SA1.C[1] = color(1);
SA1.W[0] = W_fir;
SA1.W[1] = weight(1);
segment( SA1, opt, cap_first,false);
}
//last cap
if( !(inopt&&inopt->join_last))
{
SA2.P[0] = P_las;
SA2.P[1] = P[length-1];
SA2.C[0] = C_las;
SA2.C[1] = color(length-1);
SA2.W[0] = W_las;
SA2.W[1] = weight(length-1);
segment( SA2, opt, false,cap_last);
}
#undef color
#undef weight
if( opt && opt->tess && opt->tess->tessellate_only && opt->tess->holder)
{
vertex_array_holder& holder = *(vertex_array_holder*)opt->tess->holder;
holder.push(vcore);
holder.push(vfadeo);
holder.push(vfadei);
holder.push(SA1.vah);
holder.push(SA2.vah);
}
else
{
vcore.draw();
vfadeo.draw();
vfadei.draw();
SA1.vah.draw();
SA2.vah.draw();
}
if ( opt && opt->tess && opt->tess->triangulation)
{
vcore.draw_triangles();
vfadeo.draw_triangles();
vfadei.draw_triangles();
SA1.vah.draw_triangles();
SA2.vah.draw_triangles();
}
}
void polyline_exact(
const Vec2* P,
const Color* C,
const double* W,
int size_of_P,
const polyline_opt* opt,
const polyline_inopt* inopt)
{
bool cap_first= inopt? !inopt->no_cap_first :true;
bool cap_last= inopt? !inopt->no_cap_last :true;
bool join_first = inopt && inopt->join_first;
bool join_last = inopt && inopt->join_last;
#define color(I) C[inopt&&inopt->const_color?0: I]
#define weight(I) W[inopt&&inopt->const_weight?0: I]
Point mid_l, mid_n; //the last and the next mid point
Color c_l, c_n;
double w_l, w_n;
{ //init for the first anchor
poly_point_inter( (Point*)P,C,W,inopt, mid_l,c_l,w_l, 0, join_first?0.5:0);
}
st_anchor SA;
if ( size_of_P == 2)
{
SA.P[0] = P[0];
SA.P[1] = P[1];
SA.C[0] = color(0);
SA.C[1] = color(1);
SA.W[0] = weight(0);
SA.W[1] = weight(1);
segment( SA, opt, cap_first, cap_last);
}
else
for ( int i=1; itess && opt->tess->tessellate_only && opt->tess->holder)
(*(vertex_array_holder*)opt->tess->holder).push(SA.vah);
else
SA.vah.draw();
//draw triangles
if( opt && opt->tess && opt->tess->triangulation)
SA.vah.draw_triangles();
#undef color
#undef weight
}
void polyline_range(
const Vec2* P,
const Color* C,
const double* W,
int length,
const polyline_opt* opt,
const polyline_inopt* in_options,
int from, int to,
bool approx)
{
polyline_inopt inopt={0};
if( in_options) inopt=*in_options;
if( from>0) from-=1;
inopt.join_first = from!=0;
inopt.join_last = to!=(length-1);
inopt.no_cap_first = inopt.no_cap_first || inopt.join_first;
inopt.no_cap_last = inopt.no_cap_last || inopt.join_last;
if( approx)
polyline_approx( P+from, C+(inopt.const_color?0:from), W+(inopt.const_weight?0:from), to-from+1, opt, &inopt);
else
polyline_exact ( P+from, C+(inopt.const_color?0:from), W+(inopt.const_weight?0:from), to-from+1, opt, &inopt);
}
void polyline(
const Vec2* PP, //pointer to array of point of a polyline
const Color* C, //array of color
const double* W, //array of weight
int length, //size of the buffer P
const polyline_opt* options, //options
const polyline_inopt* in_options) //internal options
{
polyline_opt opt={0};
polyline_inopt inopt={0};
if( options) opt=*options;
if( in_options) inopt=*in_options;
/* if( opt.fallback)
{
backend::polyline(PP,C[0],W[0],length,0);
return;
} */
if( opt.cap >= 10)
{
char dec=(opt.cap/10)*10;
if( dec==PLC_first || dec==PLC_none)
inopt.no_cap_last=true;
if( dec==PLC_last || dec==PLC_none)
inopt.no_cap_first=true;
opt.cap -= dec;
}
if( inopt.const_weight && W[0] < cri_segment_approx)
{
polyline_exact(PP,C,W,length,&opt,&inopt);
return;
}
const Point* P=(Point*)PP;
int A=0,B=0;
bool on=false;
for( int i=1; icos_a) ||
(costho>cos_b) || //when the angle difference at an anchor is smaller than a critical degree, do polyline approximation
(lencos_c) ) //when vector length is smaller than weight, do approximation
approx = true;
if( approx && !on)
{
A=i; if( A==1) A=0;
on=true;
if( A>1)
polyline_range(PP,C,W,length,&opt,&inopt,B,A,false);
}
else if( !approx && on)
{
B=i;
on=false;
polyline_range(PP,C,W,length,&opt,&inopt,A,B,true);
}
}
if( on && B
#include
#include
namespace VASEr
{
namespace VASErin
{ //VASEr internal namespace
const double vaser_min_alw=0.00000000001; //smallest value not regarded as zero
const Color default_color = {0,0,0,1};
const double default_weight = 1.0;
#include "point.h"
#include "color.h"
class vertex_array_holder;
#include "backend.h"
#include "vertex_array_holder.h"
#include "agg_curve4.cpp"
}
#include "opengl.cpp"
#include "polyline.cpp"
#include "gradient.cpp"
#include "curve.cpp"
} //namespace VASEr
#undef DEBUG
#endif
================================================
FILE: cpp/vaser/vaser.h
================================================
#ifndef VASER_H
#define VASER_H
/* Vase Renderer first draft, version 0.3 */
/* Basic usage
* You should provide these structs before any vase_renderer include, using
struct Vec2 { double x,y;};
struct Color { float r,g,b,a;};
* or
typedef your_vec2 Vec2;
typedef your_color Color;
*/
namespace VASEr
{
const double vaser_pi=3.141592653589793;
struct gradient_stop
{
double t; //position
char type; //GS_xx
union
{
Color color;
double weight;
};
};
const char GS_none =0;
const char GS_rgba =1;
const char GS_rgb =2; //rgb only
const char GS_alpha =3;
const char GS_weight=4;
struct gradient
{
gradient_stop* stops; //array must be sorted in ascending order of t
int length; //number of stops
char unit; //use_GD_XX
};
const char GD_ratio =0; //default
const char GD_length=1;
struct Image
{
unsigned char* buf; //must **free** buffer manually
short width;
short height;
};
class renderer
{
public:
static void init();
static void before();
static void after();
static Image get_image();
};
struct tessellator_opt
{
//set the whole structure to 0 will give default options
bool triangulation;
char parts; //use TS_xx
bool tessellate_only;
void* holder; //used as (VASErin::vertex_array_holder*) if tessellate_only is true
};
//for tessellator_opt.parts
const char TS_core_fade =0; //default
const char TS_core =1;
const char TS_outer_fade=2;
const char TS_inner_fade=3;
struct polyline_opt
{
//set the whole structure to 0 will give default options
const tessellator_opt* tess;
char joint; //use PLJ_xx
char cap; //use PLC_xx
bool feather;
double feathering;
bool no_feather_at_cap;
bool no_feather_at_core;
};
//for polyline_opt.joint
const char PLJ_miter =0; //default
const char PLJ_bevel =1;
const char PLJ_round =2;
//for polyline_opt.cap
const char PLC_butt =0; //default
const char PLC_round =1;
const char PLC_square=2;
const char PLC_rect =3;
const char PLC_both =0; //default
const char PLC_first =10;
const char PLC_last =20;
const char PLC_none =30;
void polyline( const Vec2*, const Color*, const double*, int length, const polyline_opt*);
void polyline( const Vec2*, Color, double W, int length, const polyline_opt*); //constant color and weight
void polyline( const Vec2*, const Color*, double W, int length, const polyline_opt*); //constant weight
void polyline( const Vec2*, Color, const double* W, int length, const polyline_opt*); //constant color
void segment( Vec2, Vec2, Color, Color, double W1, double W2, const polyline_opt*);
void segment( Vec2, Vec2, Color, double W, const polyline_opt*); //constant color and weight
void segment( Vec2, Vec2, Color, Color, double W, const polyline_opt*); //constant weight
void segment( Vec2, Vec2, Color, double W1, double W2, const polyline_opt*); //const color
struct polybezier_opt
{
//set the whole structure to 0 will give default options
const polyline_opt* poly;
};
void polybezier( const Vec2*, const gradient*, int length, const polybezier_opt*);
void polybezier( const Vec2*, Color, double W, int length, const polybezier_opt*);
} //namespace VASEr
#endif
================================================
FILE: cpp/vaser/vertex_array_holder.h
================================================
#ifndef VASER_VERTEX_ARRAY_HOLDER_H
#define VASER_VERTEX_ARRAY_HOLDER_H
class vertex_array_holder
{
public:
int count; //counter
int glmode; //drawing mode in opengl
bool jumping;
std::vector vert; //because it holds 2d vectors
std::vector color; //RGBA
vertex_array_holder()
{
count = 0;
glmode = GL_TRIANGLES;
jumping = false;
}
void set_gl_draw_mode( int gl_draw_mode)
{
glmode = gl_draw_mode;
}
void clear()
{
count = 0;
}
void move( int a, int b) //move b into a
{
vert[a*2] = vert[b*2];
vert[a*2+1] = vert[b*2+1];
color[a*4] = color[b*4];
color[a*4+1]= color[b*4+1];
color[a*4+2]= color[b*4+2];
color[a*4+3]= color[b*4+3];
}
void replace( int a, Point P, Color C)
{
vert[a*2] = P.x;
vert[a*2+1] = P.y;
color[a*4] = C.r;
color[a*4+1]= C.g;
color[a*4+2]= C.b;
color[a*4+3]= C.a;
}
/* int draw_and_flush()
{
int& i = count;
draw();
switch( glmode)
{
case GL_POINTS:
i=0;
break;
case GL_LINES:
if ( i%2 == 0) {
i=0;
} else {
goto copy_the_last_point;
}
break;
case GL_TRIANGLES:
if ( i%3 == 0) {
i=0;
} else if ( i%3 == 1) {
goto copy_the_last_point;
} else {
goto copy_the_last_2_points;
}
break;
case GL_LINE_STRIP: case GL_LINE_LOOP:
//for line loop it is not correct
copy_the_last_point:
move(0,MAX_VERT-1);
i=1;
break;
case GL_TRIANGLE_STRIP:
copy_the_last_2_points:
move(0,MAX_VERT-2);
move(1,MAX_VERT-1);
i=2;
break;
case GL_TRIANGLE_FAN:
//retain the first point,
// and copy the last point
move(1,MAX_VERT-1);
i=2;
break;
case GL_QUAD_STRIP:
case GL_QUADS:
case GL_POLYGON:
//let it be and I cannot help
i=0;
break;
}
if ( i == MAX_VERT) //as a double check
i=0;
}*/
int push( const Point& P, const Color& cc, bool trans=false)
{
int cur = count;
vert.push_back(P.x);
vert.push_back(P.y);
color.push_back(cc.r);
color.push_back(cc.g);
color.push_back(cc.b);
color.push_back(trans?0.0f:cc.a);
count++;
if ( jumping)
{
jumping=false;
repeat_last_push();
}
return cur;
}
void push3( const Point& P1, const Point& P2, const Point& P3,
const Color& C1, const Color& C2, const Color& C3,
bool trans1=0, bool trans2=0, bool trans3=0)
{
push( P1,C1,trans1);
push( P2,C2,trans2);
push( P3,C3,trans3);
}
void push( const vertex_array_holder& hold)
{
if ( glmode == hold.glmode)
{
count += hold.count;
vert.insert(vert.end(), hold.vert.begin(), hold.vert.end());
color.insert(color.end(), hold.color.begin(), hold.color.end());
}
else if ( glmode == GL_TRIANGLES &&
hold.glmode == GL_TRIANGLE_STRIP)
{
int& a = count;
for (int b=2; b < hold.count; b++)
{
for ( int k=0; k<3; k++,a++)
{
int B = b-2 + k;
vert.push_back(hold.vert[B*2]);
vert.push_back(hold.vert[B*2+1]);
color.push_back(hold.color[B*4]);
color.push_back(hold.color[B*4+1]);
color.push_back(hold.color[B*4+2]);
color.push_back(hold.color[B*4+3]);
}
}
}
else
{
DEBUG( "vertex_array_holder:push: unknown type\n");
}
}
Point get(int i)
{
Point P;
P.x = vert[i*2];
P.y = vert[i*2+1];
return P;
}
Color get_color(int b)
{
Color C;
C.r = color[b*4];
C.g = color[b*4+1];
C.b = color[b*4+2];
C.a = color[b*4+3];
return C;
}
Point get_relative_end(int di=-1)
{ //di=-1 is the last one
int i = count+di;
if ( i<0) i=0;
if ( i>=count) i=count-1;
return get(i);
}
void repeat_last_push()
{
Point P;
Color cc;
int i = count-1;
P.x = vert[i*2];
P.y = vert[i*2+1];
cc.r = color[i*4];
cc.g = color[i*4+1];
cc.b = color[i*4+2];
cc.a = color[i*4+3];
push(P,cc);
}
void jump() //to make a jump in triangle strip by degenerated triangles
{
if ( glmode == GL_TRIANGLE_STRIP)
{
repeat_last_push();
jumping=true;
}
}
void draw()
{
backend::vah_draw(*this);
}
void draw_triangles()
{
Color col={1 , 0, 0, 0.5};
if ( glmode == GL_TRIANGLES)
{
for ( int i=0; i
This is description.