| rev | 
   line source | 
| 
bsw@1045
 | 
     1 function util.initiative_pie(initiative, d, gap)
 | 
| 
bsw@1045
 | 
     2   if not initiative.issue.closed or not initiative.admitted then
 | 
| 
bsw@1045
 | 
     3     return
 | 
| 
bsw@1045
 | 
     4   end
 | 
| 
bsw@1045
 | 
     5   
 | 
| 
bsw@1045
 | 
     6   if initiative.issue.voter_count == 0 or initiative.positive_votes == nil or initiative.negative_votes == nil then
 | 
| 
bsw@1045
 | 
     7     return
 | 
| 
bsw@1045
 | 
     8   end
 | 
| 
bsw@1045
 | 
     9   
 | 
| 
bsw@1045
 | 
    10   local first_preference_votes = initiative.first_preference_votes
 | 
| 
bsw@1045
 | 
    11     
 | 
| 
bsw@1045
 | 
    12   local d = d or 100
 | 
| 
bsw/jbe@1309
 | 
    13   local gap = 2
 | 
| 
bsw@1045
 | 
    14   
 | 
| 
bsw@1045
 | 
    15   local r = d/2
 | 
| 
bsw@1045
 | 
    16   local r_circle = r - gap
 | 
| 
bsw@1045
 | 
    17   
 | 
| 
bsw@1045
 | 
    18   local function circle(p)
 | 
| 
bsw@1045
 | 
    19     return
 | 
| 
bsw@1045
 | 
    20       gap + 2 * r_circle * ( 1 + math.sin( 2 * math.pi * p ) ) / 2,
 | 
| 
bsw@1045
 | 
    21       gap + 2 * r_circle * ( 1 - math.cos( 2 * math.pi * p ) ) / 2
 | 
| 
bsw@1045
 | 
    22   end
 | 
| 
bsw@1045
 | 
    23   
 | 
| 
bsw@1045
 | 
    24   local function getpath(start, stop)
 | 
| 
bsw@1045
 | 
    25     local start_x, start_y = circle(start)
 | 
| 
bsw@1045
 | 
    26     local stop_x, stop_y = circle(stop)
 | 
| 
bsw@1045
 | 
    27     local large = stop - start > 0.5 and "1" or "0"
 | 
| 
bsw@1045
 | 
    28     return "M" .. r .. "," .. r .. " "
 | 
| 
bsw@1045
 | 
    29         .. "L" .. start_x .. ",".. start_y .. " " .. " "
 | 
| 
bsw@1045
 | 
    30         .. "A" .. r_circle .. "," .. r_circle .. " 0 " .. large .. ",1 " .. stop_x .. "," .. stop_y .. " "
 | 
| 
bsw@1045
 | 
    31         .. "z"
 | 
| 
bsw@1045
 | 
    32   end
 | 
| 
bsw@1045
 | 
    33   
 | 
| 
bsw@1045
 | 
    34   local function uniPie(color, content)
 | 
| 
bsw@1045
 | 
    35     ui.tag {
 | 
| 
bsw@1045
 | 
    36       tag = "svg",
 | 
| 
bsw@1045
 | 
    37       attr = {
 | 
| 
bsw@1045
 | 
    38         class = "initiative_pie",
 | 
| 
bsw@1045
 | 
    39         width = d .. "px",
 | 
| 
bsw@1045
 | 
    40         height = d .. "px",
 | 
| 
bsw@1045
 | 
    41       },
 | 
| 
bsw@1045
 | 
    42       content = function ()
 | 
| 
bsw@1045
 | 
    43         ui.tag { tag = "circle", attr = {
 | 
| 
bsw@1045
 | 
    44           cx=r, cy=r, r=r_circle, fill = color, stroke = "#fff", ["stroke-width"] = "2"
 | 
| 
bsw@1045
 | 
    45         }, content = function () ui.tag { tag = "title", content = content } end  }
 | 
| 
bsw@1045
 | 
    46       end
 | 
| 
bsw@1045
 | 
    47     }
 | 
| 
bsw@1045
 | 
    48   end
 | 
| 
bsw@1045
 | 
    49   
 | 
| 
bsw@1045
 | 
    50   local function piePiece(path, fill, content)
 | 
| 
bsw@1045
 | 
    51     ui.tag {
 | 
| 
bsw@1045
 | 
    52       tag = "path",
 | 
| 
bsw@1045
 | 
    53       attr = {
 | 
| 
bsw@1045
 | 
    54         d = path,
 | 
| 
bsw@1045
 | 
    55         fill = fill,
 | 
| 
bsw@1045
 | 
    56         stroke = "#fff",
 | 
| 
bsw@1045
 | 
    57         ["stroke-width"] = "2",
 | 
| 
bsw@1045
 | 
    58         ["stroke-linecap"] = "butt"
 | 
| 
bsw@1045
 | 
    59       }, 
 | 
| 
bsw@1045
 | 
    60       content = function ()
 | 
| 
bsw@1045
 | 
    61         ui.tag {
 | 
| 
bsw@1045
 | 
    62           tag = "title", 
 | 
| 
bsw@1045
 | 
    63           content = content
 | 
| 
bsw@1045
 | 
    64         }
 | 
| 
bsw@1045
 | 
    65       end  
 | 
| 
bsw@1045
 | 
    66     }
 | 
