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