| -- @atlcompiler atl2006 |
| |
| -- ****************************************************************************** |
| -- Copyright (c) 2006 INRIA. |
| -- All rights reserved. This program and the accompanying materials |
| -- are made available under the terms of the Eclipse Public License v1.0 |
| -- which accompanies this distribution, and is available at |
| -- http://www.eclipse.org/legal/epl-v10.html |
| -- |
| -- Contributors: |
| -- INRIA - Initial implementation |
| -- |
| -- ****************************************************************************** |
| |
| --@name Table2SVG |
| --@version 1.0 |
| --@domains Table, SVG, Pie chart |
| --@authors Eric Vepa (eric.vepa <at> gmail.com) |
| --@date 2006/08/04 |
| --@description This transformation is used to transform generic tables into SVG pie charts. Each entry table is a two columns table (two cells per row). The first row contains two cells with a String (the first cell contains nothing and the second cell, the name of the chart). The other rows contains one cell with a name as String and an other cell with the value as Double. |
| |
| module Table2SVG; -- Module Template |
| create OUT : SVG from IN : Table; |
| |
| --@begin attribute helper radius |
| --@comments radius of the pie chart |
| helper def : radius : Real = 100; |
| --@end attribute helper radius |
| |
| --@begin attribute helper scale |
| --@comments scale of the pie chart |
| helper def : scale : Real = 0.9; |
| --@end attribute helper scale |
| |
| --@begin attribute helper allValidTables |
| --@comments returns all valid tables, ie tables which can be represented as pie chart |
| helper def : allValidTables : Sequence(Table!Table) = |
| Table!Table.allInstances()->select(t|t.rows->first().cells->at(2).content.startsWith('Percentage')) |
| ->sortedBy(t|t.rows->first().cells->at(2).content); |
| --@end attribute helper allValidTables |
| |
| --@begin attribute helper maxSizeName |
| --@comments returns the max size of all name of a table |
| helper def : maxSizeName : Integer = |
| thisModule.allValidTables->iterate(table; max:Integer=0| |
| let maxTable : Integer = |
| table.rows->subSequence(2,table.rows->size())->iterate(row; max:Integer=0| |
| let value : Integer = row.cells->first().content->size() in |
| if value > max |
| then value |
| else max |
| endif)*10*thisModule.scale in |
| if maxTable > max |
| then maxTable |
| else max |
| endif); |
| --@end attribute helper maxSizeName |
| |
| --@begin attribute helper angle |
| --@comments current angle rotation for a sector |
| helper def : angle : Real = 0; |
| --@end attribute helper angle |
| |
| --@begin helper toDegree |
| --@comments converts from radians to degrees |
| helper context Real def : toDegree() : Real = |
| self*(3.1415926535897932/180); |
| --@end helper toDegree |
| |
| --@begin helper radianAngle |
| --@comments converts the angle value, stored in the cell, from radians to degrees |
| helper context Table!Cell def : radianAngle() : Real = |
| (self.content.toReal()*3.6).toDegree(); |
| --@end helper radianAngle |
| |
| --@comments creates the SVG file with one svg tag |
| helper def: svgFile : SVG!SvgFile = OclUndefined; |
| |
| helper def: color : String = '0'; |
| |
| helper def: getNextColorIndex(color : String) : String = |
| if color = '0'then |
| '127' |
| else |
| if color = '127' then |
| '255' |
| else |
| '0' |
| endif |
| endif; |
| |
| --@begin entrypoint rule SvgFile |
| entrypoint rule SvgFile() { |
| to |
| svgFile:SVG!SvgFile ( |
| tag <- svg |
| ), |
| svg:SVG!Svg ( |
| size <- svgSize, |
| namespace <- 'http://www.w3.org/2000/svg', |
| version <- '1.1' |
| ), |
| svgSize:SVG!Dimension ( |
| width <- 2*(thisModule.radius+thisModule.maxSizeName)*thisModule.allValidTables->size()*thisModule.scale, |
| height <- 2*(thisModule.radius+thisModule.maxSizeName)*thisModule.scale |
| ) |
| do { |
| thisModule.svgFile <- svgFile; |
| for (table in thisModule.allValidTables) { |
| thisModule.Table2PieChart(table); |
| } |
| } |
| } |
| --@end entrypoint rule SvgFile |
| |
| --@begin lazy rule Table2PieChart |
| --@comments creates a pie chart (SVG group) for one valid table |
| lazy rule Table2PieChart { |
| from |
| table:Table!Table ( |
| table.rows->first().cells->at(2).content.startsWith('Percentage') |
| ) |
| to |
| g:SVG!G ( |
| attribute <- transl, |
| attribute <- scale, |
| groupContent <- Sequence{rect, text, |
| --@comments creates a complete pie chart for table with one valued row or a sector for each row |
| if table.rows->size() = 2 then |
| thisModule.Row2Circle(table.rows->at(2)) |
| else |
| table.rows->subSequence(2,table.rows->size())->collect(row | thisModule.Row2Sector(row)) |
| endif |
| } |
| ), |
| scale:SVG!Scale ( |
| sx <- thisModule.scale, |
| sy <- scale.sx |
| ), |
| transl:SVG!Translate ( |
| ty <- (thisModule.svgFile.tag.size.height/2)*scale.sx, |
| tx <- transl.ty+(thisModule.allValidTables->indexOf(table)-1)*thisModule.svgFile.tag.size.height*scale.sx |
| ), |
| rect:SVG!Rect ( |
| size <- rectSize, |
| position <- rectCoord, |
| fill <- 'none' |
| ), |
| rectSize:SVG!Dimension ( |
| width <- thisModule.svgFile.tag.size.height-5, |
| height <- rectSize.width |
| ), |
| rectCoord:SVG!AbsoluteCoord ( |
| x <- 0-rectSize.width/2, |
| y <- rectCoord.x |
| ), |
| text:SVG!Text ( |
| position <- txtCoord, |
| stroke <- 'blue', |
| --@comments text-anchor value strored in lengthAdjust attribute |
| lengthAdjust <- 'middle', |
| content <- table.rows->first().cells->at(2).content |
| ), |
| txtCoord:SVG!AbsoluteCoord ( |
| x <- 0, |
| y <- rectSize.height/2-25 |
| ) |
| do { |
| thisModule.svgFile.tag.children <- g; |
| --@comments initialise rotation angle |
| thisModule.angle <- 0; |
| thisModule.previousAngle <- 0; |
| } |
| } |
| --@end lazy rule Table2PieChart |
| |
| --@begin lazy rule Row2Sector |
| --@comments creates a sector (SVG path) for one row |
| lazy rule Row2Sector { |
| from |
| row:Table!Row |
| using { |
| x : Real = thisModule.radius*row.cells->at(2).radianAngle().cos(); |
| y : Real = thisModule.radius*row.cells->at(2).radianAngle().sin(); |
| } |
| to |
| g:SVG!G ( |
| attribute <- Sequence{rotate}, |
| groupContent <- Sequence{sector,text} |
| ), |
| rotate:SVG!Rotate ( |
| angle <- thisModule.angle |
| ), |
| sector:SVG!Path ( |
| d <- 'M0,0 L' + thisModule.radius.toString() + ',0 ' + |
| 'A' + thisModule.radius.toString() + ',' + |
| thisModule.radius.toString() + ' 0 ' + |
| if 3.6*row.cells->at(2).content.toReal() < 180 |
| then '0,1 ' |
| else '1,1 ' |
| endif + x.toString() + ',' + y.toString() + ' z', |
| --@comments rgb color defined with absolute values of angle, cosinus and sinus of the sector |
| fill <- 'rgb(' + thisModule.color + ',' + --rotate.angle.floor().toString() |
| x.abs().floor().toString() + ',' + --x.abs().floor().toString() |
| y.abs().floor().toString() + ')', --y.abs().floor().toString() |
| stroke <- 'black' |
| ), |
| text:SVG!Text ( |
| attribute <- Sequence{textTranslate,textRotate}, |
| position <- coord, |
| stroke <- 'blue', |
| fontSize <- '10', |
| content <- '-- ' + row.cells->first().content |
| ), |
| coord:SVG!AbsoluteCoord ( |
| x <- 0.0, |
| y <- 0.0 |
| ), |
| textTranslate:SVG!Translate ( |
| tx <- thisModule.radius*(row.cells->at(2).radianAngle()/2).cos(), |
| ty <- thisModule.radius*(row.cells->at(2).radianAngle()/2).sin() |
| ), |
| textRotate:SVG!Rotate ( |
| angle <- 70.0 |
| ) |
| do { |
| thisModule.color <- thisModule.getNextColorIndex(thisModule.color); |
| --@comments add the angle value of the new sector |
| thisModule.angle <- rotate.angle + 3.6*row.cells->at(2).content.toReal(); |
| } |
| } |
| --@end lazy rule Row2Sector |
| |
| --@begin lazy rule Row2Circle |
| --@comments creates a complete pie (SVG Circle) for a table containing one row with a value |
| lazy rule Row2Circle { |
| from |
| row:Table!Row |
| to |
| g:SVG!G ( |
| groupContent <- Sequence{circle,text} |
| ), |
| circle:SVG!Circle ( |
| size <- circleSize, |
| position <- circleCoord, |
| fill <- 'green', |
| stroke <- 'black' |
| ), |
| circleSize:SVG!Dimension ( |
| width <- thisModule.radius, |
| height <- thisModule.radius |
| ), |
| circleCoord:SVG!AbsoluteCoord ( |
| x <- 0, |
| y <- 0 |
| ), |
| text:SVG!Text ( |
| position <- coord, |
| stroke <- 'blue', |
| fontSize <- '10', |
| content <- '-- ' + row.cells->first().content |
| ), |
| coord:SVG!AbsoluteCoord ( |
| x <- thisModule.radius, |
| y <- 0 |
| ) |
| } |
| --@end lazy rule Row2Circle |