DASHBOARD OR BAR-PIE CHARTS

Hey, @RagdogStudios, I’ve incorporated your pie chart in my project and came to a problem on iPad (both 1st gen and 3rd gen iPad).

The pie charts simply go wild and, in some cases, crash my app.  

On simulator, the very same pie charts work perfectly fine.  So far, these pie charts have not failed me on my iPod Touch 5.

So I went ahead and created a simple project with the following, and the pie chart looks quite strange on iPad 1G, iPad 3G and iPod Touch 5:

[lua]

– build.settings

settings =

{

    orientation = 

    {

        default = “landscapeRight”,

        supported =

        {

            “landscapeRight”,“landscapeLeft”,

        },

    },

}

– config.lua

application = 

{

    content = 

    { 

        width = 320,

        height = 480,

        scale = “letterbox”, 

        xAlign = “center”,  – center is the default, so if center is desirable, this line can be removed

        yAlign = “center”,  – center is the default, so if center is desirable, this line can be removed

        antialias = true,  

        imageSuffix =

        {

            ["@2x"] = 1.6,

            ["@4x"] = 3.6,

        },

    },

}

– main.lua

display.setStatusBar( display.HiddenStatusBar )

local ragdogLib = require( “ragdogLib” )

local widget = require( “widget” )

local percent = {}

for i=1,6 do

    percent[i] = 100/6

end

local data = {

    radius = 90,

    values = {

        {percentage = percent[1], color = {0, 0, 1}},

        {percentage = percent[2], color = {.5, 0, 0}},

        {percentage = percent[3], color = {0, 1, 0}},

        {percentage = percent[4], color = {1, 1, 0}},

        {percentage = percent[5], color = {1, 0, 1}},

        {percentage = percent[6], color = {0.5, 0, 1}},

    }

};

local pie = ragdogLib.createPieChart(data);

pie.x, pie.y = display.contentCenterX, display.contentCenterY;

[/lua]

Any idea why this might be?

Naomi

Hey, @RagdogStudios, it looks like the issue boils down to some sort of mask issue – and it may have something to do with dynamic scaling of some sort (but looking at the code, I still don’t understand why it causes this odd problem.)  Regardless, I still really like the createPieChart code you did.  It works very nicely.

So, I’m modifying your library and adapting it to meet my needs.  That is, removing the process of creating & applying mask – and instead, bundling my own mask files (including @2x and @4x versions) and applying it after the slices are created.  I think it will do what I need for my project (and should work on all device resolutions that my app supports.)

Cheers,

Naomi

OMG, it does not like it when I apply @2x or @4x versions of the mask!  It simply fails just like it did with the RagdogStudios library.  Perhaps mask works so long as it uses the same mask file regardless of the screen resolution (meaning, @2x and @4x should not be used at all) – which explains why the dynamically created mask fails with the RagdogStudios library.  

At this point, it seems as if I have no choice but to use a single mask file regardless of screen resolution and live with the fuzzy edges on devices that use @2x or @4x images.  Ugh.

Naomi

P.S.  I suppose I can add a ring (that has the color of the background) right on top of the pie chart to mask the fuzziness… 

Now that Graphics 2.0 is out, I’m wondering if we can make a clean and good looking pie chart.

I tweaked the code that ricardorauber posted here:  http://forums.coronalabs.com/topic/2318-graphs-charts-needed/

But it doesn’t look clean.  I considered masking the whole circle after the pie chart is made (so that the curve of the circle will appear clean), but I can’t fix the jagged lines dividing the pie.  Here’s what I’m seeing at the moment:

[lua]

    local barRadius = 100

    local barWidth  = 2

    local barRotation = 0

    local bars = {}

    for i=1,360 do

        bars[i] = display.newLine(display.contentCenterX, display.contentCenterY, display.contentCenterX-barRadius, display.contentCenterY )

        bars[i]:setColor( 1.0, 1.0, 1.0 )

        bars[i].width = barWidth

        bars[i].rotation = barRotation

        barRotation = barRotation + 1

    end

    for i=1,59 do

        bars[i]:setColor( 1.0, 0, 0 )

    end

    for i=60,119 do

        bars[i]:setColor( 0, 1.0, 0 )

    end

    for i=120,179 do

        bars[i]:setColor( 0, 0, 1.0 )

    end

    for i=180,239 do

        bars[i]:setColor( 1.0, 1.0, 0)

    end

    for i=240,299 do

        bars[i]:setColor( 1.0, 0, 1.0 )

    end

    for i=300,360 do

        bars[i]:setColor( 0, 1.0, 1.0 )

    end

[/lua]

I don’t want to use Google API for charts (assuming it requires internet connectivity to work.)

Bar charts (and probably line graphs too) are easy to make, but I don’t seem to be able to generate good pie chart.

