Files
2025-12-05 08:54:02 +01:00

1142 lines
33 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef tools_sg_plots
#define tools_sg_plots
#include "_switch"
#include "matrix"
#include "plotter"
namespace tools {
namespace sg {
class plots : public node {
TOOLS_NODE(plots,tools::sg::plots,node)
public:
sf<float> width;
sf<float> height;
sf<unsigned int> cols; //must never be 0.
sf<unsigned int> rows; //must never be 0.
sf<bool> view_border; //current plotter border
sf<float> plotter_scale; //scale factor applied to each plotter.
sf<bool> border_visible;
sf<float> border_width;
sf<float> border_height;
sf<float> border_z;
sf<float> border_scale;
sf_vec<colorf,float> border_color;
// for gopaw :
sf<float> left_margin;
sf<float> right_margin;
sf<float> top_margin;
sf<float> bottom_margin;
sf<float> horizontal_spacing;
sf<float> vertical_spacing;
public:
virtual const desc_fields& node_desc_fields() const {
TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::plots)
static const desc_fields s_v(parent::node_desc_fields(),18, //WARNING : take care of count.
TOOLS_ARG_FIELD_DESC(width), //1
TOOLS_ARG_FIELD_DESC(height),
TOOLS_ARG_FIELD_DESC(cols),
TOOLS_ARG_FIELD_DESC(rows),
TOOLS_ARG_FIELD_DESC(view_border),
TOOLS_ARG_FIELD_DESC(plotter_scale),
TOOLS_ARG_FIELD_DESC(border_visible),
TOOLS_ARG_FIELD_DESC(border_width),
TOOLS_ARG_FIELD_DESC(border_height),
TOOLS_ARG_FIELD_DESC(border_z), //10
TOOLS_ARG_FIELD_DESC(border_scale),
TOOLS_ARG_FIELD_DESC(border_color),
TOOLS_ARG_FIELD_DESC(left_margin),
TOOLS_ARG_FIELD_DESC(right_margin),
TOOLS_ARG_FIELD_DESC(top_margin),
TOOLS_ARG_FIELD_DESC(bottom_margin),
TOOLS_ARG_FIELD_DESC(horizontal_spacing),
TOOLS_ARG_FIELD_DESC(vertical_spacing)
);
return s_v;
}
virtual bool touched() {
if(parent::touched()) return true;
if(m_sep.empty()) return true;
if(m_extras.size()!=m_extras_sep.size()) return true;
return false;
}
private:
void add_fields(){
add_field(&width);
add_field(&height);
add_field(&cols);
add_field(&rows);
add_field(&view_border);
add_field(&plotter_scale);
add_field(&border_visible);
add_field(&border_width);
add_field(&border_height);
add_field(&border_z);
add_field(&border_scale);
add_field(&border_color);
add_field(&left_margin);
add_field(&right_margin);
add_field(&top_margin);
add_field(&bottom_margin);
add_field(&horizontal_spacing);
add_field(&vertical_spacing);
}
private:
static unsigned int MATRIX() {return 0;}
static unsigned int BORDER() {return 1;}
static unsigned int PLOTTER() {return 2;}
typedef std::vector<plottable*> ptbs_t;
typedef std::vector<node*> todels_t;
typedef std::vector<plotprim*> prims_t;
public:
virtual void render(render_action& a_action) {
update_if_touched();
m_group.render(a_action);
}
virtual void pick(pick_action& a_action) {
update_if_touched();
nodekit_pick(a_action,m_group,this);
}
virtual void search(search_action& a_action) {
update_if_touched();
parent::search(a_action);
if(a_action.done()) return;
if(a_action.do_path()) a_action.path_push(this);
m_group.search(a_action);
if(a_action.done()) return;
if(a_action.do_path()) a_action.path_pop();
}
virtual void bbox(bbox_action& a_action) {
update_if_touched();
m_group.bbox(a_action);
}
virtual void event(event_action& a_action) {
update_if_touched();
m_group.event(a_action);
if(a_action.done()) return;
}
virtual bool write(write_action& a_action) {
update_if_touched();
return m_group.write(a_action);
}
public:
plots(const base_freetype& a_ttf)
:parent()
,width(1)
,height(1)
,cols(1)
,rows(1)
,view_border(true)
,plotter_scale(1)
,border_visible(false)
,border_width(0)
,border_height(0)
,border_z(0)
,border_scale(1)
,border_color(colorf_grey())
,left_margin(0)
,right_margin(0)
,top_margin(0)
,bottom_margin(0)
,horizontal_spacing(0)
,vertical_spacing(0)
,m_ttf(a_ttf)
,m_current(0)
,m_extras()
,m_old_cols(0)
,m_old_rows(0)
{
add_fields();
init_sg();
}
virtual ~plots() {
}
public:
plots(const plots& a_from)
:parent(a_from)
,width(a_from.width)
,height(a_from.height)
,cols(a_from.cols)
,rows(a_from.rows)
,view_border(a_from.view_border)
,plotter_scale(a_from.plotter_scale)
,border_visible(a_from.border_visible)
,border_width(a_from.border_width)
,border_height(a_from.border_height)
,border_z(a_from.border_z)
,border_scale(a_from.border_scale)
,border_color(a_from.border_color)
,left_margin(a_from.left_margin)
,right_margin(a_from.right_margin)
,top_margin(a_from.top_margin)
,bottom_margin(a_from.bottom_margin)
,horizontal_spacing(a_from.horizontal_spacing)
,vertical_spacing(a_from.vertical_spacing)
,m_ttf(a_from.m_ttf)
,m_current(a_from.m_current)
,m_extras(a_from.m_extras)
,m_old_cols(0)
,m_old_rows(0)
,m_origins(a_from.m_origins)
,m_sizes(a_from.m_sizes)
,m_extras_origins(a_from.m_extras_origins)
,m_extras_sizes(a_from.m_extras_sizes)
{
add_fields();
init_sg();
if(!copy_plotters(a_from)) {}
}
plots& operator=(const plots& a_from){
parent::operator=(a_from);
if(&a_from==this) return *this;
width = a_from.width;
height = a_from.height;
cols = a_from.cols;
rows = a_from.rows;
view_border = a_from.view_border;
plotter_scale = a_from.plotter_scale;
border_visible = a_from.border_visible;
border_width = a_from.border_width;
border_height = a_from.border_height;
border_z = a_from.border_z;
border_scale = a_from.border_scale;
border_color = a_from.border_color;
left_margin = a_from.left_margin;
right_margin = a_from.right_margin;
top_margin = a_from.top_margin;
bottom_margin = a_from.bottom_margin;
horizontal_spacing = a_from.horizontal_spacing;
vertical_spacing = a_from.vertical_spacing;
m_old_cols = 0;
m_old_rows = 0;
m_origins = a_from.m_origins;
m_sizes = a_from.m_sizes;
m_extras_origins = a_from.m_extras_origins;
m_extras_sizes = a_from.m_extras_sizes;
m_current = a_from.m_current;
m_extras = a_from.m_extras;
if(!copy_plotters(a_from)) {}
return *this;
}
public:
unsigned int number() const {return cols*rows;}
unsigned int current_index() const {return m_current;}
void adjust_size(unsigned int a_ww,unsigned int a_wh) {
if(!a_ww||!a_wh) return;
float aspect = float(a_ww)/float(a_wh);
width = height * aspect;
}
const base_freetype& ttf() const {return m_ttf;}
public:
void clear() {
update_if_touched();
{size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->clear();
}}
tools_vforit(extra,m_extras,it) {
separator* sep = (*it).m_sep;
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->clear();
}
}
bool has_data() {
update_if_touched();
{size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
if(_plotter->plottables().size()) return true;
}}
tools_vforit(extra,m_extras,it) {
separator* sep = (*it).m_sep;
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
if(_plotter->plottables().size()) return true;
}
return false;
}
void touch_plotters() { //used in exlib/geant4/session.
update_if_touched();
{size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->touch();
}}
tools_vforit(extra,m_extras,it) {
separator* sep = (*it).m_sep;
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->touch();
}
}
void next() {
update_if_touched();
size_t _number = m_sep.size();
if(m_current>=(uint32(_number)-1)) {
m_current = 0;
} else {
m_current++;
}
update_current_border();
}
bool set_current_plotter(unsigned int a_index) {
update_if_touched();
if(a_index>=m_sep.size()) return false;
m_current = a_index;
update_current_border();
return true;
}
bool set_current(plotter* a_plotter) { //for popup.
update_if_touched();
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
if(_plotter==a_plotter) {
m_current = uint32(index);
update_current_border();
return true;
}
}
return false;
}
plotter& current_plotter() {
update_if_touched();
separator* sep = (separator*)m_sep[m_current];
return *((plotter*)(*sep)[PLOTTER()]);
}
plotter* find_plotter(unsigned int a_index) {
update_if_touched();
if(a_index>=m_sep.size()) return 0;
separator* sep = (separator*)m_sep[a_index];
return (plotter*)(*sep)[PLOTTER()];
}
void set_regions(unsigned int a_cols = 1,unsigned int a_rows = 1,bool a_transfer = false) {
unsigned int oldn = cols*rows;
std::vector<ptbs_t> ptbss;
std::vector<prims_t> primss;
std::vector<todels_t> tdlss;
std::vector<plotter> pls;
if(a_transfer) {
ptbss.resize(oldn);
primss.resize(oldn);
tdlss.resize(oldn);
pls.resize(oldn,plotter(m_ttf));
for(unsigned int index=0;index<oldn;index++) {
plotter* p = find_plotter(index);
p->transfer_plottables(ptbss[index]);
p->transfer_primitives(primss[index]);
p->transfer_todels(tdlss[index]);
pls[index] = *p; //to copy styles.
}
}
cols = a_cols?a_cols:1;
rows = a_rows?a_rows:1;
if(view_border.value()) {view_border = (number()==1?false:true);}
update_sg();
set_current_plotter(0);
clear();
if(a_transfer) {
//fill new plotters with old data :
unsigned int num = min_of(oldn,cols*rows);
for(unsigned int index=0;index<num;index++) {
plotter* p = find_plotter(index);
p->copy_style(pls[index]); //it must not touch p width, height, depth.
{const ptbs_t& ptbs = ptbss[index];
tools_vforcit(plottable*,ptbs,it) p->add_plottable(*it);}
{const prims_t& prims = primss[index];
tools_vforcit(plotprim*,prims,it) p->add_primitive(*it);}
{const todels_t& todels = tdlss[index];
tools_vforcit(node*,todels,it) p->add_node_todel(*it);}
}
}
reset_touched();
}
void current_to_one() {
ptbs_t ptbs;current_plotter().transfer_plottables(ptbs);
prims_t prims;current_plotter().transfer_primitives(prims);
todels_t tdls;current_plotter().transfer_todels(tdls);
plotter pl = current_plotter(); //to copy styles.
set_regions(1,1,false);
plotter& p = current_plotter();
p.copy_style(pl); //copy styles.
{tools_vforcit(plottable*,ptbs,it) p.add_plottable(*it);}
{tools_vforcit(plotprim*,prims,it) p.add_primitive(*it);}
{tools_vforcit(node*,tdls,it) p.add_node_todel(*it);}
}
void plotters(std::vector<plotter*>& a_vec) { //a_vec do NOT get ownership of sg::plotter objects. Use in G4Analysis.
a_vec.clear();
update_if_touched();
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
a_vec.push_back(_plotter);
}
}
//////////////////////////////////////////////////////////////////////
//// styling methods : ///////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void set_line_width(float a_line_width) {
update_if_touched();
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->bins_style(0).line_width = a_line_width;
_plotter->inner_frame_style().line_width = a_line_width;
_plotter->grid_style().line_width = a_line_width;
_plotter->x_axis().line_style().width = a_line_width;
_plotter->x_axis().ticks_style().width = a_line_width;
_plotter->y_axis().line_style().width = a_line_width;
_plotter->y_axis().ticks_style().width = a_line_width;
_plotter->z_axis().line_style().width = a_line_width;
_plotter->z_axis().ticks_style().width = a_line_width;
_plotter->colormap_axis().line_style().width = a_line_width;
_plotter->colormap_axis().ticks_style().width = a_line_width;
// needed if font is hershey :
_plotter->title_style().line_width = a_line_width;
_plotter->infos_style().line_width = a_line_width;
_plotter->title_box_style().line_width = a_line_width;
_plotter->x_axis().labels_style().line_width = a_line_width;
_plotter->x_axis().mag_style().line_width = a_line_width;
_plotter->x_axis().title_style().line_width = a_line_width;
_plotter->y_axis().labels_style().line_width = a_line_width;
_plotter->y_axis().mag_style().line_width = a_line_width;
_plotter->y_axis().title_style().line_width = a_line_width;
_plotter->z_axis().labels_style().line_width = a_line_width;
_plotter->z_axis().mag_style().line_width = a_line_width;
_plotter->z_axis().title_style().line_width = a_line_width;
_plotter->colormap_axis().labels_style().line_width = a_line_width;
_plotter->colormap_axis().mag_style().line_width = a_line_width;
_plotter->colormap_axis().title_style().line_width = a_line_width;
}
}
void set_grids_visibility(bool a_visible = false) {
update_if_touched();
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->grid_style().visible = a_visible;
}
border_visible = true;
}
void adjust_scales(float a_plotter_scale = 1) {
// Rescale some plotter parameters (for example margins) according to the number of plots.
// We assume that these parameters had been set previously according to one plot per page.
// Then this function must be applied after all the styles had been applied (because
// a plotting style may set these parameters).
float ww_wc = width.value();
float wh_wc = height.value();
float rw_wc = ww_wc/cols.value();
float rh_wc = wh_wc/rows.value();
float cooking = 1.2f; //if increased the data area is diminished.
float wfac = (rw_wc/ww_wc)*cooking;
float hfac = (rh_wc/wh_wc)*cooking;
float label_cooking = 1.6f; //if increased the labels are bigger.
if((cols.value()>=4)&&(cols.value()>rows.value())) label_cooking = 0.9f;
float title_cooking = 1.1f; //extra title cooking.
plotter_scale = a_plotter_scale;
update_if_touched();
{size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->left_margin = _plotter->left_margin * wfac;
_plotter->right_margin = _plotter->right_margin * wfac;
_plotter->bottom_margin = _plotter->bottom_margin * hfac;
_plotter->top_margin = _plotter->top_margin * hfac;
_plotter->x_axis().tick_length = _plotter->x_axis().tick_length * wfac;
_plotter->y_axis().tick_length = _plotter->y_axis().tick_length * hfac;
_plotter->title_to_axis = _plotter->title_to_axis * hfac;
_plotter->title_height = _plotter->title_height * hfac * title_cooking;
_plotter->x_axis().label_height = _plotter->x_axis().label_height * hfac * label_cooking;
_plotter->y_axis().label_height = _plotter->y_axis().label_height * hfac * label_cooking;
}}
}
//gopaw:
void configure_grid_PAW(unsigned int a_ww,unsigned int a_wh) {
m_origins.clear();
m_sizes.clear();
float wvp = float(a_ww); //pixels.
float hvp = float(a_wh); //pixels.
if( (wvp<=0)||(hvp<=0)||(width.value()<=0)||(height.value()<=0) ) return;
unsigned int _cols = cols.value();
unsigned int _rows = rows.value();
if((!_cols)||(!_rows)) return;
float wdata = (width.value()-left_margin.value()-right_margin.value()-(_cols-1)*horizontal_spacing.value())/_cols;
float hdata = (height.value()-bottom_margin.value()-top_margin.value()-(_rows-1)*vertical_spacing.value())/_rows;
if((wdata<=0)||(hdata<=0)) return;
unsigned int _num = number();
for(unsigned int iregion=0;iregion<_num;iregion++) {
//iregion = col + row * cols
unsigned int row = iregion/_cols;
unsigned int col = iregion - row * _cols;
float wr,hr,x,y,lm,rm,bm,tm;
wr = hr = x = y = 0;
lm = rm = bm = tm = 0;
if(_cols==1) {
wr = width.value();
x = 0;
lm = left_margin.value();
rm = right_margin.value();
} else {
float wrl = left_margin.value()+wdata+horizontal_spacing.value()/2;
float wrr = right_margin.value()+wdata+horizontal_spacing.value()/2;
float wri = wdata+horizontal_spacing.value();
if(col==0) {
wr = wrl;
x = 0;
lm = left_margin.value();
rm = horizontal_spacing.value()/2;
} else if(col==(_cols-1)) {
wr = wrr;
x = width.value() - wrr;
lm = horizontal_spacing.value()/2;
rm = right_margin.value();
} else {
wr = wri;
x = wrl + (col-1) * wri;
lm = horizontal_spacing.value()/2;
rm = horizontal_spacing.value()/2;
}
}
if(_rows==1) {
hr = height.value();
y = 0;
tm = top_margin.value();
bm = bottom_margin.value();
} else {
float hrt = top_margin.value()+hdata+vertical_spacing.value()/2; //top
float hrb = bottom_margin.value()+hdata+vertical_spacing.value()/2; //bottom
float hri = hdata+vertical_spacing.value();
if(row==0) { //top row.
hr = hrt;
y = height.value()-hrt;
tm = top_margin.value();
bm = vertical_spacing.value()/2;
} else if(row==(_rows-1)) {
hr = hrb;
y = 0;
tm = vertical_spacing.value()/2;
bm = bottom_margin.value();
} else {
hr = hri;
y = height.value()- (hrt + row * hri);
tm = vertical_spacing.value()/2;
bm = vertical_spacing.value()/2;
}
}
m_origins.push_back(vec2f(x,y));
m_sizes.push_back(vec2f(wr,hr));
separator* sep = (separator*)m_sep[iregion];
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->left_margin = lm;
_plotter->right_margin = rm;
_plotter->top_margin = tm;
_plotter->bottom_margin = bm;
}
}
void configure_extras_PAW(unsigned int a_ww,unsigned int a_wh) {
m_extras_origins.clear();
m_extras_sizes.clear();
float wvp = float(a_ww); //pixels.
float hvp = float(a_wh); //pixels.
if( (wvp<=0)||(hvp<=0)||(width.value()<=0)||(height.value()<=0) ) return;
tools_vforcit(extra,m_extras,it) {
const extra& _extra = *it;
unsigned int _cols = _extra.m_cols;
unsigned int _rows = _extra.m_rows;
if((!_cols)||(!_rows)) continue;
float wdata = (width.value()-left_margin.value()-right_margin.value()-(_cols-1)*horizontal_spacing.value())/_cols;
float hdata = (height.value()-bottom_margin.value()-top_margin.value()-(_rows-1)*vertical_spacing.value())/_rows;
if((wdata<=0)||(hdata<=0)) continue;
unsigned int iregion = _extra.m_index;
unsigned int row = iregion/_cols;
unsigned int col = iregion - row * _cols;
float wr,hr,x,y,lm,rm,bm,tm;
wr = hr = x = y = 0;
lm = rm = bm = tm = 0;
if(_cols==1) {
wr = width.value();
x = 0;
lm = left_margin.value();
rm = right_margin.value();
} else {
float wrl = left_margin.value()+wdata+horizontal_spacing.value()/2;
float wrr = right_margin.value()+wdata+horizontal_spacing.value()/2;
float wri = wdata+horizontal_spacing.value();
if(col==0) {
wr = wrl;
x = 0;
lm = left_margin.value();
rm = horizontal_spacing.value()/2;
} else if(col==(_cols-1)) {
wr = wrr;
x = width.value() - wrr;
lm = horizontal_spacing.value()/2;
rm = right_margin.value();
} else {
wr = wri;
x = wrl + (col-1) * wri;
lm = horizontal_spacing.value()/2;
rm = horizontal_spacing.value()/2;
}
}
if(_rows==1) {
hr = height.value();
y = 0;
tm = top_margin.value();
bm = bottom_margin.value();
} else {
float hrt = top_margin.value()+hdata+vertical_spacing.value()/2; //top
float hrb = bottom_margin.value()+hdata+vertical_spacing.value()/2; //bottom
float hri = hdata+vertical_spacing.value();
if(row==0) { //top row.
hr = hrt;
y = height.value()-hrt;
tm = top_margin.value();
bm = vertical_spacing.value()/2;
} else if(row==(_rows-1)) {
hr = hrb;
y = 0;
tm = vertical_spacing.value()/2;
bm = bottom_margin.value();
} else {
hr = hri;
y = height.value()- (hrt + row * hri);
tm = vertical_spacing.value()/2;
bm = vertical_spacing.value()/2;
}
}
m_extras_origins.push_back(vec2f(x,y));
m_extras_sizes.push_back(vec2f(wr,hr));
separator* sep = _extra.m_sep;
plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
_plotter->left_margin = lm;
_plotter->right_margin = rm;
_plotter->top_margin = tm;
_plotter->bottom_margin = bm;
} //tools_vforcit
}
void configure_PAW(unsigned int a_ww,unsigned int a_wh) {
configure_grid_PAW(a_ww,a_wh);
configure_extras_PAW(a_ww,a_wh);
touch();
update_if_touched();
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//gopaw:
void delete_extras() {
m_extras_sep.clear();
m_extras.clear();
}
plotter* create_extra_plotter(unsigned int a_cols,unsigned int a_rows,unsigned int a_index) {
// Create a plotter with size and position as if in a grid of a_colsxa_rows and at position a_index.
// The index numbering being for example for a grid of 3x2 :
// 0 1 2
// 3 4 5
m_extras.push_back(extra(a_cols,a_rows,a_index));
update_if_touched();
separator* sep = m_extras.back().m_sep;
return (plotter*)(*sep)[PLOTTER()];
}
plotter* last_extra_plotter() const {
if(m_extras.empty()) return 0;
separator* sep = m_extras.back().m_sep;
return (plotter*)(*sep)[PLOTTER()];
}
public:
void init_sg() { // used also in gopaw::base_viewer::clean_gstos().
m_group.clear();
m_sep.clear();
m_border_sep.clear();
m_extras_sep.clear();
m_group.add(new noderef(m_sep));
m_group.add(new noderef(m_border_sep));
m_group.add(new noderef(m_extras_sep));
}
void clear_sg() { //used in GL_plots_viewer.
m_group.clear();
m_sep.clear();
m_border_sep.clear();
m_extras_sep.clear();
}
protected:
void update_if_touched() {
if(touched()) {
update_sg();
reset_touched();
}
}
void update_sg(){
if(m_sep.empty()||(cols.value()!=m_old_cols)||(rows.value()!=m_old_rows)){
m_old_cols = cols;
m_old_rows = rows;
m_sep.clear();
for(unsigned int irow=0;irow<rows;irow++) {
for(unsigned int icol=0;icol<cols;icol++) {
separator* sep = new separator;
m_sep.add(sep);
sep->add(new sg::matrix); //MATRIX()
_switch* border = new _switch;
sep->add(border); //BORDER()
sep->add(new plotter(m_ttf)); //PLOTTER()
}
}
if(m_current>=m_sep.size()) m_current = 0;
}
update_current_border();
update_border();
if((width.value()>0)&&(height.value()>0)) {
size_t _number = m_sep.size();
bool configure = (m_origins.size()==_number)&&(m_sizes.size()==_number)?true:false;
// all window wc :
float ww_wc = width;
float wh_wc = height;
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
set_plotter_layout(*sep,index,configure,cols.value(),rows.value(),
ww_wc,wh_wc,m_origins,m_sizes,plotter_scale.value());
}
}
update_extras();
}
bool copy_plotters(const plots& a_from) {
update_if_touched();
if(m_sep.size()==a_from.m_sep.size()) {
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* _from_sep = (separator*)a_from.m_sep[index];
matrix* _from_matrix = (matrix*)(*_from_sep)[MATRIX()];
plotter* _from_plotter = (plotter*)(*_from_sep)[PLOTTER()];
separator* _sep = (separator*)m_sep[index];
matrix* _matrix = (matrix*)(*_sep)[MATRIX()];
plotter* _plotter = (plotter*)(*_sep)[PLOTTER()];
_matrix->operator=(*_from_matrix);
_plotter->operator=(*_from_plotter);
}
}
if(m_extras_sep.size()==a_from.m_extras_sep.size()) {
size_t _number = m_extras_sep.size();
for(size_t index=0;index<_number;index++) {
separator* _from_sep = (separator*)a_from.m_extras_sep[index];
matrix* _from_matrix = (matrix*)(*_from_sep)[MATRIX()];
plotter* _from_plotter = (plotter*)(*_from_sep)[PLOTTER()];
separator* _sep = (separator*)m_extras_sep[index];
matrix* _matrix = (matrix*)(*_sep)[MATRIX()];
plotter* _plotter = (plotter*)(*_sep)[PLOTTER()];
_matrix->operator=(*_from_matrix);
_plotter->operator=(*_from_plotter);
}
}
return true;
}
static void create_plotter_border(_switch& a_parent,float a_w,float a_h) {
a_parent.clear();
group* sep = new group;
a_parent.add(sep);
a_parent.add(new group()); //empty
rgba* mat = new rgba();
mat->color = colorf_red();
sep->add(mat);
draw_style* ds = new draw_style;
ds->style = draw_lines;
ds->line_width = 4;
sep->add(ds);
vertices* vtxs = new vertices;
vtxs->mode = gl::line_strip();
sep->add(vtxs);
float dw = a_w*0.5f;
float dh = a_h*0.5f;
vtxs->add(-dw,-dh,0);
vtxs->add( dw,-dh,0);
vtxs->add( dw, dh,0);
vtxs->add(-dw, dh,0);
vtxs->add(-dw,-dh,0);
}
void update_current_border() {
size_t _number = m_sep.size();
for(size_t index=0;index<_number;index++) {
separator* sep = (separator*)m_sep[index];
_switch* _border = (_switch*)(*sep)[BORDER()];
if(index==m_current) {
_border->which = view_border.value()?0:1;
} else {
_border->which = 1;
}
}
}
void update_border() {
m_border_sep.clear();
if(!border_visible.value()) return;
if(width.value()<=0) return;
if(height.value()<=0) return;
if(border_width.value()<=0) return;
if(border_height.value()<=0) return;
// border_scale could be used as an offscreen (inzb_[ps,png,jpeg], gl2s_pdf)
// cooking to avoid seeing a one pixel line at some side (in general left)
// coming from the border.
if(border_scale.value()!=1) {
matrix* _m = new matrix;
_m->set_scale(border_scale.value(),border_scale.value(),1);
m_border_sep.add(_m);
}
float bw = border_width;
float bh = border_height;
// do it with four externals back_area.
float zz = border_z.value();
// top :
{separator* sep = new separator;
m_border_sep.add(sep);
float wba = width+2*bw;
float hba = bh;
float x = 0;
float y = height*0.5f+bh*0.5f;
matrix* _m = new matrix;
_m->set_translate(x,y,zz);
sep->add(_m);
back_area* b = new back_area;
b->border_visible = false;
b->color = border_color;
b->width = wba;
b->height = hba;
sep->add(b);}
// bottom :
{separator* sep = new separator;
m_border_sep.add(sep);
float wba = width+2*bw;
float hba = bh;
float x = 0;
float y = -height*0.5f-bh*0.5f;
matrix* _m = new matrix;
_m->set_translate(x,y,zz);
sep->add(_m);
back_area* b = new back_area;
b->border_visible = false;
b->color = border_color;
b->width = wba;
b->height = hba;
sep->add(b);}
// left :
{separator* sep = new separator;
m_border_sep.add(sep);
float wba = bw;
float hba = height+2*bh;
float x = -width*0.5f-bw*0.5f;
float y = 0;
matrix* _m = new matrix;
_m->set_translate(x,y,zz);
sep->add(_m);
back_area* b = new back_area;
b->border_visible = false;
b->color = border_color;
b->width = wba;
b->height = hba;
sep->add(b);}
// right :
{separator* sep = new separator;
m_border_sep.add(sep);
float wba = bw;
float hba = height+2*bh;
float x = width*0.5f+bw*0.5f;
float y = 0;
matrix* _m = new matrix;
_m->set_translate(x,y,zz);
sep->add(_m);
back_area* b = new back_area;
b->border_visible = false;
b->color = border_color;
b->width = wba;
b->height = hba;
sep->add(b);}
}
protected:
class extra {
public:
extra(unsigned int a_cols,unsigned int a_rows,unsigned int a_index)
:m_cols(a_cols),m_rows(a_rows),m_index(a_index),m_sep(0){}
virtual ~extra(){}
public:
extra(const extra& a_from):m_cols(a_from.m_cols),m_rows(a_from.m_rows),m_index(a_from.m_index),m_sep(0){}
extra& operator=(const extra& a_from) {
m_cols = a_from.m_cols;
m_rows = a_from.m_rows;
m_index = a_from.m_index;
m_sep = a_from.m_sep;
return *this;
}
public:
unsigned int m_cols;
unsigned int m_rows;
unsigned int m_index;
separator* m_sep;
};
void update_extras() {
if(m_extras.size()!=m_extras_sep.size()) {
m_extras_sep.clear();
tools_vforit(extra,m_extras,it) { // same sg layout than grid plotters.
separator* sep = new separator;
m_extras_sep.add(sep);
(*it).m_sep = sep; //*it does not get ownership.
sep->add(new sg::matrix); //MATRIX()
_switch* border = new _switch;
sep->add(border); //BORDER()
sep->add(new plotter(m_ttf)); //PLOTTER()
}
}
if(width.value()<=0) return;
if(height.value()<=0) return;
// all window wc :
float ww_wc = width;
float wh_wc = height;
size_t _number = m_extras.size();
bool configure = (m_extras_origins.size()==_number)&&(m_extras_sizes.size()==_number)?true:false;
tools_vforcit(extra,m_extras,it) {
const extra& _extra = *it;
unsigned int index = _extra.m_index;
if(index>=(m_extras_sep.size())) index = 0;
separator* sep = _extra.m_sep;
set_plotter_layout(*sep,index,configure,_extra.m_cols,_extra.m_rows,
ww_wc,wh_wc,m_extras_origins,m_extras_sizes,plotter_scale.value());
}
}
protected:
static void set_plotter_layout(separator& a_sep,size_t a_index,bool a_configure,
unsigned int a_cols,unsigned int a_rows,
float a_ww_wc,float a_wh_wc,
const std::vector<vec2f>& a_origins,const std::vector<vec2f>& a_sizes,float a_scale) {
size_t row = a_index/a_cols;
size_t col = a_index-a_cols*row;
float rw_wc = a_ww_wc/a_cols;
float rh_wc = a_wh_wc/a_rows;
matrix* _matrix = (matrix*)(a_sep)[MATRIX()];
plotter* _plotter = (plotter*)(a_sep)[PLOTTER()];
if(a_configure) {
_plotter->width = a_sizes[a_index].x();
_plotter->height = a_sizes[a_index].y();
float x = -a_ww_wc*0.5f + a_origins[a_index].x() + _plotter->width*0.5f;
float y = -a_wh_wc*0.5f + a_origins[a_index].y() + _plotter->height*0.5f;
_matrix->set_translate(x,y,0);
} else {
float x = -a_ww_wc*0.5f + col * rw_wc + rw_wc * 0.5f;
float y = a_wh_wc*0.5f - row * rh_wc - rh_wc * 0.5f;
_matrix->set_translate(x,y,0);
}
_matrix->mul_scale(a_scale,a_scale,1); //applied first.
{_switch* _border = (_switch*)(a_sep)[BORDER()];
create_plotter_border(*_border,rw_wc,rh_wc);
_border->which = 1;
}
if(_plotter->shape.value()==plotter::xy) {
_plotter->depth = min_of(rw_wc,rh_wc);
} else {
_plotter->depth = rh_wc;
}
if(a_configure) {
} else {
if(_plotter->shape.value()==plotter::xy) {
_plotter->width = rw_wc;
_plotter->height = rh_wc;
} else {
if((rw_wc/rh_wc)>=1.0f) {
_plotter->width = rh_wc;
_plotter->height = rh_wc;
} else {
_plotter->width = rw_wc;
_plotter->height = rw_wc;
}
}
}
}
protected:
const base_freetype& m_ttf;
group m_group;
separator m_sep;
separator m_border_sep;
separator m_extras_sep;
unsigned int m_current;
std::vector<extra> m_extras;
//updater* m_updater;
unsigned int m_old_cols;
unsigned int m_old_rows;
std::vector<vec2f> m_origins;
std::vector<vec2f> m_sizes;
std::vector<vec2f> m_extras_origins;
std::vector<vec2f> m_extras_sizes;
};
}}
#endif