FluidPlotterPoint { var id, color, <>size = 1; *new { arg id, x, y, color, size = 1; ^super.new.init(id,x,y,color,size); } init { arg id_, x_, y_, color_, size_ = 1; id = id_; x = x_; y = y_; color = color_ ? Color.black; size = size_; } } FluidPlotter : FluidViewer { var (categoryColors.size-1),{ "FluidPlotter:setCategories_ FluidPlotter doesn't have that many category colors. You can use the method 'setColor_' to set colors for individual points.".warn }); color = categoryColors[category_int]; fp_pt.color_(color); }); this.refresh; },{ "FluidPlotter::setCategories_ FluidPlotter cannot receive method \"categories_\". It has no data. First set a dictionary.".warn; }); } pointSize_ { arg identifier, size; identifier = identifier.asSymbol; if(dict_internal.at(identifier).notNil,{ dict_internal.at(identifier).size_(size); this.refresh; },{ "FluidPlotter::pointSize_ identifier not found".warn; }); } addPoint_ { arg identifier, x, y, color, size = 1; identifier = identifier.asSymbol; if(dict_internal.at(identifier).notNil,{ "FluidPlotter::addPoint_ There already exists a point with identifier %. Point not added. Use setPoint_ to overwrite existing points.".format(identifier).warn; },{ this.setPoint_(identifier,x,y,size,color); }); } setPoint_ { arg identifier, x, y, color, size = 1; identifier = identifier.asSymbol; dict_internal.put(identifier,FluidPlotterPoint(identifier,x,y,color ? Color.black,size)); this.refresh; } pointColor_ { arg identifier, color; identifier = identifier.asSymbol; if(dict_internal.at(identifier).notNil,{ dict_internal.at(identifier).color_(color); this.refresh; },{ "FluidPlotter::setColor_ identifier not found".warn; }); } shape_ { arg sh; shape = sh; this.refresh; } background_ { arg bg; userView.background_(bg); } refresh { {userView.refresh}.defer; } pointSizeScale_ { arg ps; pointSizeScale = ps; this.refresh; } dict_ { arg d; if(d.isNil,{^this.dictNotProperlyFormatted}); if(d.size != 2,{^this.dictNotProperlyFormatted}); if(d.at("data").isNil,{^this.dictNotProperlyFormatted}); if(d.at("cols").isNil,{^this.dictNotProperlyFormatted}); if(d.at("cols") != 2,{^this.dictNotProperlyFormatted}); dict = d; dict_internal = Dictionary.new; dict.at("data").keysValuesDo({ arg k, v; dict_internal.put(k.asSymbol,FluidPlotterPoint(k,v[0],v[1],Color.black,1)); }); if(userView.notNil,{ this.refresh; }); } xmin_ { arg val; xmin = val; zoomxmin = xmin; this.refresh; } xmax_ { arg val; xmax = val; zoomxmax = xmax; this.refresh; } ymin_ { arg val; ymin = val; zoomymin = ymin; this.refresh; } ymax_ { arg val; ymax = val; zoomymax = ymax; this.refresh; } highlight_ { arg arr; if(arr.isKindOf(String).or(arr.isKindOf(Symbol)),{arr = [arr]}); highlightIdentifiersArray = arr.collect({arg item; item.asSymbol}); this.refresh; } dictNotProperlyFormatted { "FluidPlotter: The dictionary passed in is not properly formatted.".error; } createPlotWindow { arg bounds,parent_, mouseMoveAction,dict_; var xpos, ypos; var zoomRect = nil; var zoomDragStart = Point(0,0); if(parent_.isNil,{xpos = 0; ypos = 0},{xpos = bounds.left; ypos = bounds.top}); { var reportMouseActivity; parent = parent_ ? Window("FluidPlotter",bounds); userView = UserView(parent,Rect(xpos,ypos,bounds.width,bounds.height)); userView.drawFunc_({ if(dict_internal.notNil,{ dict_internal.keysValuesDo({ arg key, pt; var pointSize_, scaledx, scaledy, color; if(highlightIdentifiersArray.notNil,{ if(highlightIdentifiersArray.includes(key),{ pointSize_ = pointSize * 2.3 * pt.size },{ pointSize_ = pointSize * pt.size }); },{ pointSize_ = pointSize * pt.size; }); pointSize_ = pointSize_ * pointSizeScale; scaledx = pt.x.linlin(zoomxmin,zoomxmax,0,userView.bounds.width,nil); scaledy = pt.y.linlin(zoomymax,zoomymin,0,userView.bounds.height,nil); shape.switch( \square,{Pen.addRect(Rect(scaledx - (pointSize_ /2),scaledy - (pointSize_ /2),pointSize_,pointSize_))}, \circle,{Pen.addOval(Rect(scaledx - (pointSize_ /2),scaledy - (pointSize_ /2),pointSize_,pointSize_))} ); Pen.color_(pt.color); Pen.draw; }); if(zoomRect.notNil,{ Pen.strokeColor_(Color.black); Pen.addRect(zoomRect); Pen.draw(2); }); }); }); reportMouseActivity = { arg view, x, y, modifiers, buttonNumber, clickCount; var realx = x.linlin(pointSize/2,userView.bounds.width-(pointSize/2),zoomxmin,zoomxmax); var realy = y.linlin(pointSize/2,userView.bounds.height-(pointSize/2),zoomymax,zoomymin); mouseMoveAction.(this,realx,realy,modifiers,buttonNumber, clickCount); }; userView.mouseDownAction_({ arg view, x, y, modifiers, buttonNumber, clickCount; case{modifiers == 524288}{ zoomDragStart.x = x; zoomDragStart.y = y; zoomRect = Rect(zoomDragStart.x,zoomDragStart.y,0,0); } {modifiers == 1048576}{ this.resetZoom; } { reportMouseActivity.(this,x,y,modifiers,buttonNumber,clickCount); }; }); userView.mouseMoveAction_({ arg view, x, y, modifiers, buttonNumber, clickCount; if(modifiers == 524288,{ zoomRect = Rect(zoomDragStart.x,zoomDragStart.y,x - zoomDragStart.x,y - zoomDragStart.y); this.refresh; },{ reportMouseActivity.(this,x,y,modifiers,buttonNumber,clickCount); }); }); userView.mouseUpAction_({ arg view, x, y, modifiers, buttonNumber, clickCount; if(zoomRect.notNil,{ var xmin_new, xmax_new, ymin_new, ymax_new; zoomRect = nil; xmin_new = min(x,zoomDragStart.x).linlin(0,userView.bounds.width,zoomxmin,zoomxmax,nil); xmax_new = max(x,zoomDragStart.x).linlin(0,userView.bounds.width,zoomxmin,zoomxmax,nil); // it looks like maybe these are wrong, with max on top and min on bottom, but they are // correct. this accounts for the fact that for the pixels, the lower numbers are higher // in the frame and vice versa, but for the plot values the lower numbers are lower in // the window. ymin_new = max(y,zoomDragStart.y).linlin(userView.bounds.height,0,zoomymin,zoomymax,nil); ymax_new = min(y,zoomDragStart.y).linlin(userView.bounds.height,0,zoomymin,zoomymax,nil); zoomxmin = xmin_new; zoomxmax = xmax_new; zoomymin = ymin_new; zoomymax = ymax_new; this.refresh; }); reportMouseActivity.(this,x,y,modifiers,buttonNumber,clickCount); }); this.background_(Color.white); if(parent_.isNil,{parent.front;}); }.defer; } resetZoom { zoomxmin = xmin; zoomxmax = xmax; zoomymin = ymin; zoomymax = ymax; this.refresh; } post { "xmin: %".format(xmin).postln; "xmax: %".format(xmax).postln; "ymin: %".format(ymin).postln; "ymax: %".format(ymax).postln; "zoomxmin: %".format(zoomxmin).postln; "zoomxmax: %".format(zoomxmax).postln; "zoomymin: %".format(zoomymin).postln; "zoomymax: %".format(zoomymax).postln; } close { parent.close; } }