//? cpmgraph.h
//? C+- by Ulrich Mutze. Status of work 2022-05-06.
//? Copyright (c) 2022 Ulrich Mutze
//? contact: see contact-info at www.ulrichmutze.de
//?
//? This program is free software: you can redistribute it and/or
//? modify it under the terms of the GNU General Public License as
//? published by the Free Software Foundation, either version 3 of
//? the License, or (at your option) any later version.
//?
//? This program is distributed in the hope that it will be useful,
//? but WITHOUT ANY WARRANTY; without even the implied warranty of
//? MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//? GNU General Public License for
//? more details.
#ifndef CPMGRAPH_H_
#define CPMGRAPH_H_
/*
Purpose: Declaration of graphics class Graph
*/
#include
#include
#include
// Class Graph is derived from class Frame. An instance of class Graph
// represents a rectangular part of a two-dimensional object space
// which is mapped in a simple linear axis to axis manner onto a
// frame on screen. Positions in object space are referenced by
// instances of class R2, and the corresponding pixel position
// (instance of class Z2) is given via the member function
//
// Z2 toPixel(R2 const& p)
//
// See also class CpmGraphics::VecGraph for functions to draw line-graphics
// on instances of class Graph.
namespace CpmGraphics{
using CpmRoot::Z;
using CpmRoot::R;
using CpmRoot::C;
using CpmRoot::B;
using CpmLinAlg::Z2;
using CpmLinAlg::Z3;
using CpmLinAlg::R2;
using CpmLinAlg::R3;
using CpmArrays::R_Matrix;
using CpmArrays::R_Vector;
using CpmArrays::V;
using CpmArrays::X2;
using CpmArrays::X3;
using CpmFunctions::F;
using CpmFunctions::R_Func;
using CpmFunctions::R2_Func;
using CpmFunctions::RR;
using CpmImaging::Color;
using CpmImaging::BLACK;
using CpmImaging::WHITE;
using CpmImaging::RED;
using CpmImaging::GREEN;
using CpmImaging::BLUE;
using CpmImaging::LIGHTBLUE;
using CpmImaging::rainBow;
using CpmImaging::Response;
using CpmGeo::Iv;
using namespace CpmRoot;
enum SitRec{LOW,UPP,RIG,LEFT,CEN};
// denotes the sites of a rectangle (such as the screen
// window) lower (site), upper, right-hand, left-hand
// 2006-02-14 added CEN for center, which -although
// not being a site of the rectangle- turned out to be a
// useful parameter for methods dealing with rectangles.
///////////////////// struct Fig /////////////////////////////////////////
struct Fig{ // relevant for making Latex figures
// collects the
Word tit; // title of the figure
Word abs; // name of the quantity shown on x-axis
V ord; // name of the quantities shown on y-axis
};
//////////////////////// function gpScript ///////////////////////////////
void gpScript(Word const& fn, Word const& title,
Iv const& xRange, Iv const& yRange,
Word const& xLabel, Word const& yLabel,
Word const& output, Word const& datFile,
V const& leg,
V const& col = V{"black","green","red","cyan","magenta"},
Word const& hPos = "right", Word const& vPos = "top");
// Assumes that we have a textfile datFile containing data which
// should be graphically represented by entering 'gnuplot fn' to
// the command line (where fn is the first argument of the
// function). Here 'output' is the name of the pdf-file created
// via '> gnuplot fn'. The arguments title, xLabel, yLabel,
// xRange, yRange, leg (legend, one Word per curve),
// col (list of color names, can be any lenght >=1), hPos, vPos
// (position of legend) go into the precise form of output. If
// the y scale should be automatic, yRange should be given as
// Iv(a,b) with a == b.
// The default argument for col reflects my application to
// vizualizing Dirac wave functions in 1D.
/////////////////////////////// class Graph //////////////////////////////
class Graph: public Frame{// Frame as rectangular part of object space
// independent data:
Color gridColor;
Color gridTextColor;
Color graphColor;
Z withOrigin;
// withOrigin!=0 controls autoscaling such that the origin
// is always on the diagram
Z newGrid;
// only =0 or !=0 matters. If it is 0 (should be an exception) one
// let the grid remain unchanged and draw curves on the frame with
// a meaning of object space wich is no longer in agreement with
// the grid and the grid text.
B autoScale;
// Influences show(..) such that CL CU are adjusted to the data to
// be represented prior to drawing them.
// 'autoscaling'
// The change of CL and CU remains till new values are assigned
// by new action, or by asking for drawing of data which need
// changed values of CL and CU.
B autoScaleY;
// Influences show(..) such that CL CU are adjusted to the data to
// be represented prior to drawing them.
// 'autoscaling'
// The change of CL and CU remains till new values are assigned
// by new action or by asking for drawing of data which need
// changed values of CL and CU. The components CL.x1 and CU.x1
// which influence the size of object space in x-direction will
// be held fixed, however.
Z gridPrec{4};
// number of digits used to write limits for x and y in the
// legend of a grid
// B sclMem;
//: scale memory, used in function mark(V< V >,...)
// If this is false the following code comes in action when
// mark(V< V > const& dat,...) or mark(V const& dat,...) is
// called:
// if (!sclMem){
// sclMem=true;
// bool b=setXY(dat,1_R,secFac);
// if (b==false){
// cpmwarning(loc&": no graph could be created");
// CPM_MZ
// return false;
// }
// steps=adjXY();
// }
// This means that the window in object space (shown by *this) will be
// changed as to fit the input data 'dat'. In the following code 'dat'
// will be written to video memory takeing the changed object space
// window into account.
B writeOnShow;
// if this is true, calling any of the show-functions
// will create an image
B noGridText;
// If this is true, graphics supresses the textual
// indication of x-range and y-range
B noGrid;
// If this is true, graphics supresses the grid
V gridText;
// since 2005-04-14, we have V instead of Word to allow
// multi-line grid text
R2 gridTextPosition;
// text position is understood in the Frame coordinate system
// (xF,yF) i.e. (0,0) is left lower corner, (1,1) is right upper
// corner
R gridTextHeight_{0.};
// height of grid text in appr. pixel
R2 CL; // lower left corner of the rectangular domain in
// object space
R2 CU; // upper right corner of the rectangular domain in
// object space
// The following four quantities are simple derivates from
// the former primary geometric data. They are chosen to be data
// members since they are needed in the implementation of
// member functions in many places. Thus they had to be calculated
// many times for a single graph.
R dX; // x-range in object space dX=CU.x1-CL.x1
R dY; // y-range in object space dY=CU.x2-CL.x2
R dXInv; // inverse of dX
R dYInv; // inverse of dY
Z nPoints{0}; // in situations where the graphical content is an object
// which is charcterized by a number of points, we set nPoints
// equal to this number
// methods
void iniGraph(R Xl, R Yl, R dX, R dY);
bool updatedXdY(void);
// updates dX, dY, dXInv, dYInv if CL or/and CU have been changed.
// Returns false if dX or dY would be <=0
public:
typedef Graph Type;
CPM_IO
// It is not the intention of this I/O operations
// to save the content of video memory, and other content data
// constructors and assignement
Graph(R xl, R yl, R dx, R dy, const Color& color=defaultColorActive,
R Xl=0_R, R Yl=0_R, R dX=1_R, R dY=1_R);
Graph(void);
explicit Graph(const Color& c);
// is Graph() except of Frame::colorActive=c
Graph(Frame const& f);
Graph(Frame const& fr, R rimRel);
// *this will be placed within fr so that a rim of the same
// width in x- and y-direction will arise. In x-direction,
// this width is a fraction rimRel of frame width.
Word nameOf()const{ return Word("Graph");}
// setting functions
void setPoints(Z np){ nPoints=np;}
void setWriteOnShow(bool wos){ writeOnShow=B(wos);}
void setWrtOnShow(bool wos){ writeOnShow=B(wos);}
//: set write on show
// correct C+- name
void setNoGridText(bool ngt){ noGridText=B(ngt);}
void setNoGrid(bool ng){ noGrid=B(ng);}
void setGridColor(const Color& c);
void setGridTextColor(const Color& c);
void setGraphColor(const Color& c);
void setGridPrec(Z prec){ gridPrec=prec;}
void setWithOrigin(Z);
// setWithOrigin(1) controls autoscaling such that the origin
// is always within the diagram
void setNewGrid(Z);
void setAutoScale(bool ac){
autoScale=B(ac);
// if (ac) autoScaleY=false;
}
// enable auto scaling
void setAutoScaleY(bool ac){ autoScaleY=B(ac);}
// enable auto scaling in y-direction, holding the size of object space
// in x-direction fixed.
// void setSclMem_(bool sm){ sclMem=B(sm);}
// enable auto scaling
void setGridText(Word a);
void setGridTextPosition(const R2& pos);
void setGridTextPosition(R xText, R yText);
// 0<=xText<=1, 0<=yText<=1, sets position of left-lower corner
// of grid text window within the frame of the Graph
R2 getGridTextPos()const{ return gridTextPosition;}
//: get grid text position
void setGridTextPos_(R2 const& pos){setGridTextPosition(pos);}
//: set grid text position
R getGridTextHeg()const{ return gridTextHeight_;}
//: get grid text height
void setGridTextHeg_(R h){ gridTextHeight_=h;}
//: set grid text height
bool setXY(R X, R Y, R dX1, R dY1);
// sets the corners in object space
// modern style making use of class Iv (interval)
bool setX(Iv iv, R fac=1.025_R);
// sets the corners in object space X direction according to the
// interval iv
bool setX_(Iv iv, R fac=1.025_R){ return setX(iv,fac);}
// better named version
bool setY(Iv iv, R fac=1.025_R);
// sets the corners in object space Y direction according to the
// interval iv. Also sets autoScale to 0. The argument fac blows the
// object space window a bit so that iv fits in with some free space
// around it.
bool setY_(Iv iv, R fac=1.025_R){ return setY(iv,fac);}
// better named version
bool setXY(Iv const& ivX, Iv const& ivY, R fac=1.025_R);
// sets the corners in object space according to the
// intervals ivX, ivY. Also sets autoScale to 0.
bool setXY_(Iv ivX, Iv ivY, R fac=1.025_R){ return setXY(ivX,ivY,fac);}
// better named version
// some getting functions
Color getGridColor()const{ return gridColor;}
Color getGridTextColor()const{ return gridTextColor;}
Color getGraphColor()const{ return graphColor;}
bool getWriteOnShow()const{ return writeOnShow;}
bool getWrtOnShow()const{ return writeOnShow;}
Iv getIvX()const{ return Iv(CL.x1,CU.x1,true);}
Iv getIvY()const{ return Iv(CL.x2,CU.x2,true);}
R2 cen()const{ return (CL+CU)*0.5;}
//: center
R2 lowLeft()const{ return CL;}
//: lower left (corner)
R2 uppRight()const{ return CU;}
//: upper right (corner)
R rad()const{ return (CU-cen()).abs();}
//: radius
R dx()const{ return dX*nColInv;}
// progess in object space x-direction from a pixel to the
// next
R dy()const{ return dY*nRowInv;}
// progess in object space y-direction from a pixel to the
// next
R dyDdx()const{ return dY*dXInv*nRowInv*nCol;}
// dy divided by dx
R dxDdy()const{ return dX*dYInv*nColInv*nRow;}
// dx divided by dy
V centersX()const{ return getIvX().centers(nCol);}
V centersY()const{ return getIvY().centers(nRow);}
// transformation between coordinates (xF,yF) in frame system and
// (xG,yG) in object space of the graph
R2 get_xFyF(const R2& xGyG)const;
R2 get_xGyG(const R2& xFyF)const;
// my old graphics functions with new interface, i.e as member functions
// of class Graph
Z to_i(R x, Z& excess)const; // to i
// returns the column-number (\in {0,...,nCol-1} ) to which a point p
// belongs, that is known to have a x-coordinate in the object space
// of Graph *this given by number x. Column numbers increase from left to
// right. After call excess will have the value 0 if x was within the
// object space range, +1 if x was too large, and -1 if it was too low.
Z to_j(R y, Z& excess)const; // to j
// returns the row-number (\in {0,...,nRow-1} ) to which a point p
// belongs, that is known to have a y-coordinate in the object space
// of Graph *this given by number y. Row numbers increase from
// above to below. Thus the output of this and the previous function
// give directly the pixel location in img that corresponds to
// p(x,y). Thus a call img.putPel(i(p[1]),j(p[2]) marks a point p
// characterized by object space coordinates. After call excess will have
// the value 0 if x was within the object space range, +1 if x was too
// large, and -1 if it was too low.
R to_i_(R x)const{ return (x-CL.x1)*dXInv*nCol;}
// new fast version to to_i taking into account that now pixel access
// is by R
R to_j_(R y)const{ return (CU.x2-y)*dYInv*nRow;}
// new fast version of to_j
Z iX(R x)const;
// x<=XL corresponds to left boundery of the Frame
// x>=XU corr. to right bound. of Frame
Z jY(R y)const;
// y<=YL corresponds to the lower boundery of the Frame
// y>=YU corresponds to the upper boundery of the Frame
R iX_(R x)const;
// x<=XL corresponds to left boundery of the Frame
// x>=XU corr. to right bound. of Frame
R jY_(R y)const;
// y<=YL corresponds to the lower boundery of the Frame
// y>=YU corresponds to the upper boundery of the Frame
R2 natTrn(R2 const& p)
//: natural transformation
// See implementation of fillTriangle for the typical usage
{ return R2(iX_(p.x1),jY_(p.x2));}
Z iX0(R x)const;
// x=XL corresponds to left boundery of the Frame
// x=XU corr. to right bound. of Frame
// for xXU we return -1
Z jY0(R y)const;
// y=YL corresponds to the lower boundery of the Frame
// y=YU corresponds to the upper boundery of the Frame
// for yYU we return -1
Z2 toPixel(R2 const& p)const;
// maps a point from object space into a pixel (discrete position on
// screen)
R2 toObjectSpace(const Z2& z )const;
// maps a discrete pixel position into object space. More precisely it is
// the center of the pixel which is taken.
// access fuctions to corner coordinates in object space
R getxLeft(void)const { return CL.x1;}
R getxRight(void)const{ return CU.x1;}
R getyLower(void)const{ return CL.x2;}
R getyUpper(void)const{ return CU.x2;}
R get_dX(void)const{ return dX;}
R get_dY(void)const{ return dY;}
R getX(void)const{ return CL.x1;}
R getY(void)const{ return CL.x2;}
R getdX(void)const{ return CU.x1-CL.x1;}
// same as get_dX
R getdY(void)const{ return CU.x2-CL.x2;}
// same as get_dY
R diameter()const{ return R2(dX,dY).abs();}
//:: diameter
Iv ranX(void)const{ return Iv(CL.x1,CU.x1);}
// range X
Iv ranY(void)const{ return Iv(CL.x2,CU.x2);}
// range Y
// C+- names for manipulating the autoScale parameter.
// Underscore for mutating functions.
void setAutoScl_(bool b)
//: set auto scale
{
autoScale=B(b);
if (b) autoScaleY=false;
}
void setAutoSclY_(bool b){ autoScaleY=B(b);}
//: set auto scale in y-direction of object space
bool getAutoScl()const{ return autoScale;}
//: get auto scale
bool getAutoSclY()const{ return autoScaleY;}
//: get auto scale in y-direction of object space
// changing the size in object space of the window
bool scale(R magnification);
// argument >1 means that the rectangle in object space gets enlarged
// by a factor 'magnification' in linear dimensions. The center of the
// window remains fixed in object space. The display size is not
// affected.
// If the argument is < 1, the rectangle in object space gets reduced.
bool scaleY(R mag);
// just as function scale, but acts only on the Y-range
void shift(R sx, R sy);
// shifts along X-axis and Y-axis in object space. The display size
// is not affected. sx and sy are to be understood as fractions of dX
// and dY. Thus g.shift(0.5,0.5) will always make the old center the
// new left-lower corner.
void shiftAbs(R sx, R sy);
// shifts along X-axis and Y-axis in object space. The display size
// is not affected. sx and sy are to be understood as given relative
// to units in object space.
void getObjectSpaceFrom(const Graph& gr );
// takes the object space related data CL, CU, dX, dY from gr and
// changes the corresponding data of *this as to become identical to
// them.
// Setting the window according to the size of objects to be
// represented graphically. Overloading with differently
// typed arguments may be added later according to need
bool setXY(R_Vector const& x, R_Matrix const& y);
/*
(x,y) represents a family of curves. x contains the x-values
and y[i][j] are the y-values belonging to x=x[i].
The values of CL, CU such that these curves
fill the window a bit less than exactly
*/
bool setXY(const R_Vector& x, const R_Vector& y, Z method=0);
// method 0 as above, where y is treated as a 1-matrix
// method 1: the first point is taken as the origin and
// scale on x- and y-axis are the same. This was first
// needed 2005-03-31 for a representation of Brownian motion.
bool setXY(const R_Matrix& y);
// y[1] is the discrete x-axis, y[2], ... y[y.dim()] gives the
// y values for y.dim()-1 curves.
bool setXY(V const& pos, R secFac=1.1, Z met=0);
// For secFac=1 the function adjusts the frame just as to frame
// the set of points given pos. A larger secFac allows for a
// boundary arround the points, a smaller one yields a subframe.
// The new method parameter met allows for met=1 to set for
// both directions the same range. This is done in a minimal way
// under this restriction. This does not change the functionality
// controlled by secFac.
bool setXY(V< V > const& pos, R secFacX=1.01, R secFacY=1.01);
// as previous function but here a set of points is given
// by a more complex object pos (typically a list of
// discretized curves). A different security factor may be set
// for the x-range and the y-range.
bool setXY(V const& pos, R secFac=1.1);
// same behavior as previous function
bool setXY2(V const& pos, R secFac=1.1)
// same behavior as previous function
{ return setXY(pos(F(Frame::cToR2)),secFac);}
Z2 adjXY(Z divX=10, Z divY=10);
// Enlarges the window such that the boundaries correspond to values
// which in scientific number format give short and convenient
// numbers. Also a grid is drawn which subdivides the window
// conveniently into approximately divX subintervals in the x-axis
// and divY subintervals in the y-axis. The actual grid may deviate
// from these numbers since only convenient values for the grid line
// coordinates are accepted (for instance, the unit interval will
// be devided into 2 or 4 subintervals but never in 3).
// It returns nx and ny the division numbers of a grid.
void adjustScaleY(void);
// The origin CL and the X-interval [CL.x1,CU.x1] are left unchanged
// and CU.x2 is adjusted such that a circle in object space (actually,
// object plane) will be mapped into a circle (as opposed to an
// ellipse) on screen. The essential part of this operation is to
// set an appropriate scale in Y direction, which explains the name
// of the function.
void adjustScaleX(void);
// The origin CL and the Y-interval [CL.x2,CU.x2] are left unchanged
// and CU.x1 is adjusted such that a circle in object space (actually,
// object plane) will be mapped into a circle (as opposed to an
// ellipse) on screen. The essential part of this operation is to
// set an appropriate scale in X direction, which explains the name
// of the function.
Z adjust(R2& x)const;
// If x is very near to one of the 4 infinite lines defining the window,
// x is shifted to sit exactly on this line. This is sometimes necessary
// for making decisions like in function boundary meaningful. The return
// value counts the number of adjustments done. The return value ranges
// from 0 (no adjustment) to 2. (XU>XL,YU>YL assumed.)
Z boundary(const R2& p)const;
// Consider the rectangle with corners (Xi,Yj), i=1,2, j=1,2.
// This function determines the position of the point P=(x,y) relative
// to this rectangle. Return value 0 says that P belongs to the
// interior. Return value 5 says exterior. If (x,y) belongs to that
// boundary the number of the boundary segment is returned. (The 4 parts
// are numbered counter-clockwise starting from (XL,YL), they are
// disjoint as sets, so that P can belong at most to one of them.
Z same_boundary(R2 p, R2 q);
// gives 1 if the points p and q both belong to the boundary and their
// connecting line also belongs to the boundary. Zero is returned if any
// of the points doesn't belong to the boundary. And -1 is returned if
// the connection enters the interiour of the rectangle.
Z next_corner(R2 b, R2& c);
// Consider the rectangle with corners (Xi,Yj), i=1,2, j=1,2.
// We look to the boundary as a oriented curve starting at (XL,YL)
// and running counter-clock-wise. Let b be a point of this curve
// (hence a point of the boundary of the rectangle). c is the first
// corner encountered when starting at b and following the orientation
// given above.
// The return value is the number of the corner in the same order.
// If b doesn't belong to the boundary, the return value is 0
// The next point of a corner has to be another corner.
Z line_bound_inters(R a, R b, R c, Rn2& x);
// line_bound_inters stands for line-boundary-intersection.
// The line under consideration is given by the implicit equation
// ax+by+c=0.
// The return value is the number of intersection points of this line
// and the boundary of the rectangle considered in function boundary.
// (x[k][0],x[k][1]) is the kth intersection point. (Enumeration
// starting with 0.
Z connect_on_boundary(R2 xi, R2 xf, Rn2& xc);
// Let k be the return value. Then the points xc[0],...xc[k],
// form a connection from xi to xf along the boundary of the rectangle
// considered in function boundary. The orientation is always
// counter-clock wise for having a simple logic, as opposed to having
// always the shortest way. xc[0]=xi, xc[k]=xf. The chain may have up
// to 6 points!
R goto_boundary(R2 x, R2 e, R2& y);
// if x is any point and e any direction (not necessarily a unit vector).
// xa is the adjusted x (it is shifted to the boundary if it is very
// close to it, see function "adjust".
// Then y is the nearest point of the form xa+t*e, t>0, which belongs to
// the boundary if there is such a point. In this case t_min is returned.
// (Note that only t's larger than zero are considered, so we do not
// return zero if xa is on the boundary.)
// If no such point exists we return t=IMMENSE and put y=x. (This
// occurrs for instance if e is the zero vector). So the meaning is:
// goto boundary in e-direction if you can. If you can't, stay where you
// are and state that the distance is IMMENSE.
bool indFunc(const R2& p)const;
// returns true if p belongs to the object space rectangle associated
// with *this and false else.
void mark(R2 const& p, Color const& color)const;
// Sets a marked pixel of a color given by the second
// argument at screen point which corresponds to point p in object
// space.
void mark(C const& p, Color const& color)const
{ mark(R2(p),color);}
void mark(R2 const& p, Z size, Color const& color)const;
// Sets a mark 'of size s' of a color given by the second
// argument at screen point which corresponds to point p in object
// space.
// 'Size', presently, is the number of pixels which get colored.
// The shape of the mark follows a simple and effective rule but
// is not optimum with respect to symmetry to the the marked
// position. All sizes larger than 17 are marked identically.
void mark(const C& p, Z size, const Color& color)const
{ mark(R2(p),size,color);}
void draw(R2 const& p1, R2 const& p2, Color const& color);
// draws a line from p1 to p2 (points included),
// properly clipped to the frame. After calling setThcLine_(true), a
// member function of Frame, a thick line is drawn.
void draw(C const& p1, C const& p2, Color const& color)
{ draw(R2(p1), R2(p2), color);}
void fillDisk(R2 const& pc, R r, Color const& color);
//: fill disk
// Draws a circle to the graphics window filled by color.
// Clearly, the value of r gives the radius in units of pixels.
// Length in object space can't be used due to probably different scales
// in x-direction and y-direction. The quality of small disks, say
// r <= 4, is quite bad.
void fillCircle(R2 const& center, R radius, const Color& color);
//:: fill circle
// Draws a circle to the graphics window filled by color.
// Clearly, the value of radius refers to *this' object
// space. 2019-09-09: If x and y-directionn's don't have the same
// unit length (i.e if dx() != dy() ) we don't get a circle on screen.
// This function thus is obsolete. Use fillDisk instead.
void fillCircle(C const& center, R radius, const Color& color)
{ fillCircle(R2(center),radius,color);}
void fillTriangle(R2 const& p1, R2 const& p2, R2 const& p3,
Color const& color);
// analogous to as fillCircle
void fillRectangle(R2 const& p1, R2 const& p2,
Color const& color);
void fillEllipse(R2 const& p1, R2 const& p2,
Color const& color);
Z polygonClip( const Rn2& x, Rn2& y);
// x is an array of points (in a plane). The polygon determined
// by them is restricted ("clipped") to the window in such
// a way that it remains a polygon and any point where the original
// polygon leaves the window is connected on the boundary with the point
// of reentrance. The return value is the number of points of the clipped
// polygon. After function call, the points of the clipped polygon are
// held in the array y. The value of y before function call has no
// influence. The return value is the dimension of y (after function
// call, of course).
Z bundleClip(const R_Vector& a, const R_Vector& b,
const R_Vector& c, Rn2& y);
/*
Consider a set of n straight lines. The ith line is described by the
equation
a[i]*x+b[i]*y+c[i]=0.
These lines are clipped to the window and the end points of the line
segments are connected on boundary such that a connected polygon arises.
The return value is the number of points of this polygon.
The points of the polygon are held in the array y[]. It is tried to
arrange the connection on boundary such that for a continuous bundle
(lines not too much changing from i to i+1) a minimum number of points
is created.
*/
void show( const R_Vector& a,const R_Vector& b, const R_Vector& c,
const Color& color);
/*
Here, n,a,b,c have the same meaning as in function bundle_clip.
The restriction of the lines to the window is drawn to screen without
creating a connected polygon (which would be suited for printing).
*/
void showConnected( const R_Vector& a,
const R_Vector& b, const R_Vector& c, const Color& color);
/* Let n,a,b,c be as in bundle_clip. The polygon created by this function
is represented drawn on screen. Having show_bundle_simple, this is
usefull only as a test for the function bundle_clip, the latter being
important for printing results.
*/
void gridL(Z n_x=4, Z n_y=4);
// Draws a grid by dividing x-range in n_x equally sized parts, and
// y-range into n_y such parts. No text indicating the x,y ranges.
void clearAndGrid(Z nX=10_Z, Z nY=10_Z);
// Fills the screen window with Color colorActive
// draws a grid (only if newGrid!=0 what is always the
// case if not changed intentionally).
// Enlarges the object space rectangle such that the
// boundaries correspond to values which in scientific
// number format give short and convenient numbers.
// A grid is drawn which subdivides the window conveniently
// into approximately divX subintervals in the x-axis
// and divY subintervals in the y-axis. The actual grid may
// deviate from these numbers since only convenient values
// for the grid line coordinates are accepted (for instance,
// the unit interval will be divided into 2 or 4 subintervals
// but never in 3). Stores the grid legend in the variable text_.
void draw(R_Vector const& x, R_Vector const& y, const Color& c);
// Draws the polygon (x[1],y[1]),...(x[n],y[n]), properly clipped,
// to the presently valid window (in color c)
bool markCf(R_Vector const& x, R_Matrix const& y, Iv const& ivX,
CpmArrays::V const& cl=rainBow,
Word const& title=Word(), R xRelTitle=0.05,
bool xLog=false, bool yLog=false);
bool mark(const R_Vector& x, const R_Matrix& y,
const CpmArrays::V& cl=rainBow,
const Word& title=Word(), R xRelTitle=0.5,
bool xLog=false, bool yLog=false);
// as following function but needs call of vis() for display
bool show(const R_Vector& x, const R_Matrix& y,
const CpmArrays::V& cl=rainBow,
const Word& title=Word(), R xRelTitle=0.5,
bool xLog=false, bool yLog=false)
/*
Graphical representation of a family of curves:
The m lists of n object space points
(x[1],y[1][1]),......,(x[n],y[1][n])
.................................
(x[1],y[m][1]),....,(x[n],y[m][n])
define m polygons with n points each.
Depending on autoScale==0 or autoScale!=0
the object-space corners CL, CU of *this will be adjusted or not adjusted
according to the minimum window in object space containing all these
points.
Then the polygons will be drawn to the screen-window determined
by *this according to the then valid object space window (thus the
polygons may leave the window, or even not being there at all, if
autoScale is zero, and will not leave the window otherwise).
The m curves are drawn in colors defined in the array cl
If there are more curves than entries in colorList the sequence of colors
starts gets repeated as often as needed. The colors colorActive and
gridColor, even if present in cl will not be used for curve drawing.
Whether the grid text is updated depends on the value of newGrid (!=0 for
proper updating - which is the normal case). The updated gridtext gets
written after the curves, so that the grid text may not be covered by
the curves, it nevertheless may be difficult to read.
A typical code segment for flexible display of many curve families on
screen is the following
Frame f;
N ni=2;
N nj=3;
Frames fs(f,ni,nj);
V g(ni*nj);
// a list of graphs corresponding to a ni*nj matrix of
// subframes of f
// one also could use directly a ni*nj matrix of Graphs:
// VV g(ni,nj)
N k=0;
R2 textPos(0.1,0.1);
// standard text position might conflict with the curves
// which then might become hidden
for (i=1;i<=ni;i++) for (j=1;j<=nj;j++) {
k++;
g[k]=fs[i][j];
(g[k]).setAutoScale(0);
(g[k]).setXY(4.3,0,1.6,2.4);
// given a fixed coordinatesystem to make the curves in
// different
// Graphs comparable
(g[k]).setNewGrid(1);
(g[k]).setGridTextPosition(textPos);
(g[k]).adjustXY(10,10);
// making an appropriate grid
g[k].show(.....)
}
The argument xRelTitle gives the relative x-coordinate of the
beginning of the title text.
*/
{
bool b=mark(x,y,cl,title,xRelTitle,xLog,yLog);
vis(writeOnShow);
return b;
}
void mark1(R_Matrix const& a, CpmArrays::V const& cl=rainBow,
Word const& title=Word(), R xRelTitle=0.5);
// This is the version of mark(R_Vector const& x, R_Matrix const& y,...)
// in which x is taken as the first line as part of the matrix.
// Unfortunately another function with R_Matrix const& as the only
// mandatory argument already exists, so that the present function
// has to get a name different from mark(...). Notice that so far the
// classes R_Vector and R_Matrix do not know the graphical system
// declared with cpmframe.h, cpmgraph.h and thus can't define their
// graphical representations as member functions.
bool markTr(R_Vector x, R_Matrix const& y,
CpmArrays::V const& cl=rainBow);
// Tr: transparent
// no painting of background. So, curves can be overlayed to a color
// 'image' such as a 2D wave function.
void show(R_Matrix const& a, CpmArrays::V const& cl=rainBow,
Word const& title=Word(), R xRelTitle=0.5)
/*
Similar to the last function; the first line of matrix a is
interpreted as x values. The further lines are interpreted
as corresponding y-values of a family of curves
*/
{
mark1(a,cl,title,xRelTitle);
vis(writeOnShow);
return;
}
bool markGL(const R_Vector& x, const R_Matrix& y, V const& cl,
bool xLog=false, bool yLog=false);
// as following function but needs call of vis() for display
bool showGL(const R_Vector& x, const R_Matrix& y, V const& cl,
bool xLog=false, bool yLog=false)
{
bool b=markGL(x,y,cl,xLog,yLog);
vis(writeOnShow);
return b;
}
bool markGL(R_Vector const& x, R_Vector const& y,
Color const& c, Z bars);
bool showGL(R_Vector const& x, R_Vector const& y,
Color const& c, Z bars)
{
bool b=markGL(x,y,c,bars);
vis(writeOnShow);
return b;
}
/////////////// particularly useful mark function /////////////////////////
bool mark(R_Vector const& x, R_Vector const& y,
Color const& color=BLACK, Z bars=0,
Word const& title=Word(), R xRelTitle=0.5);
// Writes the pixel data to frame store. In all other respects like the
// next function.
bool show(const R_Vector& x, const R_Vector& y,
const Color& color=BLACK, Z bars=0,
const Word& title=Word(), R xRelTitle=0.5)
// Similar to case that y is a matrix. Only one polygon is shown and the
// color of the latter is given by 'color'.
// If bars is different from 0, a bar diagram is made instead of a curve
// Bar representation sometimes is usefull to strictly indicate the
// distinct values since we have no 'points and lines style'
{
bool b=mark(x,y,color,bars,title,xRelTitle);
vis(writeOnShow);
return b;
}
bool makeLaTexGraph(R_Matrix const& a, Fig const& fg,
ostream& str, Iv rx=Iv(0,140), Iv ry=Iv(0,100));
// Writes on a text stream the LaTex commands that let
// LaTex create a figure similiar to the representation of a.
// Additional input that makes sense for a printed figure and
// not for a screen graphic is provided by the argument fg.
bool mark(const R_Vector& y, const Color& color=BLACK, Z bars=0,
const Word& title=Word(), R xRelTitle=0.5);
// Writes the pixel data to frame store. In all other respects like the
// next function.
bool show(const R_Vector& y, const Color& color=BLACK, Z bars=0,
const Word& title=Word(), R xRelTitle=0.5)
// The vector of x-values is {1,2,... dim(y)} and the y-values are given
// by y. In all other respects like the second last function.
{
bool b=mark(y,color,bars,title,xRelTitle);
vis(writeOnShow);
return b;
}
bool show(F const& f, Z n=400, Color const& cf=RED,
V const& xList=V(0), Color const& cx=LIGHTBLUE);
// The n points (x,f(x)), with x ranging equidistantly from XL to XU,
// are connected to form a polygon, and the this polygon is shown in a
// autoscaled window and grid. Window data are written to screen.
// The polygon is drawn in a color described by cf=1,...15.
// For negative cf, see auto_show_list xList is a list of x-Values that
// will be marked on the x-Axis by vertical lines as far as they are in
// the x-Range determined by *this. The color of these lines is set as cx.
// Returns false if no graph could be created. This function is used in:
// R_Func f; Graph gr; gr.show(f).
bool mark(F const& f, Z n=400, Color const& cf=RED);
template
void show(V< FRR > const& f, Z n=100,
CpmArrays::V const& cl=rainBow, bool logarithmic=false)
// Generalizing previous function for an array of curves. For more
// flexibility, F is replaced by a template parameter.
{
using CpmGeo::Iv; // interval
if (n<=1) n=2;
Z d=f.dim();
Iv iv(CL[1],CU[1]);
R_Vector x(iv,n);
R_Matrix y(d,n);
for (Z i=1;i<=d;i++){
for (Z j=1;j<=n;j++){
FRR fi=f[i];
y[i][j]=fi(x[j]);
}
}
show(x,y,cl,"",0.5,false,logarithmic);
}
void show(const R2_Func& fR, const R2_Func& fG, const R2_Func& fB );
// Three functions, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals. For this purpose,
// function values are converted to Au by the automatics chosen by the
// compiler.
void show(const R2_Func& fR, const R2_Func& fG, const R2_Func& fB,
R XL, R XU, R YL, R YU, bool noDisplay=false);
// Three functions, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals. For this purpose,
// function values are converted to Au by the automatics chosen by the
// compiler. Only the part of the f's living on the rectangle XL<=X<=XU,
// YL<=Y<=YU are actually 'painted'. This allows to update small subareas
// of an image much more quickly than redrawing of the whole image would
// allow. Unfortunately the C++ syntax doesn't allow to put the natural
// default values XL=CL.x,... YU=CU.y.
void show(const R2_Func& fR, const R2_Func& fG, const R2_Func& fB,
const Response& rR, const Response& rG, const Response& rB,
Iv roiX, Iv roiY, bool noDisplay=false);
// Three functions, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals. For this purpose,
// function values are converted to Au by the action of the three
// instances of class Response.
// Only the part of the f's living on the rectangle XL<=X<=XU,
// YL<=Y<=YU are actually 'painted'. This allows to update small subareas
// of an image much more quickly than redrawing of the whole image would
// allow. Unfortunately the C++ syntax doesn't allow to put the natural
// default values XL=CL.x,... YU=CU.y.
void show(R2_Func const& f, Response const& rsp, Iv roiX=Iv(),
Iv roiY=Iv());
// Functions f, defined on the object space of the Graph is
// represented as a distribution of R,G,B-signals. For this purpose,
// function values is converted to Color by the action of the instance
// rsp of class Response. Here the capability of class Response
// to create directly Colors by the new function member color()
// is exploited.
// Only the part of the f's living on the rectangle roiX x roiY are
// actually 'painted'. This allows to update small subareas
// of an image much more quickly than redrawing of the whole image would
// allow. If roiX/Y is void it gets replaced by getIvX()/Y().
// Most convenient form to represent functions on the plane.
void show(const F >& f, R XL, R XU, R YL, R YU,
bool noDisplay=false);
// A vector-valued function, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals.
// For this purpose, function values are converted to Au by the automatics
// chosen by the compiler. Only the part of the f's living on the
// rectangle XL<=X<=XU, YL<=Y<=YU are actually 'painted'. This allows to
// update small subareas of an image much more quickly than redrawing of
// the whole image would allow. Unfortunately the C++ syntax doesn't allow
// to put the natural default values XL=CL.x,... YU=CU.y.
void show(const F& f, R XL, R XU, R YL, R YU,
bool noDisplay=false);
// A vector-valued function, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals.
// For this purpose, function values are converted to Au by the automatics
// chosen by the compiler. Only the part of the f's living on the
// rectangle XL<=X<=XU, YL<=Y<=YU are actually 'painted'. This allows to
// update small subareas of an image much more quickly than redrawing of
// the whole image would allow. Unfortunately the C++ syntax doesn't allow
// to put the natural default values XL=CL.x,... YU=CU.y.
void show(const F >& f);
// A vector-valued function, defined on the object space of the Graph are
// represented as a distribution of R,G,B-signals. For this purpose,
// function values are converted to Au by the automatics chosen by the
// compiler.
void show(const F& f, R gamma=0.01, bool zeroIsZero=false);
// exaluates f all over the object space associated with *this
// (considering this rectangle as a part of the complex plane and
// displays the function value as a color according to
// CpmImaging::Color(C). Notice that all negative values of gamma have
// the same effect (logarithmic response).
// If the last argument is true, zero brightness belongs to f-value
// C(0) and not to the minimum calue of |f(z)| over the image.
// There are situations where the range of |f(z)| is very small
// and essentially due to numerical noise. In these cases we
// make this noise visible if zeroIsZero=false. In these cases
// zeroIsZero=true gives a more natural representation.
void show(const F& f, R gamma=0.01, bool zeroIsZero=false);
// essentially the same as the previous function, only a different
// coding of the domain (C coded as R2) is used.
void show(Z n, R pl, R pu, R f1(R z1), R f2(R z2), const Color& c);
// The n points (f1(p),f2(p)), with p ranging equidistantly from pl to
// pu, are connected to form a polygon, and the this polygon is shown
// in a autoscaled window and grid. Window data are written to screen.
// The polygon is drawn in a color c.
void addFrame(void);
// marks the boundary of the Graph by a line in GraphColor
void addFrm_(Color const& cf);
//: add frame
// marks the boundary of the Graph by a line in Color cf
void mark( V const& xList, V const& cList=V(1,RED));
// xList is a list of x-Values that will be marked on the x-Axis
// by vertical lines as far as they are in the x-Range determined by
// *this. The color of these lines is determined by cList. If
// cList is shorter than xList, it gets repeated periodically.
// The argument *thick allows to use thick lines which sometimes improves
// printability.
// most advanced function for representing a family of curves
bool mark(V< V > const& dat, V const& cl=rainBow,
R secFac=1.05, B adaptToData=B(0));
// Graphical representation of a family of curves which don't need
// to have the same list of x-values. The representation of a curve as
// an instance of vector allows fast iteration and flexible
// generation by adding points. Now this type can very efficiently
// be converted to V so that there is no need for introducing
// std::vector in the interface of the C+- classes.
// This function (as all functions named 'mark') only writes to image
// memory. To make the curves visible one has to call vis().
// Adding text is best done by calling addText(...) with suitable
// arguments.
// For secFac=1 the function adjusts the frame just as to frame
// the set of points given by dat. A larger secFac allows for a
// boundary arround the points, a smaller one yields a subframe.
// Typical application:
// V > dat=...;
// Word w1= ...; // text of first line of text
// Word w2= ...; // text of second line of text
// V txt("",w1,w2);
// Graph g;
// g.setText(txt); // set text first, in order not to destroy the
// // legend created by mark
// g.mark(dat); // auto-scaled graphics with legend for axis scales.
// 2020-03-24: using two calls of this function in succession for the
// V> dat was such that dat.dim() == 1 showed strange behaviour that
// the image content of the second graphics got lost and was not shown.
// Grid lines and legend on scales of the axes remained unperturbated.
// in this case the x-axes was uniformely divided so that
// mark(R_vector ...) could be used to do the job. This worked fine.
bool mark( V const& dat, V const& cl=rainBow,
R secFac=1.05, B adaptToData=B(0))
// This function can be implemented by means of the foregoing one:
{
return mark(V(1_Z,dat),cl,secFac,adaptToData);
}
bool mark(R_Matrix const& dat, V const& cl=rainBow, R secFac=1.05,
Z modeXY=0);
// This is effectively the previous function where the array of type
// V< V > to be inserted as argument into the previous function
// is formed from matrix dat in a manner that depends on the argument
// modeXY.
// Matrix dat is assumed to have the following layout:
//
// t1 x11 y11 x12 y12 x13 y13 ... x1n y1n
// ...
// tm xm1 ym1 xm2 ym2 xm3 ym3 ... xmn ymn
//
// Data of this form arise when a system of n-particles is simulated in
// the course of times (t1,t2,...,tm). Here (x,y) is a 2D position of a
// particle, which/ may result from a 3D position by projection.
// A convenient visualization should collect the (x,y)-pairs of any
// selected particle, ordered according to their associated time ti
// as a curve. This curve thus results from the polygon
// Pi := ((x1i,y1i),(x2i,y2i),...,(xmi,ymi)),
// where the points belong to the times (t1,t2,...,tm).
// Representation of these polygons is done for modeXY==0.
// For modeXY==1 we consider and represent the polygons
// Xi := ((t1,x1i),(t2,x2i),...,(tm,xmi))
// and for modeXY==2 we consider and represent the polygons
// Yi := ((t1,y1i),(t2,y2i),...,(tm,ymi)).
// For secFac=1 the function adjusts the frame just as to frame
// the set of points given by dat. A larger secFac allows for a
// boundary arround the points, a smaller one yields a subframe.
// Most advanced function for representing a rectangular field
// of complex values.
R mark(V> const& mat, bool matrixstyle=false, Iv range=Iv(),
Z ipolMethod=0, bool faithful=true, bool periodic=true,
R secFac=-1, bool bw=false)const
{
return
markVVC(mat,matrixstyle,range,ipolMethod,faithful,periodic,secFac,bw);
// this function is declared in cpmframe.h
}
// Most efficient way of Graph to graphically represent a rectangular
// field of data.
// Interpolating representation of a matrix 'mat' of complex numbers.
// The indexing of the matrix is as it arrose in 2-dimensional
// quantum mechanical applications: mat[i][j] is such that the first
// index i grows from 0 to mat.dim()-1 along the graphical x-axis
// (i.e. horizontally from left to right). And the second indey j
// grows from 0 to mat[0].dim()-1 along the y-axis on screen
// (i.e. vertically from bottom to top, sic!). This correspondence
// between matrix indexes and graphical position holds for the
// default value matrixstyle=false. For matrixstyle=true, mat will be
// represented as a mat.dim() times mat[0].dim() matrix with the
// first matrix index growing vertically downwards and the second
// matrix index growing horizontally to the right. Especially
// mat[0][0] is shown at the upper left screen corner.
// The parameter 'range' has the effect that the complex number z gets
// represented by the color Color(z,range), see cpmimagingtools.h class
// Color. If range is the void interval (default), the actual values
// of mat will be used to define a suitable range for representation.
// Notice that Color::gamma has an influence on Color(z,range) and
// thus on the functionality of the present function.
// Parameter 'ipolMethod' has to be either 0 or 1. 0 for pixelation,
// 1 for bilinear interpolation.
// Interpolation at the boundaries is controlled by parameter
// 'periodic'. If it has value true, we use periodic continuation of the
// data in mat which is the correct method for my quantum mechanics
// application. If it has value false the boundary interpolation data
// are taken from a continuation 'as constant'.
// This is OK for arbitrary content of mat.
// The interpolation methods here are appropriate only if mat has
// not (much) more pixels than *this. This is the common situation
// in modelling, where mat is hard to compute and therefore has typically
// not more than 100 x 100 pixels and should be displayed on screen
// on a larger window for convenient visual presentation.
// If the argument 'faithful' has the value 'true', the actually painted
// area has the aspect ratio as prescribed by mat, and fills the
// frame of *this maximally while respecting this boundary condition.
// For value false, the whole frame of *this will be used for the
// representation of the data; this means that the aspect ratio
// gets set from *this.
// Addition 2007-09-26: 'first run auto adjustment' controlled
// by the newly introduced last argument 'secFac'.
// For secFac>0 the range will set in the first call to
// secFac*Max{ mat[i][j].abs() | i,j}
// and then used in further calls.
// The new last argument bw allows black and white rendition of colors.
}; // class Graph
} // namespace CpmGraphics
#endif