6 var <>opacity=0.7,<>smoothing=false,<>linePattern;
9 *new { |bounds,horzGrid,vertGrid|
10 ^super.new.init(bounds, horzGrid, vertGrid)
12 *test { arg horzGrid,vertGrid,bounds;
14 bounds = bounds ?? {Rect(0,0,500,400)};
15 grid = DrawGrid(bounds,horzGrid,vertGrid);
16 w = Window("Grid",bounds).front;
17 UserView(w,bounds ?? {w.bounds.moveTo(0,0)})
20 grid.bounds = v.bounds;
23 .background_(Color.white)
27 init { arg bounds,h,v;
33 this.font = Font( Font.defaultSansFace, 9 );
34 this.fontColor = Color.grey(0.3);
35 this.gridColors = [Color.grey(0.7),Color.grey(0.7)];
45 pen.smoothing = smoothing;
46 if(linePattern.notNil) {Pen.lineDash_(linePattern)};
47 x.commands.do({ arg cmd; pen.perform(cmd) });
48 y.commands.do({ arg cmd; pen.perform(cmd) });
59 gridColors_ { arg colors;
60 x.gridColor = colors[0];
61 y.gridColor = colors[1];
70 ^DrawGrid(bounds,x.grid,y.grid).x_(x.copy).y_(y.copy).opacity_(opacity).smoothing_(smoothing).linePattern_(linePattern)
81 var <grid,<>range,<>bounds;
82 var <>font,<>fontColor,<>gridColor,<>labelOffset;
83 var commands,cacheKey;
86 ^super.newCopyArgs(grid.asGrid).init
90 range = [grid.spec.minval, grid.spec.maxval];
91 labelOffset = 4 @ -10;
95 range = [grid.spec.minval, grid.spec.maxval];
98 setZoom { arg min,max;
103 if(cacheKey != [range,bounds],{ commands = nil });
105 cacheKey = [range,bounds];
107 p = grid.getParams(range[0],range[1],bounds.left,bounds.right);
108 p['lines'].do { arg val;
112 x = val[0].linlin(range[0],range[1],bounds.left,bounds.right);
113 commands = commands.add( ['strokeColor_',val[1] ? gridColor] );
114 commands = commands.add( ['line', Point( x, bounds.top), Point(x,bounds.bottom) ] );
115 commands = commands.add( ['stroke' ] );
117 if(bounds.width >= 12 ,{
118 commands = commands.add(['font_',font ] );
119 commands = commands.add(['color_',fontColor ] );
120 p['labels'].do { arg val;
122 // value, label, [color, font]
124 commands = commands.add( ['color_',val[2] ] );
127 commands = commands.add( ['font_',val[3] ] );
129 x = val[0].linlin(range[0],range[1],bounds.left,bounds.right);
130 commands = commands.add( ['stringAtPoint', val[1].asString, Point(x, bounds.bottom) + labelOffset ] );
136 clearCache { cacheKey = nil; }
137 copy { ^super.copy.clearCache }
141 DrawGridY : DrawGridX {
144 range = [grid.spec.minval, grid.spec.maxval];
149 if(cacheKey != [range,bounds],{ commands = nil });
153 p = grid.getParams(range[0],range[1],bounds.top,bounds.bottom);
154 p['lines'].do { arg val;
158 y = val[0].linlin(range[0],range[1],bounds.bottom,bounds.top);
159 commands = commands.add( ['strokeColor_',val[1] ? gridColor] );
160 commands = commands.add( ['line', Point( bounds.left,y), Point(bounds.right,y) ] );
161 commands = commands.add( ['stroke' ] );
163 if(bounds.height >= 20 ,{
164 commands = commands.add(['font_',font ] );
165 commands = commands.add(['color_',fontColor ] );
166 p['labels'].do { arg val,i;
168 y = val[0].linlin(range[0],range[1],bounds.bottom,bounds.top);
170 commands = commands.add( ['color_',val[2] ] );
173 commands = commands.add( ['font_',val[3] ] );
175 commands = commands.add( ['stringAtPoint', val[1].asString, Point(bounds.left, y) + labelOffset ] );
184 // DrawGridRadial : DrawGridX {}
192 ^super.newCopyArgs(spec.asSpec)
196 niceNum { arg val,round;
197 // http://books.google.de/books?id=fvA7zLEFWZgC&pg=PA61&lpg=PA61
199 exp = floor(log10(val));
200 f = val / 10.pow(exp);
226 ideals { arg min,max,ntick=5;
227 var nfrac,d,graphmin,graphmax,range,x;
228 range = this.niceNum(max - min,false);
229 d = this.niceNum(range / (ntick - 1),true);
230 graphmin = floor(min / d) * d;
231 graphmax = ceil(max / d) * d;
232 nfrac = max( floor(log10(d)).neg, 0 );
233 ^[graphmin,graphmax,nfrac,d];
235 looseRange { arg min,max,ntick=5;
236 ^this.ideals(min,max).at( [ 0,1] )
238 getParams { |valueMin,valueMax,pixelMin,pixelMax,numTicks|
239 var lines,p,pixRange;
240 var nfrac,d,graphmin,graphmax,range;
241 pixRange = pixelMax - pixelMin;
243 numTicks = (pixRange / 64);
244 numTicks = numTicks.max(3).round(1);
246 # graphmin,graphmax,nfrac,d = this.ideals(valueMin,valueMax,numTicks);
249 forBy(graphmin,graphmax + (0.5*d),d,{ arg tick;
250 if(tick.inclusivelyBetween(valueMin,valueMax),{
251 lines = lines.add( tick );
257 if(pixRange / numTicks > 9) {
258 p['labels'] = lines.collect({ arg val; [val, this.formatLabel(val,nfrac) ] });
262 formatLabel { arg val, numDecimalPlaces;
263 ^val.round( (10**numDecimalPlaces).reciprocal).asString + (spec.units?"")
268 BlankGridLines : GridLines {
277 asGrid { ^BlankGridLines.new }