| 
bsw@1045
 | 
    67   end
 | 
| 
bsw@1045
 | 
    68   
 | 
| 
bsw@1045
 | 
    69   local function pie(args)
 | 
| 
bsw@1045
 | 
    70     local offset = args.offset or 0
 | 
| 
bsw@1045
 | 
    71     local list = {}
 | 
| 
bsw@1045
 | 
    72     local sum = 0
 | 
| 
bsw@1045
 | 
    73     for i, element in ipairs(args) do
 | 
| 
bsw@1045
 | 
    74       element.start = sum + offset
 | 
| 
bsw@1045
 | 
    75       list[#list+1] = element
 | 
| 
bsw@1045
 | 
    76       sum = sum + element.value
 | 
| 
bsw@1045
 | 
    77     end
 | 
| 
bsw@1045
 | 
    78     
 | 
| 
bsw@1045
 | 
    79     for i, element in ipairs(list) do
 | 
| 
bsw@1045
 | 
    80       if element.value == sum then
 | 
| 
bsw@1045
 | 
    81         uniPie(element.fill, _(element.label, { count = element.value } ))
 | 
| 
bsw@1045
 | 
    82         return
 | 
| 
bsw@1045
 | 
    83       end
 | 
| 
bsw@1045
 | 
    84     end
 | 
| 
bsw@1045
 | 
    85     ui.tag {
 | 
| 
bsw@1045
 | 
    86       tag = "svg",
 | 
| 
bsw@1045
 | 
    87       attr = {
 | 
| 
bsw@1045
 | 
    88         class = "initiative_pie",
 | 
| 
bsw@1045
 | 
    89         width = d .. "px",
 | 
| 
bsw@1045
 | 
    90         height = d .. "px"
 | 
| 
bsw@1045
 | 
    91       },
 | 
| 
bsw@1045
 | 
    92       content = function ()
 | 
| 
bsw@1045
 | 
    93         table.sort(list, function (a, b)
 | 
| 
bsw@1045
 | 
    94           return a.value < b.value
 | 
| 
bsw@1045
 | 
    95         end )
 | 
| 
bsw@1045
 | 
    96         for i, element in ipairs(list) do
 | 
| 
bsw@1045
 | 
    97           local path = getpath(element.start / sum, (element.start + element.value) / sum)
 | 
| 
bsw@1045
 | 
    98           local content = _(element.label, { count = element.value })
 | 
| 
bsw@1045
 | 
    99           piePiece(path, element.fill, content)
 | 
| 
bsw@1045
 | 
   100         end
 | 
| 
bsw@1045
 | 
   101       end
 | 
| 
bsw@1045
 | 
   102     }
 | 
| 
bsw@1045
 | 
   103   end
 | 
| 
bsw@1045
 | 
   104   
 | 
| 
bsw@1045
 | 
   105   local yes1 = first_preference_votes
 | 
| 
bsw@1045
 | 
   106   local yes = initiative.positive_votes - first_preference_votes
 | 
| 
bsw@1045
 | 
   107   local neutral = initiative.issue.voter_count - initiative.positive_votes - initiative.negative_votes
 | 
| 
bsw@1045
 | 
   108   local no = initiative.negative_votes
 | 
| 
bsw@1045
 | 
   109   
 | 
| 
bsw@1045
 | 
   110   local sum = yes1 + yes + neutral + no
 | 
| 
bsw@1045
 | 
   111   
 | 
| 
bsw@1045
 | 
   112   local q = initiative.issue.policy.direct_majority_num / initiative.issue.policy.direct_majority_den
 | 
| 
bsw@1045
 | 
   113       
 | 
| 
bsw@1045
 | 
   114   
 | 
| 
bsw@1045
 | 
   115   local maxrot = sum * 7 / 12 - no
 | 
| 
bsw@1045
 | 
   116   
 | 
| 
bsw@1045
 | 
   117   local offset = 0
 | 
| 
bsw@1045
 | 
   118   
 | 
| 
bsw@1045
 | 
   119   if maxrot > 0 then
 | 
| 
bsw@1045
 | 
   120     offset = math.min (
 | 
| 
bsw@1045
 | 
   121       maxrot, 
 | 
| 
bsw@1045
 | 
   122       no * ( 1 / ( 1 / q - 1 ) -1 ) / 2
 | 
| 
bsw@1045
 | 
   123     )
 | 
| 
bsw@1045
 | 
   124   end
 | 
| 
bsw@1045
 | 
   125     
 | 
| 
bsw@1045
 | 
   126   pie{
 | 
| 
bsw@1045
 | 
   127     { value = yes1,    fill = "#0a0", label = _"#{count} Yes, first choice" },
 | 
| 
bsw@1045
 | 
   128     { value = yes,     fill = "#6c6", label = _"#{count} Yes, alternative choice" },
 | 
| 
bsw@1045
 | 
   129     { value = neutral, fill = "#ccc", label = _"#{count} Neutral" },
 | 
| 
bsw@1045
 | 
   130     { value = no,      fill = "#c00", label = _"#{count} No" },
 | 
| 
bsw@1045
 | 
   131     offset = - offset
 | 
| 
bsw@1045
 | 
   132   }
 | 
| 
bsw@1045
 | 
   133     
 | 
| 
bsw/jbe@1309
 | 
   134 end
 |