agg/examples/graph_test.cpp

926 lines
26 KiB
C++

#include <math.h>
#include <stdio.h>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_rasterizer_outline.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "agg_conv_curve.h"
#include "agg_conv_contour.h"
#include "agg_conv_marker.h"
#include "agg_conv_shorten_path.h"
#include "agg_conv_marker_adaptor.h"
#include "agg_conv_concat.h"
#include "agg_arrowhead.h"
#include "agg_vcgen_markers_term.h"
#include "agg_scanline_p.h"
#include "agg_scanline_u.h"
#include "agg_renderer_scanline.h"
#include "agg_renderer_primitives.h"
#include "agg_span_allocator.h"
#include "agg_span_gradient.h"
#include "agg_span_interpolator_linear.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "ctrl/agg_slider_ctrl.h"
#include "ctrl/agg_rbox_ctrl.h"
#include "ctrl/agg_cbox_ctrl.h"
#include "platform/agg_platform_support.h"
#define AGG_BGR24
//#define AGG_BGR96
#include "pixel_formats.h"
enum flip_y_e { flip_y = true };
typedef agg::renderer_base<pixfmt> base_renderer;
typedef agg::renderer_primitives<base_renderer> primitives_renderer;
typedef agg::renderer_scanline_aa_solid<base_renderer> solid_renderer;
typedef agg::renderer_scanline_bin_solid<base_renderer> draft_renderer;
typedef agg::gradient_radial_d gradient_function;
typedef agg::span_interpolator_linear<> interpolator;
typedef agg::pod_auto_array<color_type, 256> color_array_type;
typedef agg::span_gradient<color_type,
interpolator,
gradient_function,
color_array_type> gradient_span_gen;
typedef agg::span_allocator<color_type> gradient_span_alloc;
typedef agg::renderer_scanline_aa<base_renderer,
gradient_span_alloc,
gradient_span_gen> gradient_renderer;
typedef agg::rasterizer_scanline_aa<> scanline_rasterizer;
typedef agg::rasterizer_outline<primitives_renderer> outline_rasterizer;
//============================================================================
class graph
{
public:
struct node
{
double x, y;
node() {}
node(double x_, double y_) : x(x_), y(y_) {}
};
struct edge
{
int node1;
int node2;
edge() {}
edge(int n1, int n2) : node1(n1), node2(n2) {}
};
~graph()
{
delete [] m_edges;
delete [] m_nodes;
}
graph(int num_nodes, int num_edges) :
m_num_nodes(num_nodes),
m_num_edges(num_edges),
m_nodes(new node[num_nodes]),
m_edges(new edge[num_edges])
{
int i;
srand(100);
for(i = 0; i < m_num_nodes; i++)
{
m_nodes[i].x = (double(rand()) / RAND_MAX) * 0.75 + 0.2;
m_nodes[i].y = (double(rand()) / RAND_MAX) * 0.85 + 0.1;
}
for(i = 0; i < m_num_edges; i++)
{
m_edges[i].node1 = rand() % m_num_nodes;
m_edges[i].node2 = rand() % m_num_nodes;
if(m_edges[i].node1 == m_edges[i].node2) i--;
}
}
int get_num_nodes() const { return m_num_nodes; }
int get_num_edges() const { return m_num_edges; }
node get_node(int idx, double w, double h) const
{
node p(0.0, 0.0);
if(idx < m_num_nodes)
{
p = m_nodes[idx];
p.x = p.x * w;
p.y = p.y * h;
}
return p;
}
edge get_edge(int idx) const
{
edge b(0,0);
if(idx < m_num_edges)
{
b = m_edges[idx];
}
return b;
}
private:
graph(const graph&);
const graph& operator = (const graph&);
int m_num_nodes;
int m_num_edges;
node* m_nodes;
edge* m_edges;
};
//============================================================================
struct line
{
double x1, y1, x2, y2;
int f;
line(double x1_, double y1_, double x2_, double y2_) :
x1(x1_), y1(y1_), x2(x2_), y2(y2_), f(0) {}
void rewind(unsigned) { f = 0; }
unsigned vertex(double* x, double* y)
{
if(f == 0) { ++f; *x = x1; *y = y1; return agg::path_cmd_move_to; }
if(f == 1) { ++f; *x = x2; *y = y2; return agg::path_cmd_line_to; }
return agg::path_cmd_stop;
}
};
//============================================================================
struct curve
{
agg::curve4 c;
curve(double x1, double y1, double x2, double y2, double k=0.5)
{
c.init(x1, y1,
x1 - (y2 - y1) * k,
y1 + (x2 - x1) * k,
x2 + (y2 - y1) * k,
y2 - (x2 - x1) * k,
x2, y2);
}
void rewind(unsigned path_id) { c.rewind(path_id); }
unsigned vertex(double* x, double* y) { return c.vertex(x, y); }
};
//============================================================================
template<class Source> struct stroke_draft_simple
{
Source& s;
stroke_draft_simple(Source& src, double w) :
s(src)
{
}
void rewind(unsigned path_id) { s.rewind(path_id); }
unsigned vertex(double* x, double* y) { return s.vertex(x, y); }
};
//============================================================================
template<class Source> struct stroke_draft_arrow
{
typedef agg::conv_marker_adaptor<Source, agg::vcgen_markers_term> stroke_type;
typedef agg::conv_marker<typename stroke_type::marker_type, agg::arrowhead> marker_type;
typedef agg::conv_concat<stroke_type, marker_type> concat_type;
stroke_type s;
agg::arrowhead ah;
marker_type m;
concat_type c;
stroke_draft_arrow(Source& src, double w) :
s(src),
ah(),
m(s.markers(), ah),
c(s, m)
{
ah.head(0, 10, 5, 0);
s.shorten(10.0);
}
void rewind(unsigned path_id) { c.rewind(path_id); }
unsigned vertex(double* x, double* y) { return c.vertex(x, y); }
};
//============================================================================
template<class Source> struct stroke_fine_simple
{
typedef agg::conv_stroke<Source> stroke_type;
stroke_type s;
stroke_fine_simple(Source& src, double w) :
s(src)
{
s.width(w);
}
void rewind(unsigned path_id) { s.rewind(path_id); }
unsigned vertex(double* x, double* y) { return s.vertex(x, y); }
};
//============================================================================
template<class Source> struct stroke_fine_arrow
{
typedef agg::conv_stroke<Source, agg::vcgen_markers_term> stroke_type;
typedef agg::conv_marker<typename stroke_type::marker_type, agg::arrowhead> marker_type;
typedef agg::conv_concat<stroke_type, marker_type> concat_type;
stroke_type s;
agg::arrowhead ah;
marker_type m;
concat_type c;
stroke_fine_arrow(Source& src, double w) :
s(src),
ah(),
m(s.markers(), ah),
c(s, m)
{
s.width(w);
ah.head(0, 10, 5, 0);
s.shorten(w * 2.0);
}
void rewind(unsigned path_id) { c.rewind(path_id); }
unsigned vertex(double* x, double* y) { return c.vertex(x, y); }
};
//============================================================================
template<class Source> struct dash_stroke_draft_simple
{
typedef agg::conv_dash<Source, agg::vcgen_markers_term> dash_type;
dash_type d;
dash_stroke_draft_simple(Source& src,
double dash_len,
double gap_len,
double w) :
d(src)
{
d.add_dash(dash_len, gap_len);
}
void rewind(unsigned path_id) { d.rewind(path_id); }
unsigned vertex(double* x, double* y) { return d.vertex(x, y); }
};
//============================================================================
template<class Source> struct dash_stroke_draft_arrow
{
typedef agg::conv_dash<Source, agg::vcgen_markers_term> dash_type;
typedef agg::conv_marker<typename dash_type::marker_type, agg::arrowhead> marker_type;
typedef agg::conv_concat<dash_type, marker_type> concat_type;
dash_type d;
agg::arrowhead ah;
marker_type m;
concat_type c;
dash_stroke_draft_arrow(Source& src,
double dash_len, double gap_len, double w) :
d(src),
ah(),
m(d.markers(), ah),
c(d, m)
{
d.add_dash(dash_len, gap_len);
ah.head(0, 10, 5, 0);
d.shorten(10.0);
}
void rewind(unsigned path_id) { c.rewind(path_id); }
unsigned vertex(double* x, double* y) { return c.vertex(x, y); }
};
//============================================================================
template<class Source> struct dash_stroke_fine_simple
{
typedef agg::conv_dash<Source> dash_type;
typedef agg::conv_stroke<dash_type> stroke_type;
dash_type d;
stroke_type s;
dash_stroke_fine_simple(Source& src,
double dash_len, double gap_len, double w) :
d(src),
s(d)
{
d.add_dash(dash_len, gap_len);
s.width(w);
}
void rewind(unsigned path_id) { s.rewind(path_id); }
unsigned vertex(double* x, double* y) { return s.vertex(x, y); }
};
//============================================================================
template<class Source> struct dash_stroke_fine_arrow
{
typedef agg::conv_dash<Source, agg::vcgen_markers_term> dash_type;
typedef agg::conv_stroke<dash_type> stroke_type;
typedef agg::conv_marker<typename dash_type::marker_type, agg::arrowhead> marker_type;
typedef agg::conv_concat<stroke_type, marker_type> concat_type;
dash_type d;
stroke_type s;
agg::arrowhead ah;
marker_type m;
concat_type c;
dash_stroke_fine_arrow(Source& src,
double dash_len, double gap_len, double w) :
d(src),
s(d),
ah(),
m(d.markers(), ah),
c(s, m)
{
d.add_dash(dash_len, gap_len);
s.width(w);
ah.head(0, 10, 5, 0);
d.shorten(w * 2.0);
}
void rewind(unsigned path_id) { c.rewind(path_id); }
unsigned vertex(double* x, double* y) { return c.vertex(x, y); }
};
//#define stroke_draft stroke_draft_simple
//#define dash_stroke_draft dash_stroke_draft_simple
//#define stroke_fine stroke_fine_simple
//#define dash_stroke_fine dash_stroke_fine_simple
#define stroke_draft stroke_draft_arrow
#define dash_stroke_draft dash_stroke_draft_arrow
#define stroke_fine stroke_fine_arrow
#define dash_stroke_fine dash_stroke_fine_arrow
class the_application : public agg::platform_support
{
agg::rbox_ctrl<agg::rgba> m_type;
agg::slider_ctrl<agg::rgba> m_width;
agg::cbox_ctrl<agg::rgba> m_benchmark;
agg::cbox_ctrl<agg::rgba> m_draw_nodes;
agg::cbox_ctrl<agg::rgba> m_draw_edges;
agg::cbox_ctrl<agg::rgba> m_draft;
agg::cbox_ctrl<agg::rgba> m_translucent;
graph m_graph;
color_array_type m_gradient_colors;
int m_draw;
agg::scanline_u8 m_sl;
public:
the_application(agg::pix_format_e format, bool flip_y) :
agg::platform_support(format, flip_y),
m_type(-1, -1, -1, -1, !flip_y),
m_width(110+80, 8.0, 110+200.0+80, 8.0 + 7.0, !flip_y),
m_benchmark(110+200+80+8, 8.0-2.0, "Benchmark", !flip_y),
m_draw_nodes(110+200+80+8, 8.0-2.0+15.0, "Draw Nodes", !flip_y),
m_draw_edges(200+200+80+8, 8.0-2.0+15.0, "Draw Edges", !flip_y),
m_draft(200+200+80+8, 8.0-2.0, "Draft Mode", !flip_y),
m_translucent(110+80, 8.0-2.0+15.0, "Translucent Mode", !flip_y),
m_graph(200, 100),
m_gradient_colors(),
m_draw(3)
{
add_ctrl(m_type);
m_type.text_size(8.0);
m_type.add_item("Solid lines");
m_type.add_item("Bezier curves");
m_type.add_item("Dashed curves");
m_type.add_item("Poygons AA");
m_type.add_item("Poygons Bin");
m_type.cur_item(0);
add_ctrl(m_width);
m_width.num_steps(20);
m_width.range(0.0, 5.0);
m_width.value(2.0);
m_width.label("Width=%1.2f");
m_benchmark.text_size(8.0);
m_draw_nodes.text_size(8.0);
m_draft.text_size(8.0);
m_draw_nodes.status(true);
m_draw_edges.status(true);
add_ctrl(m_benchmark);
add_ctrl(m_draw_nodes);
add_ctrl(m_draw_edges);
add_ctrl(m_draft);
add_ctrl(m_translucent);
agg::rgba c1(1, 1, 0, 0.25);
agg::rgba c2(0, 0, 1);
int i;
for(i = 0; i < 256; i++)
{
m_gradient_colors[i] = c1.gradient(c2, double(i) / 255.0);
}
}
//------------------------------------------------------------------------
void draw_nodes_draft()
{
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
primitives_renderer prim(rb);
int i;
for(i = 0; i < m_graph.get_num_nodes(); i++)
{
graph::node n = m_graph.get_node(i, width(), height());
prim.fill_color(m_gradient_colors[147]);
prim.line_color(m_gradient_colors[255]);
prim.outlined_ellipse(int(n.x), int(n.y), 10, 10);
prim.fill_color(m_gradient_colors[50]);
prim.solid_ellipse(int(n.x), int(n.y), 4, 4);
}
}
//------------------------------------------------------------------------
void draw_nodes_fine(scanline_rasterizer& ras)
{
gradient_span_alloc sa;
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
int i;
for(i = 0; i < m_graph.get_num_nodes(); i++)
{
graph::node n = m_graph.get_node(i, width(), height());
agg::ellipse ell(n.x, n.y, 5.0 * m_width.value(), 5.0 * m_width.value());
double x, y;
switch(m_draw)
{
case 0:
ell.rewind(0);
while(!agg::is_stop(ell.vertex(&x, &y)));
break;
case 1:
ras.reset();
ras.add_path(ell);
break;
case 2:
ras.reset();
ras.add_path(ell);
ras.sort();
break;
case 3:
{
gradient_function gf;
agg::trans_affine mtx;
mtx *= agg::trans_affine_scaling(m_width.value() / 2.0);
mtx *= agg::trans_affine_translation(n.x, n.y);
mtx.invert();
interpolator inter(mtx);
gradient_span_gen sg(inter, gf, m_gradient_colors, 0.0, 10.0);
gradient_renderer ren(rb, sa, sg);
ras.add_path(ell);
agg::render_scanlines(ras, m_sl, ren);
}
break;
}
}
}
//------------------------------------------------------------------------
template<class Source>
void render_edge_fine(scanline_rasterizer& ras,
solid_renderer& ren_fine,
draft_renderer& ren_draft,
Source& src)
{
double x, y;
switch(m_draw)
{
case 0:
src.rewind(0);
while(!agg::is_stop(src.vertex(&x, &y)));
break;
case 1:
ras.reset();
ras.add_path(src);
break;
case 2:
ras.reset();
ras.add_path(src);
ras.sort();
break;
case 3:
{
int r = rand() & 0x7F;
int g = rand() & 0x7F;
int b = rand() & 0x7F;
int a = 255;
if(m_translucent.status()) a = 80;
ras.add_path(src);
if(m_type.cur_item() < 4)
{
ren_fine.color(agg::srgba8(r, g, b, a));
agg::render_scanlines(ras, m_sl, ren_fine);
}
else
{
ren_draft.color(agg::srgba8(r, g, b, a));
agg::render_scanlines(ras, m_sl, ren_draft);
}
}
break;
}
}
//------------------------------------------------------------------------
void draw_lines_draft()
{
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
primitives_renderer prim(rb);
outline_rasterizer ras(prim);
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge e = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(e.node1, width(), height());
graph::node n2 = m_graph.get_node(e.node2, width(), height());
line l(n1.x, n1.y, n2.x, n2.y);
stroke_draft<line> s(l, m_width.value());
int r = rand() & 0x7F;
int g = rand() & 0x7F;
int b = rand() & 0x7F;
int a = 255;
if(m_translucent.status()) a = 80;
prim.line_color(agg::srgba8(r, g, b, a));
ras.add_path(s);
}
}
//------------------------------------------------------------------------
void draw_curves_draft()
{
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
primitives_renderer prim(rb);
outline_rasterizer ras(prim);
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge e = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(e.node1, width(), height());
graph::node n2 = m_graph.get_node(e.node2, width(), height());
curve c(n1.x, n1.y, n2.x, n2.y);
stroke_draft<curve> s(c, m_width.value());
int r = rand() & 0x7F;
int g = rand() & 0x7F;
int b = rand() & 0x7F;
int a = 255;
if(m_translucent.status()) a = 80;
prim.line_color(agg::srgba8(r, g, b, a));
ras.add_path(s);
}
}
//------------------------------------------------------------------------
void draw_dashes_draft()
{
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
primitives_renderer prim(rb);
outline_rasterizer ras(prim);
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge e = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(e.node1, width(), height());
graph::node n2 = m_graph.get_node(e.node2, width(), height());
curve c(n1.x, n1.y, n2.x, n2.y);
dash_stroke_draft<curve> s(c, 6.0, 3.0, m_width.value());
int r = rand() & 0x7F;
int g = rand() & 0x7F;
int b = rand() & 0x7F;
int a = 255;
if(m_translucent.status()) a = 80;
prim.line_color(agg::srgba8(r, g, b, a));
ras.add_path(s);
}
}
//------------------------------------------------------------------------
void draw_lines_fine(scanline_rasterizer& ras,
solid_renderer& solid,
draft_renderer& draft)
{
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge b = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(b.node1, width(), height());
graph::node n2 = m_graph.get_node(b.node2, width(), height());
line l(n1.x, n1.y, n2.x, n2.y);
stroke_fine<line> s(l, m_width.value());
render_edge_fine(ras, solid, draft, s);
}
}
//------------------------------------------------------------------------
void draw_curves_fine(scanline_rasterizer& ras,
solid_renderer& solid,
draft_renderer& draft)
{
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge b = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(b.node1, width(), height());
graph::node n2 = m_graph.get_node(b.node2, width(), height());
curve c(n1.x, n1.y, n2.x, n2.y);
stroke_fine<curve> s(c, m_width.value());
render_edge_fine(ras, solid, draft, s);
}
}
//------------------------------------------------------------------------
void draw_dashes_fine(scanline_rasterizer& ras,
solid_renderer& solid,
draft_renderer& draft)
{
int i;
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge b = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(b.node1, width(), height());
graph::node n2 = m_graph.get_node(b.node2, width(), height());
curve c(n1.x, n1.y, n2.x, n2.y);
dash_stroke_fine<curve> s(c, 6.0, 3.0, m_width.value());
render_edge_fine(ras, solid, draft, s);
}
}
//------------------------------------------------------------------------
void draw_polygons(scanline_rasterizer& ras,
solid_renderer& solid,
draft_renderer& draft)
{
int i;
if(m_type.cur_item() == 4)
{
ras.gamma(agg::gamma_threshold(0.5));
}
for(i = 0; i < m_graph.get_num_edges(); i++)
{
graph::edge b = m_graph.get_edge(i);
graph::node n1 = m_graph.get_node(b.node1, width(), height());
graph::node n2 = m_graph.get_node(b.node2, width(), height());
curve c(n1.x, n1.y, n2.x, n2.y);
render_edge_fine(ras, solid, draft, c);
}
ras.gamma(agg::gamma_none());
}
//------------------------------------------------------------------------
void draw_scene(scanline_rasterizer& ras,
solid_renderer& solid,
draft_renderer& draft)
{
ras.gamma(agg::gamma_none());
srand(100);
if(m_draw_nodes.status())
{
if(m_draft.status())
{
draw_nodes_draft();
}
else
{
draw_nodes_fine(ras);
}
}
if(m_draw_edges.status())
{
if(m_draft.status())
{
switch(m_type.cur_item())
{
case 0: draw_lines_draft(); break;
case 1: draw_curves_draft(); break;
case 2: draw_dashes_draft(); break;
}
}
else
{
switch(m_type.cur_item())
{
case 0: draw_lines_fine(ras, solid, draft); break;
case 1: draw_curves_fine(ras, solid, draft); break;
case 2: draw_dashes_fine(ras, solid, draft); break;
case 3:
case 4: draw_polygons(ras, solid, draft); break;
}
}
}
}
//------------------------------------------------------------------------
virtual void on_draw()
{
scanline_rasterizer ras;
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
solid_renderer solid(rb);
draft_renderer draft(rb);
rb.clear(agg::rgba(1, 1, 1));
draw_scene(ras, solid, draft);
ras.filling_rule(agg::fill_non_zero);
agg::render_ctrl(ras, m_sl, rb, m_type);
agg::render_ctrl(ras, m_sl, rb, m_width);
agg::render_ctrl(ras, m_sl, rb, m_benchmark);
agg::render_ctrl(ras, m_sl, rb, m_draw_nodes);
agg::render_ctrl(ras, m_sl, rb, m_draw_edges);
agg::render_ctrl(ras, m_sl, rb, m_draft);
agg::render_ctrl(ras, m_sl, rb, m_translucent);
}
virtual void on_ctrl_change()
{
if(m_benchmark.status())
{
int i;
on_draw();
update_window();
scanline_rasterizer ras;
pixfmt pixf(rbuf_window());
base_renderer rb(pixf);
solid_renderer solid(rb);
draft_renderer draft(rb);
char buf[256];
if(m_draft.status())
{
start_timer();
for(i = 0; i < 10; i++)
{
draw_scene(ras, solid, draft);
}
sprintf(buf, "%3.3f milliseconds", elapsed_time());
}
else
{
double times[5];
for(m_draw = 0; m_draw < 4; m_draw++)
{
start_timer();
for(i = 0; i < 10; i++)
{
draw_scene(ras, solid, draft);
}
times[m_draw] = elapsed_time();
}
m_draw = 3;
times[4] = times[3];
times[3] -= times[2];
times[2] -= times[1];
times[1] -= times[0];
FILE* fd = fopen(full_file_name("benchmark"), "a");
fprintf(fd, "%10.3f %10.3f %10.3f %10.3f %10.3f\n",
times[0], times[1], times[2], times[3], times[4]);
fclose(fd);
sprintf(buf, " pipeline add_path sort render total\n"
"%10.3f %10.3f %10.3f %10.3f %10.3f",
times[0], times[1], times[2], times[3], times[4]);
}
message(buf);
m_benchmark.status(false);
force_redraw();
}
}
};
int agg_main(int argc, char* argv[])
{
the_application app(pix_format, flip_y);
app.caption("AGG Example. Line Join");
if(app.init(600+100, 500+30, agg::window_resize))
{
return app.run();
}
return 1;
}