Are there other ways to generate a clean, beautiful looking pie chart?  I really don’t need anything special.  Just a simple, 2D pie chart.  Has anyone come up with code for creating a really nice pie chart?  I’d so like to hear and learn about it.

Naomi

Actually, maybe I’ll create an image (a simple white line) that would be placed on top of where pies are divided, plus the circle mask thingy to smooth the pie chart.  It’s a bit convoluted solution, but it may still do the job…

That said, if there are better ways to generate a good looking pie chart, without having to layer some helper png files on top of the chart to make it look better, I’d love to hear about it.

Naomi

This looks interesting.Tonight I’ll try giving it a go.
But basically what I have in mind would be to make the chart out of triangles (point at the center of our “circle”, and radius and color of it based on the % of that specific value), and then use our dynamic masking library to always get a smooth circle no matter the device resolution without directly preparing png and masks.

I got it to work tolerably, but there’s some sort of anti-aliasing issue.

I use simple white strip of image (90 pixels wide and 3 pixels high) to be applied as the divider, but some of them appear jagged, when the actually image does not have anything jagged about it.

I think it’s a Corona bug for rendering straight line when it is slightly angled.  If this anti-aliasing issue is fixed, I should be able to have a usable pie chart.  (In fact, if the anti-aliasing issue is not there, I probably don’t even need to apply image files to smooth out the lines and curves.)

I’m submitting it as a bug.  Hopefully I’ll hear back from someone.

Naomi

Hey, @hachisoft, thanks for sharing your thoughts.  I like your idea of putting together the pie chart with triangles and a mask, instead of doing the bazillions of lines with the mask.  I might give it a go too.

That said, the dividing line with anti-aliasing issue would probably still exist.  It is quite apparent when the pie is divided into 6 equal sizes.  This needs to be fixed for the pie-chart to look acceptable.

Naomi

@Naomi, in that case maybe we can save a snapshot of the triangles before masking them, and apply a very slight blur effect on the resulting texture? It should alleviate the aliasing, and then the mask would do the rest.

@hachisoft, that’s an interesting idea.  But… I’m not sure if I want to do the snapshot and apply some blur effect.  It feels like a lot of process for something that should be simple.  And I’m also not sure if blur effect would make difference.  I suppose I could try applying some  blur effects to the divider, which is a PNG file of white strip that sits on top of the pie chart, making it look like it’s dividing the pie, and see if it makes any difference.

Naomi

Okay so I gave it a go.
This is the function that integrates with our dynamic masks lib. (Get it and copy and paste this code into it).

ragdogLib.createPieChart = function(data) local group = display.newGroup(); local values = data.values; local mSin, mCos = math.sin, math.cos; local toRad = math.pi/180; local currAngle = -90; for i = 1, 3 do local newAngle = currAngle+values[i].percentage\*360\*0.01; local midAngle = currAngle+(newAngle-currAngle)\*.5; local shape = {0, 0, mCos(currAngle\*toRad)\*data.radius\*1.5, mSin(currAngle\*toRad)\*data.radius\*1.5, mCos(midAngle\*toRad)\*data.radius\*1.5, mSin(midAngle\*toRad)\*data.radius\*1.5, mCos(newAngle\*toRad)\*data.radius\*1.5, mSin(newAngle\*toRad)\*data.radius\*1.5}; currAngle = newAngle; local slice = display.newPolygon(group, 0, 0, shape); slice:setFillColor(unpack(values[i].color)); slice:setStrokeColor(1, 1, 1, .05); local lowerPointX, higherPointX, lowerPointY, higherPointY = 10000, -10000, 10000, -10000; for i = 1, #shape, 2 do if shape[i] \< lowerPointX then lowerPointX = shape[i]; end if shape[i] \> higherPointX then higherPointX = shape[i]; end if shape[i+1] \< lowerPointY then lowerPointY = shape[i+1]; end if shape[i+1] \> higherPointY then higherPointY = shape[i+1]; end end slice.x = lowerPointX+(higherPointX-lowerPointX)\*.5; slice.y = lowerPointY+(higherPointY-lowerPointY)\*.5; end group.x, group.y = display.contentCenterX, display.contentCenterY; display.save(group, data.name or "pie.png", true); local width, height = group.contentWidth, group.contentHeight; group:removeSelf(); group = display.newGroup(); local pie = display.newImageRect(group, data.name or "pie.png", system.DocumentsDirectory, width, height); local circle = display.newCircle(0, 0, data.radius) ragdogLib.applyMaskFromPolygon(group, circle); return group; end

Then to use it:
 

data = { radius = 50, name = "myPieChart.png" values = { {percentage = 30, color = {1, 1, 1}}, {percentage = 45, color = {1, 0, 0}}, {percentage = 25, color = {0, 1, 0}}, } }; --Put it in a timer ONLY if you want to do this at the very start of the app. In any other case, you can just call it without timer timer.performWithDelay(150, function() local pie = ragdogLib.createPieChart(data); pie.x, pie.y = display.contentCenterX, display.contentCenterY; end, 1);

