function flower(appraisal, probeIndex, rotation, scale) {
const probe = appraisal.probes[probeIndex];
setupGradients(appraisal, probeIndex);
let g = "";
g += grid();
g += ripples(numberOfRipples, scale);
g += label(appraisal, probeIndex);
let a = "";
a += defs(appraisal);
a += "<!-- Petals -->";
probe.scores.forEach((score, i) => {
a += layersOfPetals(score.mean / appraisal.max, numberOfRipples, i);
if (showRadials)
a += `<line class="ripple" x1="0" y1="0" x2="100" y2="0" />`;
});
return `${g}<!-- Flower --><g class="petal-stroke" transform="rotate(${rotation}) scale(${scale})">${a}</g>`;
function head(appraisal, probeIndex) {
let h = "";
h += title(appraisal, probeIndex);
return h;
function title(appraisal, probeIndex) {}
}
function defs(appraisal) {
let gradients = "";
appraisal.appearance.forEach(segment => {
gradients += `${segment.gradient.value}`;
});
let filters = "";
appraisal.appearance.forEach(segment => {
filters += `${segment.blur.value}`;
});
return `<!-- Gradients & Blurs --><defs>${gradients}${filters}</defs>`;
}
function layersOfPetals(value, rippleCount, segment) {
let a = "";
a += `transform="rotate(${(segment * 360) / probe.scores.length})"`;
a += `fill="${appraisal.appearance[segment].gradient.id}"`;
if (!navigator.userAgent.match("HeadlessChrome"))
a += `filter="${appraisal.appearance[segment].blur.id}"`;
return `<g ${a}>${petals(rippleCount)}</g>`;
function petals(rippleCount) {
let a = "";
for (let i = rippleCount; i >= 0; i--) {
let scale =
(i + 1) / rippleCount < value ? (i + 1) / rippleCount : value;
a += `<path transform="scale(${scale})"`;
a += `d="M 0 0 C ${petalLength} ${petalWidth} ${petalLength} ${-petalWidth} 0 0" `;
a += "/>";
}
return a;
}
}
function setupGradients(appraisal, probeIndex) {
appraisal.probes[probeIndex].scores.forEach((score, i) => {
const appearance = appraisal.appearance[i % appraisal.appearance.length];
const guid = DOM.uid("gradient");
appearance.gradient = {
id: guid,
value:
`<radialGradient id="${guid.id}" cx="0%" r="100%">` +
`<stop class='heart-color' offset="${heart * 100}%" />` +
`<stop stop-color="${appearance.color}" offset="${edge * 100}%" />` +
`</radialGradient>`
};
const buid = DOM.uid("blur");
appearance.blur = {
id: buid,
value:
`<filter id="${buid.id}">` +
`<feGaussianBlur in="SourceGraphic" stdDeviation="${score.sigma *
2}" />` +
`</filter>`
};
});
return appraisal;
}
function label(appraisal, probeIndex) {
const probe = appraisal.probes[probeIndex];
const center = [0, 0];
let a = "";
probe.scores.forEach((b, i) => {
const dotColor = appraisal.appearance[i].color;
const alpha = 2 * Math.PI * (i / probe.scores.length + rotation / 360);
const bend = dotOnCircle(labelCenterDistance, alpha);
const side = bend[0] > 0 ? +1 : -1;
const edgePoint = [side * 100, bend[1]];
const textAnchor = [edgePoint[0] + side * 5, edgePoint[1]];
const statAnchor = [textAnchor[0], textAnchor[1] + 5];
const stats = `avg = ${b.mean.toFixed(1)} / σ = ${b.sigma.toFixed(1)}`;
a += polyline([center, bend, edgePoint]);
a += text("label", textAnchor, side, appraisal.appearance[i].category);
if (showStatistics) a += text("stats", statAnchor, side, stats);
a += dot(bend, dotColor);
a += dot(center, "darkred");
});
return `<!-- Labels --><g>${a}</g>`;
function polyline(l, kind = "ray") {
let a = "";
const start = l.shift();
if (start) a += `M${start[0]} ${start[1]}`;
l.forEach(p => {
a += ` L${p[0]} ${p[1]}`;
});
return `<path class="${kind}" d="${a}" />`;
}
function text(kind, [x, y], anchor, t) {
const a = anchor == 1 ? "start" : "end";
return `<text class="${kind}" text-anchor="${a}" x="${x}" y="${y}" dy="0mm"> ${t}</text>`;
}
function dotOnCircle(radius, angle) {
return [radius * Math.cos(angle), radius * Math.sin(angle)];
}
}
function ripples(numberOfRipples, scale) {
if (showRipples) {
const ripple = numberOfRipples => {
let a = "";
for (let r = numberOfRipples; r > 0; r--)
a += `<circle class="ripple" r="${(100 * r) / numberOfRipples}" />`;
return a;
};
return `<!-- Ripples --><g transform="scale(${scale})">${ripple(
numberOfRipples
)}</g>`;
}
}
function dot([x, y], color) {
const a = x.toFixed(1);
const b = y.toFixed(1);
let z = "";
if (showHandles)
z += `<!-- dot --><circle class="handle" cx=${a} cy=${b} r="3" fill="${color}" />`;
if (showCoordinates)
z += `<text dy="-1mm" class="point" x=${a} y=${b}>(${a}, ${b})</text>`;
return z;
}
}