/* File: Plotstream.cxx
*
* Implementation class Plotstream
* A plotstream opens a window displays a data plot, then closes
* the window.
*
* left-clicking the mouse in the window displays the current plot coordinates of
* the cursor.
*
* This file hides all the fiddly bits about plotting a function.
*
* Author: jlk
* Version: 1.2
* Date: January 2006
*/
#include <ostream>
#include <iomanip>
#include <string>
#include <cfloat>
#include "Plotstream.h"
#include <sstream>
//#include "BGI_util.h"
#include <windowsx.h>
HWND Plotstream::wnd;
HDC Plotstream::dc;
// Constants used exclusively in this file
//static const int topx = 150; // top left corner of window
//static const int topy = 50;
static const int border_height = 20;
static const int border_width = 20;
static const int left_border = 70;
static const int mark_length = 4;
//static const int max_height = 600;
//static const int min_height = 200;
static const int graph_color = GREEN; // Default only, can be changed
static const int point_color = graph_color; // will be XORed
static const int marker_color = CYAN; // Will give red in XOR mode over white
static const float chouia = 0.0001; // an invisible difference (in drawing terms)
static const double epsilon = DBL_EPSILON * 1000; // Extremely small difference
//static const bool ERASE = true;
// Mouse events variables used exclusively in this file
//static bool left_clicked = false;
static int cursorX;
static int cursorY;
// dtoa safely converts a double to the equivalent char *
// It is the responsibility of the calling program to delete the returned string
static char * dtoa(double val)
{
ostringstream ostr;
ostr.precision(3);
ostr << val;
char * ret = new char[ostr.str().size() + 1];
strcpy(ret, ostr.str().c_str());
return ret;
}
// pow10() does not exist in mingw (redefine as inline wrapper here)
static inline double pow10(double x) {return pow(10.0,x);}
/* Rounds double value by discarding very small decimals.
* (necessary to deal with previous small floating point errors)
* @param val value to round
* @param sigDigits number of significant digits.
* @param intrep all significant digits as a long int
* @return truncated value as a double (may be inexact)
*/
static double floatRound(double val, int&sigDigits, int&intrep) {
int neg;
int decPos;
const int n=2; // wanted significant digits
char*str=_ecvt(val,n,&decPos,&neg);
intrep = atoi(str);
sigDigits=n;
// find position of dot if any
if (decPos>=n) { // then it's an integer, return now
sigDigits = decPos;
return val;
}
val=intrep*pow10(decPos-n);
if (neg) val=-val;
return val;
}
/* Local function hidden at file scope
* Obtains a practical y axis range with "round" numbers at both ends
* Params hold the min and max values in plot data on entry and
* pass back the practical values on output.
*/
static void getNearest(double&y_min, double&y_max) {
int sigdigits; // Holds number of significant digits
int intrep; // the integer representation
double min = y_min, max = y_max, dif;
min = floatRound(min, sigdigits, intrep);
max = floatRound(max, sigdigits, intrep);
dif = max - min - (y_max - y_min);
y_max = floatRound(y_max + dif / 2, sigdigits, intrep);
y_min = floatRound(y_min - dif / 2, sigdigits, intrep);
}
/*
* Local function hidden at file scope
* Attempt to guess a convenient divisor for grid along the X axis
* If 0 is in the range, it should in many cases be on the grid.
* Otherwise, divide the x range by 10, or 6, or 7, or 5, or 8, or 3,
* or 4, in this order if divisible, by 10 otherwise.
*/
static int getXDivisor(double lo, double hi, int plotWidth) {
int sigdigits, pquo, div = 0;
int intVal;
double delta = (hi - lo) / plotWidth; // Ignore 1 pixel inaccuracies
double range = hi - lo;
floatRound(range, sigdigits, intVal);
// If 0 is on the axis attempt to place it on the grid
if (lo < 0 && hi > 0) // 0 is part of the range
{
if(fabsl(lo) < hi)
div = int(round(hi / fabsl(lo)));
else
div = int(round(fabs(lo) / hi));
if (div > 0 && div <= 9)
{
div += 1;
// If too small number of divisions, try multiples
if(div == 2) // See if divisible by 12, 10, 6, 8, or 4
{
if (remquo(intVal, 12, & pquo) < delta)
div = 12;
else if (remquo(intVal, 10, & pquo) < delta)
div = 10;
else if (remquo(intVal, 6, & pquo) < delta)
div = 6;
else if (remquo(intVal, 8, & pquo) < delta)
div = 8;
else if (remquo(intVal, 4, & pquo) < delta)
div = 4;
}
else if (div == 3) // See if divisible by 12, 9 or 6
{
if (remquo(intVal, 12, & pquo) < delta)
div = 12;
else if (remquo(intVal, 9, & pquo) < delta)
div = 9;
else if (remquo(intVal, 6, & pquo) < delta)
div = 6;
}
return div;
}
}
// If not, then attempt a reasonable division of the range.
// Try dividing by 12, or 10, or 5, or 6, or 8, or 3, or 4
else
{
if (remquo(intVal, 12, & pquo) < delta)
div = 12;
else if (remquo(intVal, 10, & pquo) < delta)
div = 10;
else if (remquo(intVal, 7, & pquo) < delta)
div = 7;
else if (remquo(intVal, 6, & pquo) < delta)
div = 6;
else if (remquo(intVal, 5, & pquo) < delta)
div = 5;
else if (remquo(intVal, 8, & pquo) < delta)
div = 8;
else if (remquo(intVal, 3, & pquo) < delta)
div = 3;
else if (remquo(intVal, 4, & pquo) < delta)
div = 4;
else // We are running out of common divisors
div = 10;
}
return div;
}
/*
* Attempt to guess a convenient divisor for grid along the Y axis
* If 0 is in the range, it should in many cases be on the grid.
* Otherwise, divide the y range by 5 or 3 or 4 in this order if
* divisible, by 5 otherwise.
*/
static int getYDivisor(double lo, double hi, int plotHeight) {
int sigdigits, pquo, div = 0;
int intVal=0;
double delta = (hi - lo) / plotHeight; // Ignore 1 pixel inaccuracies
// If 0 is on the axis attempt to place it on the grid
if (lo < 0 && hi > 0) {// 0 is part of the range
if (fabsl(lo) < hi) div = int(round(hi / fabsl(lo)));
else div = int(round(fabs(lo) / hi));
if (div > 0 && div < 6) {
div += 1;
// If too small number of divisions, try multiples
if(div == 2) {// See if divisible by 6, 4
if (remquo(intVal, 6, & pquo) < delta) div = 6;
else if (remquo(intVal, 4, & pquo) < delta) div = 4;
}else if (div == 3) { // See if divisible 6
if (remquo(intVal, 6, & pquo) < delta) div = 6;
}
return div;
}
}
// if not, then attempt a reasonable division of the range
double range = hi - lo;
floatRound(range, sigdigits, intVal);
// If 0 is in the middle try dividing by 4
if (div == 1) {
if (remquo(intVal, 4, & pquo) < delta) div = 4;
else div = 2; // We are running out of common divisors
}else{ // else try dividing by 3, or 4, or 5, or 2
if (remquo(intVal, 5, & pquo) < delta) div = 5;
if (remquo(intVal, 3, & pquo) < delta) div = 3;
else if (remquo(intVal, 4, & pquo) < delta) div = 4;
else if (remquo(intVal, 2, & pquo) < delta) div = 2;
else div = 5;// We are running out of common divisors
}
return div;
}
#if 0
/*
* This handler will be triggered on left mouse click.
* It will record the event and current mouse coordinates in the window
*/
static void click_handler(int x, int y)
{
left_clicked = true;
cursorX = x;
cursorY = y;
}
/*
* Local function hidden at file scope
* Delete a rectangle with top left corner x1, y1 and bottom right x2, y2
*/
static void delRectangle(int x1, int y1, int x2, int y2)
{
int poly[8] = {x1, y1, x2, y1, x2, y2, x1, y2};
// int bkColour = getbkcolor();
// setfillstyle(EMPTY_FILL, bkColour );
// setcolor(bkColour);
// fillpoly(4, poly);
}
#endif
/************************* CLASS FUNCTIONS ***************************/
Plotstream::Plotstream(const char*title)
:plotStarted(false) {
if (!wnd) wnd=CreateWindow("koolplot",title,WS_OVERLAPPEDWINDOW|WS_VISIBLE,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
0,0,0,this);
else{
SetWindowLongPtr(wnd,0,(LONG_PTR)this); // repoint Windows object data
if (title) SetWindowText(wnd,title);
}
}
void Plotstream::addplot(const Plotdata&x, const Plotdata&y, Color color) {
size_t i=traces.size();
traces.resize(i+1);
internal_xytrace&t=traces[i];
t.t.x=&x;
t.t.y=&y;
t.t.a.colour=color;
t.t.a.drawstyle=0;
t.t.a.penstyle=PS_SOLID;
t.t.a.penwidth=1;
t.g.penPlot=CreatePen(t.t.a.penstyle,t.t.a.penwidth,t.t.a.colour);
}
void Plotstream::show(const char*title) {
if (title) SetWindowText(wnd,title);
InvalidateRect(wnd,0,TRUE);
MSG Msg;
while (GetMessage(&Msg,0,0,0)) { // mouse click, space bar, or Alt+F4 generates WM_QUIT
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
void Plotstream::plot(const Plotdata&x, const Plotdata&y, Color color) {
addplot(x,y,color);
show();
}
Plotstream::~Plotstream() {
for each(internal_xytrace t in traces) {
DeleteObject(t.g.penPlot);
t.markers.clear();
}
traces.clear();
}
void Plotstream::onPaint() {
RECT r;
GetClientRect(wnd,&r);
for each(internal_xytrace t in traces) {
// Need 2 points minimum to do a plot
if (t.t.x->size() < 2) break;
// Need as many y values as x values to do a plot
if (t.t.x->size() > t.t.y->size()) break;
// Store the hi and lo points of the axes
Plotdata::minXY(*t.t.x,*t.t.y,lo_x,lo_y);
Plotdata::maxXY(*t.t.x,*t.t.y,hi_x,hi_y);
}
// Set Y values to the nearest "round" numbers
getNearest(lo_y, hi_y);
x_range = hi_x - lo_x;
y_range = lo_y - hi_y; // negativ!
SetRect(&rcPlot,left_border,border_height,r.right-border_width,r.bottom-border_height);
x_scale = x_range / (rcPlot.right-rcPlot.left);
y_scale = y_range / (rcPlot.bottom-rcPlot.top);
drawAxes();
for each (internal_xytrace t in traces) drawFunc(t);
}
/* Convert graph x value to screen coordinate */
int Plotstream::X(double x) const{ return int((x - lo_x) / x_scale + rcPlot.left);}
/* Convert graph y value to screen coordinate */
int Plotstream::Y(double y) const{ return int((y - hi_y) / y_scale + rcPlot.top);}
/* Convert screen coordinate to graph x value */
double Plotstream::plotX(int screenX) const{ return (screenX - rcPlot.left) * x_scale + lo_x;}
/* Convert screen coordinate to graph y value */
double Plotstream::plotY(int screenY) const{ return (screenY - rcPlot.top) * y_scale + hi_y;}
/* @return true if given location is within x and y axes ranges */
bool Plotstream::withinRange( double x, double y) const{
return x + x_scale >= lo_x && x - x_scale <= hi_x
&& y + y_scale >= lo_y && y - y_scale <= hi_y;
}
/* Round(est) double within one pixel in X
*
* @param theVal the value to be rounded, passed back rounded to caller
* @param direction the requested direction of rounding UP, DOWN or ANY
* @return the direction the rounding took place.
*/
Rounding Plotstream::nearRoundX(double & val, Rounding direction) const
{
int valSigdigits;
int upSigdigits;
int downSigdigits;
int intrep;
double theVal, upVal, downVal;
theVal = floatRound(val, valSigdigits, intrep);
if (direction == UP) // If a higher or equal value has been requested
{
upVal = floatRound(val + x_scale, upSigdigits, intrep);
if (theVal < 0 && upVal > 0) // Takes care of 0
{
val = 0;
return UP;
}
else if (fabs(trunc(theVal)) < fabs(trunc(upVal))) // takes care of integers
{
val = trunc(upVal);
return UP;
}
else if (valSigdigits > upSigdigits)
{
val = upVal;
return UP;
}
else
return ANY;
}
else if (direction == DOWN) // If lower or equal value is requested
{
downVal = floatRound(val - x_scale, downSigdigits, intrep);
if (theVal > 0 && downVal < 0) // Takes care of 0
{
val = 0;
return ANY;
}
else if (fabs(trunc(theVal)) > fabs(trunc(downVal))) // takes care of integers
{
val = trunc(theVal);
return ANY;
}
else if (valSigdigits > downSigdigits)
{
val = downVal;
return DOWN;
}
else
return ANY;
}
else // May round in any direction
{
upVal = floatRound(val + x_scale, upSigdigits, intrep);
downVal = floatRound(val - x_scale, downSigdigits, intrep);
if (theVal < 0 && upVal > 0) // Takes care of 0
{
val = 0;
return ANY;
}
else if (theVal > 0 && downVal < 0) // Takes care of 0
{
val = 0;
return ANY;
}
else if (fabs(trunc(theVal)) < fabs(trunc(upVal)))
{
val = trunc(upVal);
return UP;
}
else if (fabs(trunc(theVal)) > fabs(trunc(downVal))) // takes care of integers
{
val = trunc(theVal);
return ANY;
}
int minimum = min(valSigdigits, upSigdigits);
minimum = min (minimum, downSigdigits);
if(minimum == valSigdigits)
{
val = theVal;
return ANY;
}
else if (minimum == upSigdigits)
{
val = upVal;
return UP;
}
else
{
val = downVal;
return DOWN;
}
}
}
/* Round(est) double within one pixel in Y */
Rounding Plotstream::nearRoundY(double & val, Rounding direction) const
{
int valSigdigits;
int upSigdigits;
int downSigdigits;
int intrep;
double theVal, upVal, downVal;
theVal = floatRound(val, valSigdigits, intrep);
if (direction == UP) // If a higher or equal value has been requested
{
upVal = floatRound(val + y_scale, upSigdigits, intrep);
if (theVal < 0 && upVal > 0) // Takes care of 0
{
val = 0;
return ANY;
}
else if (fabs(trunc(theVal)) < fabs(trunc(upVal))) // takes care of integers
{
val = trunc(upVal);
return UP;
}
else if (valSigdigits > upSigdigits)
{
val = upVal;
return UP;
}
else
return ANY;
}
else if (direction == DOWN) // If lower or equal value is requested
{
downVal = floatRound(val - y_scale, downSigdigits, intrep);
if (theVal > 0 && downVal < 0) // Takes care of 0
{
val = 0;
return DOWN;
}
else if (fabs(trunc(theVal)) > fabs(trunc(downVal))) // takes care of integers
{
val = trunc(theVal);
return ANY;
}
else if (valSigdigits > downSigdigits)
{
val = downVal;
return DOWN;
}
else
return ANY;
}
else // May round in any direction
{
upVal = floatRound(val + y_scale, upSigdigits, intrep);
downVal = floatRound(val - y_scale, downSigdigits, intrep);
if (theVal < 0 && upVal > 0) // Takes care of 0
{
val = 0;
return ANY;
}
if (theVal > 0 && downVal < 0) // Takes care of 0
{
val = 0;
return DOWN;
}
else if (fabs(trunc(theVal)) < fabs(trunc(upVal)))
{
val = trunc(upVal);
return UP;
}
else if (fabs(trunc(theVal)) > fabs(trunc(downVal))) // takes care of integers
{
val = trunc(theVal);
return ANY;
}
int minimum = min(valSigdigits, upSigdigits);
minimum = min (minimum, downSigdigits);
if(minimum == valSigdigits)
{
val = theVal;
return ANY;
}
else if (minimum == upSigdigits)
{
val = upVal;
return UP;
}
else
{
val = downVal;
return DOWN;
}
}
}
/* Draw the axes */
void Plotstream::drawAxes() {
int xDivs; // number of x divisions
int yDivs; // number of y divisions
// int divLength; // length (in pixels) of a division
int sigdigits;
int intVal;
// draw the rectangle
HPEN penRect=CreatePen(PS_SOLID,1,DARKGRAY);
HPEN penGrid=CreatePen(PS_DOT,0,LIGHTGRAY);
HPEN penMark=CreatePen(PS_SOLID,0,DARKGRAY);
HPEN open=SelectPen(dc,penRect);
Rectangle(dc,rcPlot.left-1, // -1 to fix small discrepancy on screen
rcPlot.top-1, // Probably due to line width.
rcPlot.right,
rcPlot.bottom);
// Attempt to guess a reasonable number of grid divisions for x and y
xDivs = getXDivisor(lo_x, hi_x, rcPlot.right-rcPlot.left);
// If y axis is large, divide in the same manner as x axis
/*if (winHeight > winWidth * 3 / 5.0) yDivs = getXDivisor(lo_y, hi_y, plotHeight);
else*/ yDivs = getYDivisor(lo_y, hi_y, rcPlot.bottom-rcPlot.top);
// draw the grid
SelectPen(dc,penGrid);
// Horizontal grid
for (int i = yDivs - 1; i > 0; i--) {
int y=rcPlot.top + MulDiv(rcPlot.height(),i,yDivs);
moveto(rcPlot.left,y);
lineto(rcPlot.right,y);
}
// Vertical grid
for (int i = xDivs - 1; i > 0; i--) {
int x=rcPlot.left + MulDiv(rcPlot.width(),i,xDivs);
moveto(x,rcPlot.top);
lineto(x,rcPlot.bottom);
}
// Draw Axes markers
SelectPen(dc,penMark);
// Y axis
for (int i = yDivs - 1; i > 0; i--) {
int y=rcPlot.top + MulDiv(rcPlot.height(),i,yDivs);
moveto(rcPlot.left,y);
lineto(rcPlot.left+mark_length,y);
moveto(rcPlot.right-mark_length,y);
lineto(rcPlot.right,y);
}
// X axis
for (int i = xDivs - 1; i > 0; i--) {
int x=rcPlot.left + MulDiv(rcPlot.width(),i,xDivs);
moveto(x,rcPlot.bottom-mark_length);
lineto(x,rcPlot.bottom);
moveto(x,rcPlot.top);
lineto(x,rcPlot.top+mark_length);
}
// Number the axes
SetTextColor(dc,BLACK);
SetBkMode(dc,TRANSPARENT);
// Y axis
HFONT fntY=CreateFont(16,0,0,0,0,0,0,0,0,0,0,0,0,"Arial");
HFONT ofnt=SelectFont(dc,fntY);
SetTextAlign(dc,TA_RIGHT|TA_TOP);
SIZE sz;
GetTextExtentPoint32(dc,"0",1,&sz);
// divLength = int(rcPlot.height() / yDivs);
double divVal = floatRound((lo_y - hi_y) / yDivs, sigdigits, intVal);
for (int i = 0; i <= yDivs; i++) {
int y=rcPlot.top + MulDiv(rcPlot.height(),i,yDivs)-sz.cy/2;
double val = floatRound(hi_y + divVal * i, sigdigits, intVal);
if (fabs(val) < chouia * (hi_y - lo_y)) val = 0;
char * asciival = dtoa(val);
outtextxy(rcPlot.left-sz.cx,y,asciival);
delete [] asciival;
}
// X axis
SetTextAlign(dc,TA_CENTER|TA_TOP);
divVal = floatRound((hi_x - lo_x) / xDivs, sigdigits, intVal);
for (int i = 0; i <= xDivs; i++) {
int x=rcPlot.left + MulDiv(rcPlot.width(),i,xDivs);
double val = floatRound(lo_x + divVal * i, sigdigits, intVal);
if (fabs(val) < chouia * (hi_x - lo_x)) val = 0;
char * asciival = dtoa(val);
outtextxy(x,rcPlot.bottom+sz.cy/4, asciival);
delete[] asciival;
}
SelectFont(dc,ofnt);
DeleteFont(fntY);
SelectPen(dc,open);
DeletePen(penMark);
DeletePen(penGrid);
DeletePen(penRect);
}
void Plotstream::drawFunc(internal_xytrace&t) {
vector<double>::const_iterator x_it = t.t.x->getData().begin();
vector<double>::const_iterator y_it = t.t.y->getData().begin();
vector<double>::const_iterator x_end = t.t.x->getData().end();
HPEN open=SelectPen(dc,t.g.penPlot);
if (isfinite(*y_it) && isfinite(*x_it)) {
moveto(X(*x_it), Y(*y_it));
plotStarted = true;
}else{
// Else if both x and y are NAN, then it could be either a color change
// request or a single point drawing request.
// if (!isfinite(*y_it) && !isfinite(*x_it)
// && x_it+1 != x_end && x_it+2 != x_end) handleCommand(x_it, y_it);
}
while (++x_it != x_end) {
if (isfinite(*(++y_it)) && isfinite(*x_it)) {
if (plotStarted) lineto(X(*x_it), Y(*y_it));
else{
moveto(X(*x_it), Y(*y_it));
plotStarted = true;
}
}else{
plotStarted = false;
// If both x and y are NAN, then it could be either a colour
// change request or a single point drawing request.
// if (!isfinite(*y_it) && !isfinite(*x_it)
// && x_it+1 != x_end && x_it+2 != x_end) handleCommand(x_it, y_it);
}
}
for each(marker_t marker in t.markers) {
drawPointShape(X(marker.x),Y(marker.y));
}
SelectPen(dc,open);
}
#if 0
/* Checks the mouse for left-click.
* Prints the plot coordinates of the click in the top border area.
* returns true if the keyboard was not pressed.
*/
bool Plotstream::watchMouse( )
{
if (left_clicked)
{
double xClick = plotX(cursorX);
double yClick = plotY(cursorY);
Rounding xRounding = nearRoundX(xClick);
Rounding yRounding = nearRoundY(yClick);
swapbuffers();
if (withinRange(xClick, yClick)) // write location of mouse and place marker
{
// Adjust the marker location in function of the rounding
cursorX += xRounding - 1;
cursorY -= yRounding - 1;
// Draw the location marker if within the graph
drawMarker();
char * xLoc = dtoa(xClick);
char * yLoc = dtoa(yClick);
char * location = new char[strlen(xLoc) + strlen(yLoc) + 10];
int ypos = border_height / 2 + 3;
int xpos = (border_width + winWidth) / 2;
// Delete previous location string if any
delRectangle(left_border, 0, winWidth - 1, border_height - 2);
settextjustify(CENTER_TEXT, CENTER_TEXT);
setcolor(BLUE);
// Compose and print new location string
strcpy(location, "( ");
strcat(location, xLoc);
strcat(location, " , ");
strcat(location, yLoc);
strcat(location, " )");
outtextxy(xpos, ypos, location);
delete xLoc;
delete yLoc;
delete location;
}
else // delete all previous location info if any
{
// Erase marker
drawMarker(ERASE);
// Delete previous location string if any
delRectangle(left_border, 0, winWidth - 1, border_height - 2);
}
swapbuffers();
// Get ready for next click
left_clicked = !left_clicked;
}
return !kbhit();
}
#endif
// draws the marker shape in X and Y.
void Plotstream::drawMarkShape(int x, int y) const{
// Horz
moveto(x - 4, y); lineto(x + 4, y); // centre line
moveto(x - 5, y - 1); lineto(x - 3, y - 1);
moveto(x - 5, y + 1); lineto(x - 3, y + 1); // side lines
moveto(x + 5, y - 1); lineto(x + 3, y - 1);
moveto(x + 5, y + 1); lineto(x + 3, y + 1);
// Vert
moveto(x , y - 4); lineto(x , y + 4); // centre line
moveto(x - 1, y - 5); lineto(x - 1, y - 3);
moveto(x + 1, y - 5); lineto(x + 1, y - 3); // side lines
moveto(x - 1, y + 5); lineto(x - 1, y + 3);
moveto(x + 1, y + 5); lineto(x + 1, y + 3);
}
// Draw a marker at the current cursor position
// Will erase only if erase is true
void Plotstream::drawMarker(bool erase) {
/* select XOR drawing mode and marker colour */
int orop=SetROP2(dc,R2_XORPEN);
HPEN penMark=CreatePen(PS_SOLID,0,marker_color);
HPEN open=SelectPen(dc,penMark);
if (marked) { // then erase existing marker
drawMarkShape(lastX, lastY);
marked = false;
}
if (!erase) {
drawMarkShape(lastX=cursorX, lastY=cursorY);
marked = true;
}
/* Restore drawing mode */
SelectPen(dc,open);
DeletePen(penMark);
SetROP2(dc,orop);
}
// draws the point shape in X and Y.
void Plotstream::drawPointShape(int x, int y) const {
// Horz
moveto(x - 1, y - 2); lineto(x + 1, y - 2); // top line
moveto(x - 1, y + 2); lineto(x + 1, y + 2); // bottom line
// Vert
moveto(x - 2, y - 1); lineto(x - 2, y + 1); // left line
moveto(x + 2, y - 1); lineto(x + 2, y + 1); // right line
}
// Draw a single point at the given coordinates
void Plotstream::drawSinglePoint(double xCoord, double yCoord) const{
drawPointShape(X(xCoord) + 1, Y(yCoord));
}
// Set new foreground colour, remember last colour
void Plotstream::setFgColor(Color fgColour) {
lastColour = colour;
colour = fgColour;
// setcolor(fgColour);
}
// Reset foreground colour to last used colour
void Plotstream::resetFgColor() {
setFgColor(lastColour);
}
#if 0
/*
* Check whether the stream holds a request at this point.
* and handle the request if it does.
* A request can be a colour change, or a point marker.
*
* Pre-condition: The data vectors hold at least the 3 data points
* in x and y necessary to hold a command request.
* Post-condition: On exit the iterators are unchanged if the data is not
* a known command. They will point to the last data point
* in the request sequence otherwise.
*/
void Plotstream::handleCommand( dataIterator &x_it, dataIterator &y_it)
{
// Check if command is a colour change
int newColour = Plotdata::colorChange(x_it, y_it);
if(Plotdata::isColor(newColour))
{
if(newColour == RESETCOLOR)
setFgColor(lastColour);
else
setFgColor(newColour);
}
// else, draw a single point if requested
else
{
double xCoord, yCoord;
if(Plotdata::singlePoint(xCoord, yCoord, x_it, y_it))
drawSinglePoint(xCoord, yCoord);
}
}
#endif
Detected encoding: ASCII (7 bit) | 2
|