Implement pie chart
This commit is contained in:
parent
382ba5f87c
commit
86da4c2ec3
|
@ -5,8 +5,9 @@ namespace App\Http\Controllers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Validator;
|
use Validator;
|
||||||
|
|
||||||
use DB;
|
use DB;
|
||||||
|
use Cache;
|
||||||
|
|
||||||
use App\Poll;
|
use App\Poll;
|
||||||
use App\PollVote;
|
use App\PollVote;
|
||||||
use App\PollVotingCode;
|
use App\PollVotingCode;
|
||||||
|
@ -94,14 +95,79 @@ class PollController extends Controller
|
||||||
{
|
{
|
||||||
$voted = $request->session()->pull('voted', false);
|
$voted = $request->session()->pull('voted', false);
|
||||||
|
|
||||||
|
$this->createPieChart($poll);
|
||||||
|
|
||||||
return view('view_poll_results')
|
return view('view_poll_results')
|
||||||
->with('poll', $poll)
|
->with('poll', $poll)
|
||||||
->with('voted', $voted);
|
->with('voted', $voted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function createPieChart(Poll $poll)
|
private function createPieChart(Poll $poll)
|
||||||
{
|
{
|
||||||
//TODO
|
$voteCount = $poll->votes->count();
|
||||||
|
|
||||||
|
if(Cache::has($poll->id) && Cache::get($poll->id)['vote_count'] == $voteCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$baseColours = [[0xE8, 0x96, 0x3F], [0xAD, 0x3F, 0xE8], [0x3F, 0xE8, 0x6F], [0xE8, 0xE3, 0x3F], [0x3F, 0x64, 0xEB], [0xE8, 0x3F, 0x65], [0x3F, 0xE8, 0xDB]];
|
||||||
|
shuffle($baseColours);
|
||||||
|
|
||||||
|
$supersamplingFactor = 8;
|
||||||
|
|
||||||
|
$width = 512 * $supersamplingFactor;
|
||||||
|
$height = 512 * $supersamplingFactor;
|
||||||
|
$padding = 16 * $supersamplingFactor;
|
||||||
|
|
||||||
|
$chartWidth = $width - 2 * $padding;
|
||||||
|
$chartHeight = $height - 2 * $padding;
|
||||||
|
|
||||||
|
$pieChart = imagecreatetruecolor($width, $height);
|
||||||
|
imagefill($pieChart, 0, 0, imagecolorallocate($pieChart, 0xFF, 0xFF, 0xFF));
|
||||||
|
imageantialias($pieChart, true);
|
||||||
|
|
||||||
|
$primary = imagecolorallocate($pieChart, 0xE8, 0x3F, 0xB8);
|
||||||
|
|
||||||
|
$colours = [];
|
||||||
|
|
||||||
|
$startDegrees = 0;
|
||||||
|
$sortedOptions = $poll->options->sortByDesc(function($option) use($poll) { return $poll->votes->where('poll_option_id', $option->id)->count(); });
|
||||||
|
$nonZeroOptions = $sortedOptions->filter(function($option) use($poll) { return $poll->votes->where('poll_option_id', $option->id)->count() > 0; })->values();
|
||||||
|
debug($nonZeroOptions);
|
||||||
|
for($i = 0; $i < $poll->options->count(); $i++) {
|
||||||
|
$option = $nonZeroOptions[$i];
|
||||||
|
|
||||||
|
//TODO: Fix gaps
|
||||||
|
$degrees = round($poll->votes->where('poll_option_id', $option->id)->count() / $voteCount * 360);
|
||||||
|
$endDegrees = min($startDegrees + $degrees, 360);
|
||||||
|
|
||||||
|
$c = function($j) use($i, $baseColours, $nonZeroOptions) {
|
||||||
|
return $baseColours[$i % count($baseColours)][$j]
|
||||||
|
+ (255 - $baseColours[$i % count($baseColours)][$j])
|
||||||
|
* floor($i / count($baseColours)) / (floor($nonZeroOptions->count() / count($baseColours)) + 1);
|
||||||
|
};
|
||||||
|
$colour = imagecolorallocate($pieChart, $c(0), $c(1), $c(2));
|
||||||
|
$colours[$option->id] = '#' . dechex($c(0) << 16 | $c(1) << 8 | $c(2) << 0);
|
||||||
|
|
||||||
|
debug([$option->text, [$startDegrees, $endDegrees], [$c(0), $c(1), $c(2)]]);
|
||||||
|
|
||||||
|
imagefilledarc($pieChart, $width / 2, $height / 2, $chartWidth, $chartHeight, $startDegrees, $endDegrees, $colour, IMG_ARC_PIE);
|
||||||
|
|
||||||
|
$startDegrees = $endDegrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug($colours);
|
||||||
|
|
||||||
|
$resized = imagecreatetruecolor($width / $supersamplingFactor, $height / $supersamplingFactor);
|
||||||
|
imagecopyresampled($resized, $pieChart, 0, 0, 0, 0, $width / $supersamplingFactor, $height / $supersamplingFactor, $width, $height);
|
||||||
|
$pieChart = $resized;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
imagepng($pieChart);
|
||||||
|
$dataUri = "data:image/png;base64," . base64_encode(ob_get_contents());
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
Cache::put($poll->id, ['vote_count' => $voteCount, 'pie_chart' => $dataUri, 'colours' => $colours], now()->addDays(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasVoted(Request $request, Poll $poll)
|
public function hasVoted(Request $request, Poll $poll)
|
||||||
|
|
|
@ -10,12 +10,15 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($poll->results_visible)
|
@if($poll->results_visible)
|
||||||
|
@php ($sortedOptions = $poll->options->sortByDesc(function($option) use($poll) { return $poll->votes->where('poll_option_id', $option->id)->count(); }))
|
||||||
|
@php ($nonZeroOptions = $sortedOptions->filter(function($option) use($poll) { return $poll->votes->where('poll_option_id', $option->id)->count() > 0; }))
|
||||||
|
|
||||||
<section class="grid grid--large some-top-margin">
|
<section class="grid grid--large some-top-margin">
|
||||||
<div class="some-bottom-margin">
|
<div class="some-bottom-margin">
|
||||||
<div class="primary-box">
|
<div class="primary-box">
|
||||||
@php ($total = $poll->votes->count())
|
@php ($total = $poll->votes->count())
|
||||||
|
|
||||||
@foreach ($poll->options->sortByDesc(function($option) use($poll) { return $poll->votes->where('poll_option_id', $option->id)->count(); }) as $option)
|
@foreach ($sortedOptions as $option)
|
||||||
@php ($votes = $poll->votes->where('poll_option_id', $option->id)->count())
|
@php ($votes = $poll->votes->where('poll_option_id', $option->id)->count())
|
||||||
|
|
||||||
<div class="some-more-bottom-margin">
|
<div class="some-more-bottom-margin">
|
||||||
|
@ -24,6 +27,7 @@
|
||||||
<span style="float:right">{{ $votes }} votes</span>
|
<span style="float:right">{{ $votes }} votes</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<!-- TODO: title attribute -->
|
||||||
<div style="background-color:#e83fb8;height:2rem;width:{{ $votes / $total * 100 }}%;">
|
<div style="background-color:#e83fb8;height:2rem;width:{{ $votes / $total * 100 }}%;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,8 +40,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@php ($cache = Cache::get($poll->id))
|
||||||
|
|
||||||
<div class="primary-box">
|
<div class="primary-box">
|
||||||
<span>TODO: Put a pie chart here.</span>
|
<img src="{{ $cache['pie_chart']}}" style="display:block;margin: 0 auto">
|
||||||
|
<br>
|
||||||
|
@foreach ($nonZeroOptions as $option)
|
||||||
|
<!-- TODO: Generate images so this works on text browsers -->
|
||||||
|
<div style="display:inline-block;height:1rem;width:1rem;background-color:{{$cache['colours'][$option->id]}};margin-right:1rem"></div>
|
||||||
|
<span>{{ $option->text }}</span>
|
||||||
|
<br>
|
||||||
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<title>Laravel</title>
|
|
||||||
|
|
||||||
<!-- Fonts -->
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">
|
|
||||||
|
|
||||||
<!-- Styles -->
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #636b6f;
|
|
||||||
font-family: 'Nunito', sans-serif;
|
|
||||||
font-weight: 200;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-height {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.position-ref {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-right {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 84px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links > a {
|
|
||||||
color: #636b6f;
|
|
||||||
padding: 0 25px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: .1rem;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-b-md {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="flex-center position-ref full-height">
|
|
||||||
@if (Route::has('login'))
|
|
||||||
<div class="top-right links">
|
|
||||||
@auth
|
|
||||||
<a href="{{ url('/home') }}">Home</a>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}">Login</a>
|
|
||||||
|
|
||||||
@if (Route::has('register'))
|
|
||||||
<a href="{{ route('register') }}">Register</a>
|
|
||||||
@endif
|
|
||||||
@endauth
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="title m-b-md">
|
|
||||||
Laravel
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="links">
|
|
||||||
<a href="https://laravel.com/docs">Documentation</a>
|
|
||||||
<a href="https://laracasts.com">Laracasts</a>
|
|
||||||
<a href="https://laravel-news.com">News</a>
|
|
||||||
<a href="https://nova.laravel.com">Nova</a>
|
|
||||||
<a href="https://forge.laravel.com">Forge</a>
|
|
||||||
<a href="https://github.com/laravel/laravel">GitHub</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue