bsw@1045: function util.initiative_pie(initiative, d, gap) bsw@1045: if not initiative.issue.closed or not initiative.admitted then bsw@1045: return bsw@1045: end bsw@1045: bsw@1045: if initiative.issue.voter_count == 0 or initiative.positive_votes == nil or initiative.negative_votes == nil then bsw@1045: return bsw@1045: end bsw@1045: bsw@1045: local first_preference_votes = initiative.first_preference_votes bsw@1045: bsw@1045: local d = d or 100 bsw/jbe@1309: local gap = 2 bsw@1045: bsw@1045: local r = d/2 bsw@1045: local r_circle = r - gap bsw@1045: bsw@1045: local function circle(p) bsw@1045: return bsw@1045: gap + 2 * r_circle * ( 1 + math.sin( 2 * math.pi * p ) ) / 2, bsw@1045: gap + 2 * r_circle * ( 1 - math.cos( 2 * math.pi * p ) ) / 2 bsw@1045: end bsw@1045: bsw@1045: local function getpath(start, stop) bsw@1045: local start_x, start_y = circle(start) bsw@1045: local stop_x, stop_y = circle(stop) bsw@1045: local large = stop - start > 0.5 and "1" or "0" bsw@1045: return "M" .. r .. "," .. r .. " " bsw@1045: .. "L" .. start_x .. ",".. start_y .. " " .. " " bsw@1045: .. "A" .. r_circle .. "," .. r_circle .. " 0 " .. large .. ",1 " .. stop_x .. "," .. stop_y .. " " bsw@1045: .. "z" bsw@1045: end bsw@1045: bsw@1045: local function uniPie(color, content) bsw@1045: ui.tag { bsw@1045: tag = "svg", bsw@1045: attr = { bsw@1045: class = "initiative_pie", bsw@1045: width = d .. "px", bsw@1045: height = d .. "px", bsw@1045: }, bsw@1045: content = function () bsw@1045: ui.tag { tag = "circle", attr = { bsw@1045: cx=r, cy=r, r=r_circle, fill = color, stroke = "#fff", ["stroke-width"] = "2" bsw@1045: }, content = function () ui.tag { tag = "title", content = content } end } bsw@1045: end bsw@1045: } bsw@1045: end bsw@1045: bsw@1045: local function piePiece(path, fill, content) bsw@1045: ui.tag { bsw@1045: tag = "path", bsw@1045: attr = { bsw@1045: d = path, bsw@1045: fill = fill, bsw@1045: stroke = "#fff", bsw@1045: ["stroke-width"] = "2", bsw@1045: ["stroke-linecap"] = "butt" bsw@1045: }, bsw@1045: content = function () bsw@1045: ui.tag { bsw@1045: tag = "title", bsw@1045: content = content bsw@1045: } bsw@1045: end bsw@1045: } bsw@1045: end bsw@1045: bsw@1045: local function pie(args) bsw@1045: local offset = args.offset or 0 bsw@1045: local list = {} bsw@1045: local sum = 0 bsw@1045: for i, element in ipairs(args) do bsw@1045: element.start = sum + offset bsw@1045: list[#list+1] = element bsw@1045: sum = sum + element.value bsw@1045: end bsw@1045: bsw@1045: for i, element in ipairs(list) do bsw@1045: if element.value == sum then bsw@1045: uniPie(element.fill, _(element.label, { count = element.value } )) bsw@1045: return bsw@1045: end bsw@1045: end bsw@1045: ui.tag { bsw@1045: tag = "svg", bsw@1045: attr = { bsw@1045: class = "initiative_pie", bsw@1045: width = d .. "px", bsw@1045: height = d .. "px" bsw@1045: }, bsw@1045: content = function () bsw@1045: table.sort(list, function (a, b) bsw@1045: return a.value < b.value bsw@1045: end ) bsw@1045: for i, element in ipairs(list) do bsw@1045: local path = getpath(element.start / sum, (element.start + element.value) / sum) bsw@1045: local content = _(element.label, { count = element.value }) bsw@1045: piePiece(path, element.fill, content) bsw@1045: end bsw@1045: end bsw@1045: } bsw@1045: end bsw@1045: bsw@1045: local yes1 = first_preference_votes bsw@1045: local yes = initiative.positive_votes - first_preference_votes bsw@1045: local neutral = initiative.issue.voter_count - initiative.positive_votes - initiative.negative_votes bsw@1045: local no = initiative.negative_votes bsw@1045: bsw@1045: local sum = yes1 + yes + neutral + no bsw@1045: bsw@1045: local q = initiative.issue.policy.direct_majority_num / initiative.issue.policy.direct_majority_den bsw@1045: bsw@1045: bsw@1045: local maxrot = sum * 7 / 12 - no bsw@1045: bsw@1045: local offset = 0 bsw@1045: bsw@1045: if maxrot > 0 then bsw@1045: offset = math.min ( bsw@1045: maxrot, bsw@1045: no * ( 1 / ( 1 / q - 1 ) -1 ) / 2 bsw@1045: ) bsw@1045: end bsw@1045: bsw@1045: pie{ bsw@1045: { value = yes1, fill = "#0a0", label = _"#{count} Yes, first choice" }, bsw@1045: { value = yes, fill = "#6c6", label = _"#{count} Yes, alternative choice" }, bsw@1045: { value = neutral, fill = "#ccc", label = _"#{count} Neutral" }, bsw@1045: { value = no, fill = "#c00", label = _"#{count} No" }, bsw@1045: offset = - offset bsw@1045: } bsw@1045: bsw/jbe@1309: end