#pragma once
#include "plot.h"
#include "vector.h"
#include <gdiplus.h>
char round125(float&v,bool up); // v auf ein Vielfaches von 1, 2 oder 5 (bezüglich l) runden
float round2(float v,float l, bool up); // v auf 1-2 signifikante Digits runden
struct Range:public RANGE{
Range(float a, float e) {init(a,e);}
Range() {reset();}
void init(float _a, float _e) {a=_a; e=_e;}
void reset(); // {+Inf,-Inf} -> invalid
bool inside(float v) const {return a<=v && v<=e;}
float length() const {return e-a;}
bool isRegular() const {return length()>0;} // Echtes Intervall
bool isValid() const {return length()>=0;} // Echtes oder Null-Intervall
bool expand(float v) {return expand(v,v);}
bool expand(float,float);
bool expand(const RANGE&r) {return expand(r.a,r.e);} // r muss ein gültiger Bereich sein (oder nicht?)
enum ALLOW{
EXPAND=1,
SHRINK=2,
};
BYTE test(const RANGE&);
BYTE set(const RANGE&,BYTE allow=EXPAND);
BYTE set2(const RANGE&,ALLOW allow=EXPAND); // wie set() jedoch Expansion/Shrinken in größeren Sprüngen
};
struct Label{
const char*string;
float position;
Gdiplus::RectF box;
// int priority; // bspw. 99 für kalkulierte Labels, 0 für Anwender-Labels, 1 für Achsenenden
};
class XY;
class Plot;
struct Scalepair:public SCALEPAIR {
float scale(float x) const {return x*f+o;}
float unscale(float X) const {return (X-o)/f;}
SCALEPAIR&combine(const SCALEPAIR&b); // Neues Scalepair für nicht-kommutative Hintereinanderausführungen mehrerer Skalierungen
SCALEPAIR&operator*(const SCALEPAIR&b) {return combine(b);}
SCALEPAIR&operator~(); // invertiertes SCALEPAIR liefern
};
class Scale:public SCALE{
public:
Range range;
Rect rc;
Scale(XY*parent,UINT side,char*name=0);
Scale(XY*parent,SCALE&scale);
~Scale();
// left+topdown/top+leftright/right+topdown/bottom+leftright/left+downup/top+rightleft/right+downup/bottom+rightleft
// Bit 0: X- oder Y-Ausrichtung
// Bit 1: links/oben (0) oder rechts/unten (1)
// Bit 2: nach rechts/unten wachsend (0) oder umgekehrt (1)
// Bit 3: (Nur Y-Achse) Vertikal statt horizontal stapeln
// Bit 4: Y-Achse: Digital: Name waagerecht, feste Höhe
// X-Achse: Zeit: Skalenteilung in Stunden:Minuten:Sekunden
// Bit 5: logarithmisch (nicht zusammen mit Digital)
// Anfang, Ende (darf nie gleich sein, sonst ÷0-Fehler!)
float preci; // Präzision = sinnvolle untere Tick-Grenze, <=0 = keine Grenze
Gdiplus::Font*font[2]; // Schrift der Skaleneinteilung und der Achsenbezeichnung
Gdiplus::Color color[2];// Farbe der Skaleneinteilung (Striche und Text) und der Achsenbezeichnung
Gdiplus::Brush*brush[2]; // Pinsel für Schrift
std::vector<Label> labels; // Feste Labels (Marken) in der Skale
std::vector<Plot*> plots; // Zugeordnete Plots (für Autoskalierung)
XY*parent;
float F,O; // Faktor, Offset (Großbuchstaben = Pixelbereich, Kleinbuchstaben = Anwenderbereich)
private:
int Length() const {int ret=(side&1?rc.height():rc.width())-1;if (side&4) ret=-ret; return ret;} // Länge der Skale in Pixel; negativ wenn rückwärts laufend
int Start() const {return side&4?rc[side&1|2]-1:rc[side&1];} // Koordinate des Skalenanfangs (zu "a" gehörig)
std::vector<Label> ticks; // Berechnung bei onSize(), distributeTicks()
Gdiplus::SizeF numberExtent; // Berechnung bei needSpace()
static const char ticklen=5;
public:
void calcFO() {F=(float)Length()/((Range*)&range)->length(); O=(float)Start()-range.a/F;} // Faktor + Offset neu berechnen (Bereichsänderung oder Größenänderung)
float scale(float x) const {return x*F+O;}
float unscale(float X) const {return (X-O)/F;}
void setRange(float _a, float _e) {range.a=_a; range.e=_e; calcFO();}
void distributeTicks(Gdiplus::Graphics&);
void tickstringformat(Gdiplus::StringFormat&);
void tickposition(float,Gdiplus::PointF[2]);
void namestringformat(Gdiplus::StringFormat&);
void nameposition(Gdiplus::Graphics&,Gdiplus::PointF&);
bool addTick(Gdiplus::Graphics&,float v,char nk);
void onPaint(Gdiplus::Graphics&);
void onSize(Gdiplus::Graphics&);
struct NEED{
int w; // X: Ungefähre Breite; Y: Ungefähre Höhe; Eingangsparameter
int h,l,r; // X: Höhe, linker Überhang, rechter Überhang, oder Y: Breite, oberer Überhang, unterer Überhang
};
void needSpace(Gdiplus::Graphics&,NEED&);
};
class Plot:public PLOT{
void init();
void done();
XY*parent;
Scale*xscale,*yscale;
Gdiplus::Color color;
wchar_t marker;
struct stroke{
Gdiplus::Pen*pen;
Gdiplus::PointF*points;
int len;
}s; // Cache für Kurvendaten
enum action{
NONE,ADD1,SHIFT1,FULL_NEW
};
void makePoly(action); // berechnet Kurve sowie Minima und Maxima
public:
Range rangeX,rangeY; // tatsächlicher Definitions- und Wertebereich der aktuellen Daten
Plot(XY*_parent,PLOT&);
~Plot() {done();}
void operator=(PLOT&pl) {done();*(PLOT*)this=pl;init();}
void onSize(Gdiplus::Graphics&g);
void onPaint(Gdiplus::Graphics&g);
void putdata(FLEXDATA&);
};
class XY{
XY(HWND);
~XY();
HWND wnd;
HWND hPlotLegend,hScaleLegend,hCursorLegend; // ownerdrawn ListView with checkmarks
HWND hMiniature; // full-extent plot without scales
Rect rcPlot;
std::vector<Scale*> xscales; // typisch 1
std::vector<Scale*> yscales; // typisch 1-2
std::vector<int> hdivs;
std::vector<Plot*> plots;
void onSize(int,int);
void onPaint(Gdiplus::Graphics&g);
LRESULT wndproc(UINT,WPARAM,LPARAM);
static LRESULT CALLBACK wndproc(HWND,UINT,WPARAM,LPARAM);
ULONG_PTR gdiplusToken;
enum{
PLOTDATA=1, // Datenänderung (Auswirkung auf Skalen)
PLOTATTR=2, // Plotfarbe o.ä.
SCALEEXPAND=4,// (intern) Sofortige Skalenänderung
SCALESHRINK=8,// (intern) Verzögerte Skalenänderung
PLOTS=16, // Anzahl der Plots
SCALES=32, // Anzahl der Skalen u.ä.
};
void beginChange(BYTE what);
void endChange(BYTE what);
void shrinkScales();
void repositionAll();
bool timer_running;
public:
bool needReposition;
static BOOL init();
Scale*getXScale(char&index);
Scale*getYScale(char&index);
Gdiplus::Color getPlotColor(char&index);
};
Detected encoding: UTF-8 | 0
|