|
|
|
Graph Class
Added on 5/18/2004
|
A parent script for creating line graphs, bar graphs and pie charts with imaging lingo. Usefull for visualizing dynamic data e.g. from local or online databases.
homepage: http://staff.dasdeck.de/valentin/lingo/graph/
Download PC Source
property pImg
property pXOffset
property pYOffset
property pW
property pH
property pLeftMarg
property pRightMarg
property pTopMarg
property pBottomMarg
property pMinY
property pMaxY
property pFont
property pFontsize
property pTextCol
property pshadeCol
----------------------------------------
-- PUBLIC METHODS
----------------------------------------
----------------------------------------
-- optional: bgCol=background-color of canvas, shadeCol=color of shades
----------------------------------------
on new (me, img, bgCol, shadeCol)
if not ilk(bgCol)=#color then bgCol = rgb(255,255,255)
pImg = img
pImg.fill(pImg.rect, bgCol) -- clear
-- defaults
pXOffset = 0
pYOffset = 0
pW=pImg.width
pH=pImg.height
pMinY = 0
pMaxY = 100
pLeftMarg = 0
pRightMarg = 0
pTopMarg = 0
pBottomMarg = 0
pFont = "Courier New"
pFontsize = 11
pTextCol = rgb(0, 0, 0)
if ilk(shadeCol)=#color then pshadeCol = shadeCol
else pshadeCol = rgb(80,80,80)
return me
end
----------------------------------------
-- set left and top coordinate of graph
----------------------------------------
on setOffset (me, x, y)
pXOffset = x
pYOffset = y
end
----------------------------------------
-- set width and height of graph
----------------------------------------
on setDimensions (me, w, h)
pW = w
pH = h
end
----------------------------------------
-- set left and right margin within graph
----------------------------------------
on setMarginsX (me, leftMarg, rightMarg)
if leftMarg>0 then pLeftMarg = leftMarg
if rightMarg>0 then pRightMarg = rightMarg
end
----------------------------------------
-- set top and bottom margin within graph
----------------------------------------
on setMarginsY (me, topMarg, bottomMarg)
if topMarg>0 then pTopMarg = topMarg
if bottomMarg>0 then pBottomMarg = bottomMarg
end
----------------------------------------
-- set visible range of values
----------------------------------------
on setRange (me, minY, maxY)
pMinY = minY
pMaxY = maxY
end
----------------------------------------
-- add box arround rect specified by offset and dimensions
----------------------------------------
on addBox (me)
canvasRect = rect(pXOffset, pYOffset, pXOffset+pW, pYOffset+pH)
pImg.draw(canvasRect, [#shapeType:#rect, #lineSize:1, #color: rgb(0, 0, 0)])
end
----------------------------------------
-- add title on top of graph
----------------------------------------
on addTitle (me, str, props) -- optional propList props: font, fontsize, color, fontstyle, antialias
if voidP(props) then props=[:]
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
if voidP(props["font"]) then tm.font = pFont
else tm.font = props["font"]
if voidP(props["fontsize"]) then tm.fontsize = pFontsize
else tm.fontsize = props["fontsize"]
if voidP(props["color"]) then tm.color = pTextCol
else tm.color = props["color"]
if voidP(props["fontstyle"]) then tm.fontstyle = [#plain]
else tm.fontstyle = props["fontstyle"]
if (props["antialias"]=1) then
tm.antiAliasThreshold = 0
tm.antiAlias = TRUE
else
tm.antiAlias = FALSE
end if
tm.text = str
img = tm.image
p = tm.charPosToLoc(length(str)+1)
x = pXOffset + pW/2 - p[1]/2
y = pYOffset/2 - p[2]/2
pImg.copyPixels(img, img.rect.offset(x,y), img.rect, [#ink: 36])
tm.erase()
end
----------------------------------------
-- add title of x-axis
----------------------------------------
on addAxisTitleX (me, str, props) -- optional proplist props: font, fontsize, color, fontstyle, antialias
if voidP(props) then props=[:]
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
if voidP(props["font"]) then tm.font = pFont
else tm.font = props["font"]
if voidP(props["fontsize"]) then tm.fontsize = pFontsize
else tm.fontsize = props["fontsize"]
if voidP(props["color"]) then tm.color = pTextCol
else tm.color = props["color"]
if voidP(props["fontstyle"]) then tm.fontstyle = [#plain]
else tm.fontstyle = props["fontstyle"]
if (props["antialias"]=1) then
tm.antiAliasThreshold = 0
tm.antiAlias = TRUE
else
tm.antiAlias = FALSE
end if
tm.text = str
img = tm.image
p = tm.charPosToLoc(length(str)+1)
x = pXOffset + pW/2 - p[1]/2
y = pYOffset + pH + 25
pImg.copyPixels(img, img.rect.offset(x,y), img.rect, [#ink: 36])
tm.erase()
end
----------------------------------------
-- add title of y-axis
----------------------------------------
on addAxisTitleY (me, str, props) -- optional proplist props: font, fontsize, color, fontstyle, antialias
if voidP(props) then props=[:]
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
if voidP(props["font"]) then tm.font = pFont
else tm.font = props["font"]
if voidP(props["fontsize"]) then tm.fontsize = pFontsize
else tm.fontsize = props["fontsize"]
if voidP(props["color"]) then tm.color = pTextCol
else tm.color = props["color"]
if voidP(props["fontstyle"]) then tm.fontstyle = [#plain]
else tm.fontstyle = props["fontstyle"]
if (props["antialias"]=1) then
tm.antiAliasThreshold = 0
tm.antiAlias = TRUE
else
tm.antiAlias = FALSE
end if
tm.text = str
tm.antiAliasThreshold = 10
tm.antiAlias = TRUE
p = tm.charPosToLoc(length(str)+1)
tm.width = p[1]+1
img = rotateImg(tm.image, PI*3/2)
x = 10 --pXOffset + pW/2 - p[1]/2
y = pYOffset + pH/2 - p[1]/2
pImg.copyPixels(img, img.rect.offset(x,y), img.rect, [#ink: 36])
tm.erase()
end
----------------------------------------
-- create grid, specified by number of horizontal and vertical sections (affected by margins)
----------------------------------------
on addGrid (me, cntX, cntY)
if cntX >0 then
dx=(pW-pLeftMarg-pRightMarg)/cntX
von = (pLeftMarg=0)
bis = cntX-(pRightMarg=0)
repeat with i = von to bis
x=pXOffset+pLeftMarg+i*dx
pImg.draw(point(x,pYOffset), point(x,pYOffset+pH), [#shapeType:#line, #lineSize:1, #color: rgb(180,180,180)])
end repeat
end if
if cntY>0 then
dy=(pH-pTopMarg-pBottomMarg)/cntY
von = (pTopMarg=0)
bis = cntY-(pBottomMarg=0)
repeat with i = von to bis
--repeat with i = 1 to cntY-1
y=pYOffset+pTopMarg+i*dy
pImg.draw(point(pXOffset+1,y), point(pXOffset+pW-1,y), [#shapeType:#line, #lineSize:1, #color: rgb(180,180,180)])
end repeat
end if
end
----------------------------------------
-- add labels to x-axis (affected by margins)
----------------------------------------
on addLabelsX (me, labels)
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
tm.font = pFont
tm.fontsize = pFontsize
tm.color = pTextCol
cnt=count(labels)
dx=(pW-pLeftMarg-pRightMarg)/(cnt-1)
y=pYOffset+pH+4
repeat with i = 1 to cnt
str=labels[i]
tm.text = str
img = tm.image
p = tm.charPosToLoc(length(str)+1)
x=pXOffset+pLeftMarg+(i-1)*dx - p[1]/2
pImg.copyPixels(img, img.rect.offset(x,y), img.rect, [#ink: 36])
end repeat
tm.erase()
end
----------------------------------------
-- add labels to y-axis (affected by margins)
----------------------------------------
on addLabelsY (me, labels)
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
tm.font = pFont
tm.fontsize = pFontsize
tm.color = pTextCol
cnt=count(labels)
dy=(pH-pTopMarg-pBottomMarg)/(cnt-1)
x=pXOffset-5
repeat with i = 1 to cnt
str=labels[cnt+1-i]
tm.text = str
img = tm.image
p = tm.charPosToLoc(length(str)+1)
y=pYOffset+pTopMarg+(i-1)*dy - p[2]/2
pImg.copyPixels(img, img.rect.offset(x-p[1],y), img.rect, [#ink: 36])
end repeat
tm.erase()
end
----------------------------------------
-- add line with specified color to line-graph
----------------------------------------
on drawLines (me, data, aColor, props) -- optional proplist props: strokewidth, shade, shadeoffset, blur
if voidP(props) then props=[:]
cnt=count(data)
w = pW-pLeftMarg-pRightMarg
h = pH - pTopMarg - pBottomMarg
dx = w/(cnt-1)
scaleY = float(h)/(pMaxY-pMinY)
l=[]
repeat with i = 0 to cnt-1
x=i*dx
y=(h-data[i+1])*scaleY
l.add([#vertex: point(x,y)])
end repeat
vm = new (#vectorShape)
vm.vertexList = l
vm.backgroundcolor = rgb(255, 255, 255)
if voidP(props["strokewidth"]) then sw = 1
else sw = props["strokewidth"]
vm.strokeWidth = sw
-- add shade
if (props["shade"]) then
vm.strokeColor = pshadeCol
if voidP(props["shadeoffset"]) then shadeOffset = 1
else shadeOffset = props["shadeoffset"]
blur = props["blur"]
if blur then img=blur(vm.image,blur,1)
pImg.copyPixels(img, vm.image.rect.offset(pXOffset+pLeftMarg+shadeOffset,pYOffset+pTopMarg-sw+shadeOffset), vm.image.rect, [#ink: 39])
end if
-- add line
vm.strokeColor = aColor
pImg.copyPixels(vm.image, vm.image.rect.offset(pXOffset+pLeftMarg,pYOffset+pTopMarg-sw), vm.image.rect, [#ink: 36])
vm.erase()
end
----------------------------------------
-- add bars with specified values, color, width and offset to bar-graph
----------------------------------------
on drawBar (me, data, aColor, barWidth, horOffset, props) -- optional proplist props: linesize, shade, shadeoffset, blur
if voidP(props) then props=[:]
if voidP(barWidth) then barWidth=15
if voidP(horOffset) then horOffset=0
cnt = count(data)
--w = pW-pLeftMarg-pRightMarg
h = pH - pTopMarg - pBottomMarg
scaleY = float(h)/(pMaxY-pMinY)
dx = (pW-pLeftMarg-pRightMarg)/(cnt-1)
y0 = pYOffset+pH-pBottomMarg
if voidP(props["linesize"]) then ls = 1
else ls = props["linesize"]
if (props["shade"]) then
if voidP(props["shadeoffset"]) then shadeOffset = 2
else shadeOffset = props["shadeoffset"]
blur = props["blur"]
end if
l=[]
repeat with i = 0 to cnt-1
x=pXOffset+pLeftMarg+i*dx+horOffset
y=y0 - (data[i+1]-pMinY)*scaleY
if y
-- add shade
if (props["shade"]) then
if voidP(props["shadeoffset"]) then shadeOffset = 2
else shadeOffset = props["shadeoffset"]
if blur then
img=image(barWidth+10,y0-y,24)
img.fill(5,shadeOffset,barWidth+5,y0-y, [#shapeType: #rect, #lineSize: 0, #color: pshadeCol, #bgColor: rgb(0, 0, 0)])
img=blur(img,blur,1)
pImg.copyPixels(img, rect(x+shadeOffset-5,y,x+barWidth+shadeOffset+5,y0),img.rect,[#ink: 39]) --+shadeOffset-5
else
pImg.fill(x+shadeOffset,y+shadeOffset,x+barWidth+shadeOffset,y0, [#shapeType: #rect, #lineSize: 0, #color: pshadeCol, #bgColor: rgb(0, 0, 0)])
end if
end if
pImg.fill(x,y,x+barWidth,y0-(ls=0), [#shapeType: #rect, #lineSize: ls, #color: aColor, #bgColor: rgb(0, 0, 0)])
else
-- add shade
if (props["shade"]) then
if voidP(props["shadeoffset"]) then shadeOffset = 2
else shadeOffset = props["shadeoffset"]
if blur then
img=image(barWidth+10,y-y0+shadeOffset+5,24)
img.fill(5,0,barWidth+5,y-y0+shadeOffset+5, [#shapeType: #rect, #lineSize: 0, #color: pshadeCol, #bgColor: rgb(0, 0, 0)])
img=blur(img,blur,1)
pImg.copyPixels(img, rect(x+shadeOffset-5,y0,x+barWidth+shadeOffset+5,y+shadeOffset),img.rect,[#ink: 39])
else
pImg.fill(x+shadeOffset,y0,x+barWidth+shadeOffset,y+shadeOffset, [#shapeType: #rect, #lineSize: 0, #color: pshadeCol, #bgColor: rgb(0, 0, 0)])
end if
end if
pImg.fill(x,y0-(ls>0),x+barWidth,y, [#shapeType: #rect, #lineSize: ls, #color: aColor, #bgColor: rgb(0, 0, 0)])
end if
end repeat
end
----------------------------------------
-- draw pie for specified values, labels and colors
----------------------------------------
on drawPie (me, data, aLabelList, aColorList, props) -- optional proplist props: linesize, shade, shadeoffset, blur
-- normalize to 2*PI = 100%
sum=0
repeat with d in data
sum=sum+d
end repeat
data = data * 2*PI/sum
if voidP(props["linesize"]) then ls = 1
else ls = props["linesize"]
img=image(pW,pH,24)
w=pW-pLeftMarg-pRightMarg
h=pH-pTopMarg-pBottomMarg
cx=w/2
cy=h/2
if (props["shade"]) then
if voidP(props["shadeoffset"]) then shadeOffset = 2
else shadeOffset = props["shadeoffset"]
blur = props["blur"]
if blur then
sImg=image(w+2*blur,h+2*blur,24)
sImg.fill(blur,blur,w+blur,h+blur,[#shapeType: #oval, #lineSize: 0, #color: pshadeCol])
sImg=blur(sImg,blur,1)
pImg.copyPixels(sImg,sImg.rect.offset(pXOffset+pLeftMarg+shadeOffset-blur,pYOffset+pTopMarg+shadeOffset-blur),sImg.rect)
else
pImg.fill(pXOffset+shadeOffset,pYOffset+shadeOffset,pXoffset+pW+shadeOffset,pYOffset+pH+shadeOffset,[#shapeType: #oval, #lineSize: 0, #color: pshadeCol])
end if
end if
-- draw circle
img.draw(0,0,w,h,[#shapeType: #oval, #lineSize: ls, #color: rgb(0,0,0)])
-- paint arcs
a = PI
cnt = count(data)
repeat with i = 1 to cnt
img.draw(cx,cy,cx+cx*sin(a),cy+cy*cos(a),[#shapeType: #line, #lineSize: ls, #color: rgb(0,0,0)])
img.floodfill(cx+cx*.9*sin(a+5),cy+cy*.9*cos(a+5),aColorList[i])
a=a-data[i]
end repeat
pImg.copyPixels(img,img.rect.offset(pXOffset+pLeftMarg,pYOffset+pTopMarg),img.rect,[#ink:36])
-- addLabels
tm = new (#text)
tm.bgcolor = rgb(255,255,255)
tm.font = pFont
tm.fontsize = pFontsize
tm.color = pTextCol
repeat with i = 1 to cnt
x=325
y=50+15*(i-1)
y=250-10-15*(cnt-i)
pImg.fill(x,y,x+10,y+10, [#shapeType: #rect, #lineSize: 1, #color: aColorList[i], #bgColor: rgb(0, 0, 0)])
tm.text = aLabelList[i]
img = tm.image
p = tm.charPosToLoc(1)
pImg.copyPixels(img, img.rect.offset(x+15,y+4-p[2]/2), img.rect, [#ink: 36])
end repeat
tm.erase()
end
----------------------------------------
-- PRIVATE UTILITIES
----------------------------------------
----------------------------------------
-- rotate image
----------------------------------------
on rotateImg img, deg --, tws
--deg = (pi()/180) * deg
p1 = point((img.rect.width/2.0), (img.rect.height)/2.0)
laenge = sqrt(float(p1[1] * p1[1]) + float(p1[2] * p1[2]))
rad = deg + atan(float(p1[2]),float(p1[1]))
newP1 = point((cos(rad) * laenge),(sin(rad) * laenge))
newP3 = newP1 * -1
p2 = p1 * point(1, -1)
rad = deg + atan(float(p2[2]),float(p2[1]))
newP2 = point((cos(rad) * laenge),(sin(rad) * laenge))
newP4 = newP2 * -1
breite = max(abs(newP1[1]), abs(newP2[1]))
hoehe = max(abs(newP1[2]), abs(newP2[2]))
offs = point(breite, hoehe)
newP1 = newP1 + offs
newP2 = newP2 + offs
newP3 = newP3 + offs
newP4 = newP4 + offs
temp = image(breite * 2, hoehe * 2, img.depth, 0)
temp.copyPixels(img, [newP3, newP2, newP1, newP4], img.rect)
--if tws then return temp.trimwhitespace()
--else
return temp
end
----------------------------------------
-- blur image
----------------------------------------
on blur (startImg, repetitions, doCrop)
img = startImg
repeat with i = 1 to repetitions
img=_blur(img, doCrop)
end repeat
return img
end
----------------------------------------
-- ACTION : apply a 5x5 convolution matrix and return the resulting image
-- INPUTS : startImg : #image, the image on which to do the 5x5 blur
-- doCrop {optional} : #boolean, choose if the new image is
-- the same size as the original one (TRUE) or bigger
-- (FALSE by default)
-- RETURN : #image if everything's all right
-- VOID if startImg is not an image
----------------------------------------
on _blur (startImg, doCrop)
-- 1 - check input
if (ilk(startImg) <> #image) then return VOID
-- 2 - initialization
buffer = image( startImg.width+4, startImg.height+4, 24 )
myRect = rect( 0, 0, startImg.width, startImg.height )
-- 3.1 - declaration of offset & blend lists
offsetL = [[4,4],[4,0],[0,4],[0,0],[3,4],[3,0],[1,4],[4,3],[4,1],[0,3],[1,0],[0,1],[2,0],[2,4],[0,2],[4,2],[3,3],[1,1],[3,1],[1,3],[3,2],[1,2],[2,3],[2,1],[2,2]]
blendLL = [0,0,0,0,3,3,3,3,3,3,3,3,10,10,10,10,16,16,16,16,26,26,26,26,26]
-- 3.2 - blur
repeat with j = 1 to 25 -- = offsetL.count
myBlend = blendLL[j] * 1.8
-- 1.8 = luminosity correction, from 1.5 to 5
if not myBlend then next repeat
destRect = myRect.offset(offsetL[j][1], offsetL[j][2])
buffer.copyPixels(startImg, destRect, myRect, [#blendLevel : myBlend])
end repeat
-- 4 - return the result
if doCrop then
-- crop the results
return buffer.duplicate().crop(myRect.offset(2, 2))
else
-- do not crop
return buffer.duplicate()
end if
end
|
|