1 module plot2d.label; 2 3 import std.exception : enforce; 4 import plot2d.drawable; 5 6 interface Label : Drawable, Stylized 7 { 8 static interface Formatter 9 { 10 string x(double value, double step); 11 string maxXValue() @property; 12 13 string y(double value, double step); 14 string maxYValue() @property; 15 } 16 17 /// 18 static auto defaultFmtFunc(double value, double step) 19 { 20 import std..string; 21 auto sf = format("%f", step).tr("0", " ").strip.split("."); 22 return format("%.*f", sf[1].length, value); 23 } 24 25 /// 26 static class DefaultFormatter : Formatter 27 { 28 string maxX = "0.0", maxY = "0.0"; 29 override: 30 string x(double v, double s) 31 { 32 auto r = defaultFmtFunc(v, s); 33 if (r.length > maxX.length) maxX = r; 34 return r; 35 } 36 37 string y(double v, double s) 38 { 39 auto r = defaultFmtFunc(v, s); 40 if (r.length > maxY.length) maxY = r; 41 return r; 42 } 43 44 string maxXValue() @property { return maxX; } 45 string maxYValue() @property { return maxY; } 46 } 47 } 48 49 class LBGridLabel : Label 50 { 51 mixin StylizedHelper!"label"; 52 53 Formatter formatter; 54 55 double lineSpacing = 1.5; 56 57 bool onX = true; 58 bool onY = true; 59 60 Point space = Point(8, 8); 61 62 this(Style root, Formatter fmt=null) 63 { 64 setRootStyle(root); 65 formatter = fmt is null ? new DefaultFormatter : fmt; 66 } 67 68 Point minGridStep(Ctx cr) 69 { 70 setupStyle(cr); 71 72 Point xte, yte; 73 cr.getTextSize(formatter.maxXValue, xte.x, xte.y); 74 cr.getTextSize(formatter.maxYValue, yte.x, xte.y); 75 return Point(xte.x * 1.2, yte.y * 2.5); 76 } 77 78 override void draw(Ctx cr, Trtor tr) 79 { 80 setupStyle(cr); 81 82 auto fontsize = style.number.get("fontsize", 15); 83 84 auto im = tr.inMargin; 85 auto s = tr.gridStep; 86 auto o = tr.gridOffset; 87 auto chs = s / tr.scale; 88 chs.y *= -1; 89 90 Point te; 91 92 if (onX) for (double i = im.w.min + o.x; i <= im.w.max; i += s.x) 93 { 94 auto str = formatter.x(tr.toChX(i), chs.x); 95 auto pnt = Point(i, im.h.max + space.y); 96 foreach (n, ln; str.split("\n")) 97 { 98 cr.getTextSize(ln, te.x, te.y); 99 if (n == 0 && (i - te.x/2 < im.w.min || 100 i + te.x/2 > im.w.max)) break; 101 cr.moveToP(pnt + Point(-te.x/2, te.y)); 102 cr.showText(ln); 103 pnt.y += fontsize * lineSpacing; 104 } 105 } 106 107 if (onY) for (double i = im.h.max - o.y; i > im.h.min; i -= s.y) 108 { 109 auto str = formatter.y(tr.toChY(i), chs.y); 110 auto pnt = Point(im.w.min - space.x, i); 111 foreach (ln; str.split("\n")) 112 { 113 cr.getTextSize(ln, te.x, te.y); 114 cr.moveToP(pnt + Point(-te.x, te.y/2)); 115 cr.showText(ln); 116 pnt.y += fontsize * lineSpacing; 117 } 118 } 119 } 120 121 void setupStyle(Ctx cr) 122 { 123 cr.setFont(style.strval.get("fontface", "Monospace"), 124 style.number.get("fontsize", 15)); 125 126 cr.setColor(style.color.get("color", Color.mono(0, 1))); 127 } 128 }