It works as expected. Just make sure that all the percentages adds up to 100.
@Naomi, as you stated there’s aliasing. The aliasing around the circle is easily fixable, just use an image instead of our dynamic masks (the dynamic masking uses shapes, so it keeps their aliasing). For the lines, we tried with gaussian blur but the minimum setting is just too high for this purpose. Without using shapes but images at the moment nothing pops to mind, although that would surely fix the issue once and for all (:

Also in the function we’re saving the resulting shapes into a png, load it, and apply the mask on it. This last step was done to play around with the shapes as a single texture, but can be completely skipped (just remove from display.save to local circle).

Cool.  @hachisoft, thanks for sharing!

About the anti-aliasing issue, even with an image file (I used PNG file), it still doesn’t work – meaning, slightly angled display object showing a PNG image of a thin rectangle ends up with jagged edges…  And this happens even if we set antialias to true in config.lua

Naomi

Maybe instead of a full white rectangle, the .png can contain an AA already. So for example using a 45° line (with proper AA), instead of the 0° or 90° ones. That way we could skip the AA process (or lack of).
Not sure I’ll have time to improve on the code, but should be pretty performant to leave room for messing around a bit (it creates a single object for each slice).

Ah, that is an interesting concept.  It feels a bit strange to prepare hard baked lines in all sorts of angle instead of just supplying a single image of a line to be programmatically rotated, but if that’s what it takes, I suppose that’s what needs to be done.

Naomi

I think I’d have to scrap this idea.  I’d need to create 180 pieces of line images to accommodate this.  Otherwise, I won’t be able to capture every angle I’d need.  But then, this feels like a giant waste of texture memory…  Is this really what it takes to create a good looking pie chart with Corona?

Naomi

Actually, I meant to use a single png with a 45° line (top point in the top left corner of the canvas, bottom one in the bottom right corner), set the anchor points to 0, 0 (top left), and then after having positioned it at 0, 0 of the circle, rotate it.
This way it should still keep it’s AA a bit (or at least it’s worth a try), and still load only one image.
Another way I thought again to achieve this using images is

-Get a white circle on a transparent background
-Get a mask that is half black, half white, with the central line antialiased
-Create two circles for each slice you need, and colorize them
-Apply the mask to each circle, rotate it so that from the first circle it takes out the desired portion (would be currAngle in my code)
-If the % is higher than 50%, take the second circle, apply the mask, and rotate it so that it reaches the second angle (newAngle in my code)
-If the % is lower than 50%, do a display save of the current circle, delete it, load the new texture, and again apply the mask and rotate it so that it removes everything after the second angle (always newAngle in my code)
-Repeat for all slices needed

This would 99% remove all issues of AA, using just 2 file (the circle and the mask), and the end result would use a texture for each slice (which is not too bad). Also it should be able to do that quite fast, unless there aren’t 100 slices needed. Consider also that business apps rarely need particular care for performance (apart in slide views or if they have intense animations).
 

Hey, @hachisoft, thanks for sharing your thoughts.

You know, I fooled around with the 360 lines that make up the pie chart, and I decided I’d go with it.  My pie chart will sometimes need a single piece, other times, two or three… up to six pieces.  I kind of feel I can use the line version with the flexibility I want without worrying too much about dynamically adjusting the pie chart based on how many pieces there are or how big /small each piece is going to be. 

And… when I set the barWidth to 1, the chart kind of look funky, and perhaps it can pass as intentionally stylized design – and the jaggedness from the dividers doesn’t feel so strikingly horrid when placed along with the pattern the chart creates.

So long as the device build doesn’t have any performance issue with the pie chart, I think I’ll stick with it.  (And if Corona fixes the anti-alias issue, I can simply widen the barWidth back to 2 and return the pie chart to look more normal.)

Naomi

Final note:  I decided not to use display.newLine to create lines but instead use display.newImageRect, showing a PNG image of a white strip.  I then apply color via setFillColor for each pie pieces and add divider of white using the same PNG file.  This also makes circle mask unnecessary for the pie chart.  Overall, this seems to give me the most flexibility I like.  Cheers.

@Naomi, I solved the antialiasing issue with no performance loss (actually I think it’s faster :smiley: ) and made a blog post about it (; 
It kinda bugged me and fortunately I didn’t had to rewrite basically anything of the previous code (;

Hey, @hachisoft, this is awesome!  I’m so glad you made it work so beautifully.

Thank you!!

Naomi

Oh, by the way, @hachisoft, I’m assuming you’re okay with me adding your ragdogLib.lua along with your brush.png in my commercial product.  Please confirm.

Thanks again!

Naomi