Object subclass: #LogPlotter
instanceVariableNames: 'name data form canvas '
classVariableNames: ''
poolDictionaries: ''
category: 'Ward-Plotting'
Draw as gif images Excel style charts of tabbed datasets read from text files.
(c) 2002, Ward Cunningham
Authors note: I wrote this code in a style I might use in Perl or Python. For example,
this script-like code reads and writes files within the scope of a web server and
cooperates with a cgi script. I found this lead me to make factoring decisions slightly
differently than if the objects were to become a permanent part of my Smalltalk world.
Even so, the coding style was effortless and should be considered a growth area for Squeak.
See
SerializationThroughtput Additional Results.
LogPlotter methodsFor: 'input/output'
display
form display
read
| in out |
in _ CrLfFileStream readOnlyFileNamed: name.
out _ WriteStream on: Array new.
[in atEnd] whileFalse:
[out nextPut:
(#(tab tab tab tab cr) collect:
[:each | in upTo: (Character perform: each)])].
data _ out contents
save
GIFReadWriter
putForm: form
onFileNamed: self imageName
LogPlotter methodsFor: 'plotting'
axis
| a b label |
label _ self displayText: self runName.
label align: label boundingBox bottomCenter with: self area topCenter.
label displayOn: form.
self xTics do:
[:each |
a _ self scale: (each @ self yTics first).
b _ self scale: (each @ self yTics last).
canvas line: a to: b width: 1 color: self gray.
label _ self label: each.
label align: label boundingBox topCenter with: a.
label displayOn: form].
self yTics do:
[:each |
a _ self scale: (self xTics first @ each).
b _ self scale: (self xTics last @ each).
canvas line: a to: b width: 1 color: self gray.
label _ self label: each.
label align: label boundingBox rightCenter with: -4@0 + a.
label displayOn: form].
form border: self area width: 1.
line: columnNumber color: aColor
| xData yData to from dot transparentColor label |
dot _ Form dotOfSize: 5.
xData _ self sequence: 1.
yData _ self sequence: columnNumber.
transparentColor _ aColor alpha: 0.2.
from _ nil.
xData with: yData do:
[:x :y |
to _ self scale: x log @ y log.
from notNil
ifTrue: [canvas line: from to: to width: 3 color: transparentColor].
form fillShape: dot fillColor: aColor at: to.
from _ to.].
label _ self displayText: (self title: columnNumber).
to _ 2 @ columnNumber * label height + self area origin.
canvas line: -10@0+to to: 10@0+to width: 3 color: transparentColor.
form fillShape: dot fillColor: aColor at: to.
label align: label boundingBox leftCenter with: 20@0 + to.
label displayOn: form.
plot
form _ Form extent: 640@480 depth: 16.
canvas _ form getCanvas.
canvas fillColor: Color white.
self axis.
self line: 3 color: Color red.
self line: 4 color: Color green.
self line: 5 color: Color brown.
LogPlotter methodsFor: 'support'
area
^form boundingBox insetBy:
(55@20 corner: 20@20)
displayText: aString
| displayText |
displayText _ aString asDisplayText.
displayText foregroundColor: Color gray backgroundColor: Color white.
^displayText
gray
| gray |
gray _ 0.8.
^Color r: gray g: gray b: gray
imageName
^(name allButLast: 3), 'gif'
label: anInteger
| value |
value _ (anInteger >= 0)
ifTrue: [10 raisedToInteger: anInteger]
ifFalse: [(10 raisedToInteger: anInteger) asFloat].
^self displayText: value asString
runName
| tokens |
tokens _ (name findTokens: '\') last: 2.
^(tokens first, ' -- ', tokens last) allButLast: 4
scale: aPoint
| scale |
"linear to area"
scale _ (self area width - 1 / (self xTics last - self xTics first )) @
(self area height - 1 / (self yTics last - self yTics first)).
^(aPoint x - self xTics first * scale x + self area origin x) floor @
(self area corner y - (aPoint y - self yTics first * scale y) - 1) floor
sequence: columnNumber
^data allButFirst collect:
[:each | Number readFrom:
(each at: columnNumber)]
setName: aString
name _ aString.
title: columnNumber
^data first at: columnNumber
xTics
^0 to: 5
yTics
^-3 to: 7
LogPlotter class methodsFor: 'processing'
on: aString
^self new setName: aString
processDirectory: aString
| aDirectory |
"self processDirectory: 'C:\My Documents\Squeak\TestData'"
"self processDirectory: '\\C2\ward\web\doc\SerializationThroughput\contrib\data'"
aDirectory _ FileDirectory on: aString.
(aDirectory fileNamesMatching: '*.txt') do:
[:each | each ~= 'email.txt' ifTrue: [self processFile: (aDirectory fullNameFor: each)]].
aDirectory directoryNames do:
[:each | self processDirectory: (aDirectory fullNameFor: each)].
processFile: aString
"self processFile: 'TestData\bk.txt'"
(self on: aString)
read;
plot;
display;
save