F Flexibility B Breathing I Intonation R Range T Tone A Articulation S Strength

Flexibility
Breathing
Intonation
Range
Tone
Articulation
Strength



let svg = document.getElementById(‘mysvg’)

let dims = [
{code: ‘F’, name: ‘Flexibility’, color: ‘EC393F’, pct: 0, point: [], rect: [], path: ”},
{code: ‘B’, name: ‘Breathing’, color: ‘F1952D’, pct: 0, point: [], rect: [], path: ”},
{code: ‘I’, name: ‘Intonation’, color: ‘FFCA3A’, pct: 0, point: [], rect: [], path: ”},
{code: ‘R’, name: ‘Range’, color: ‘8AC926’, pct: 0, point: [], rect: [], path: ”},
{code: ‘T’, name: ‘Tone’, color: ‘4AABE8’, pct: 0, point: [], rect: [], path: ”},
{code: ‘A’, name: ‘Articulation’, color: ‘46468B’, pct: 0, point: [], rect: [], path: ”},
{code: ‘S’, name: ‘Strength’, color: ‘7453A2’, pct: 0, point: [], rect: [], path: ”},
];

function lineIntersection($A, $B, $C, $D) {
// Line AB represented as a1x + b1y = c1
$a1 = $B[1] – $A[1];
$b1 = $A[0] – $B[0];
$c1 = $a1*($A[0]) + $b1*($A[1]);

// Line CD represented as a2x + b2y = c2
$a2 = $D[1] – $C[1];
$b2 = $C[0] – $D[0];
$c2 = $a2*($C[0]) + $b2*($C[1]);

$determinant = $a1*$b2 – $a2*$b1;

if ($determinant == 0) {
// The lines are parallel. This is simplified by returning a pair of FLT_MAX
return [false, false];
} else {
$x = ($b2*$c1 – $b1*$c2) / $determinant;
$y = ($a1*$c2 – $a2*$c1) / $determinant;
return [$x.toFixed(2), $y.toFixed(2)];
}
}

function recalc() {
dims.forEach((item, index, array) => {
item.point = [
heptagon[0][index][0] + (item.pct / 100) * (heptagon[5][index][0] – heptagon[0][index][0]),
heptagon[0][index][1] + (item.pct / 100) * (heptagon[5][index][1] – heptagon[0][index][1])
]
})

dims.forEach((item, index, array) => {
$next = index == 6 ? 0 : index + 1;
$prev = index == 0 ? 6 : index – 1;

$nextMiddlePoint = [
heptagon[5][index][0] + (heptagon[5][$next][0] – heptagon[5][index][0]) / 2,
heptagon[5][index][1] + (heptagon[5][$next][1] – heptagon[5][index][1]) / 2
];
$prevMiddlePoint = [
heptagon[5][index][0] + (heptagon[5][$prev][0] – heptagon[5][index][0]) / 2,
heptagon[5][index][1] + (heptagon[5][$prev][1] – heptagon[5][index][1]) / 2
];

$nextPoint = lineIntersection([200, 200], $nextMiddlePoint, dims[index].point, dims[$next].point);
$prevPoint = lineIntersection([200, 200], $prevMiddlePoint, dims[index].point, dims[$prev].point);

item.rect = [[200, 200], $prevPoint, item.point, $nextPoint];

item.path = ‘M200 200L’ + item.rect[1][0] + ‘ ‘ + item.rect[1][1]
+ ‘L’ + item.rect[2][0] + ‘ ‘ + item.rect[2][1]
+ ‘L’ + item.rect[3][0] + ‘ ‘ + item.rect[3][1] + ‘z’;
})
}

function redraw() {
document.querySelectorAll(‘.path7’).forEach((item, index) => {
item.setAttribute(‘d’, dims[index].path)
document.getElementsByClassName(‘progress-bar’)[index].style.width = dims[index].pct + ‘%’
document.getElementsByClassName(‘progress-dim-pct’)[index].innerHTML = dims[index].pct + ‘%’
})
}

function oMousePosSVG(e) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
var ctm = svg.getScreenCTM().inverse();
var p = p.matrixTransform(ctm);
return p;
}

var DEG = 180 / Math.PI;

document.querySelectorAll(‘.drag7’).forEach((item, index) => {
let path = document.getElementsByClassName(‘line7’)[index]
let pathLength = path.getTotalLength() || 0;
let startPoint = path.getPointAtLength(0)
var startAngle = getRotation(startPoint, path.getPointAtLength(0.1));

gsap.to(item, {
transformOrigin: “center”,
rotation: startAngle + “_rad”,
xPercent: -50,
yPercent: -50,
x: startPoint.x,
y: startPoint.y
});

Draggable.create(item, {
bounds: document.getElementById(‘mysvg’),
liveSnap: {
points: function(point) {
let p = closestPoint(path, pathLength, point);
return p.point;
}
},
onMove: function() {
let a = Math.abs(this.endX – 200)
let b = Math.abs(this.endY – 200)
let c = Math.round(100 * (Math.sqrt(a * a + b * b) – 25) / 125)
// 125 = length of spokes, c = percentage of new line
// I and A won’t get to 100% no matter what
if ((index == 2 || index == 5) && (c == 99)) {
c = 100
}
dims[index].pct = c
recalc()
redraw()
}
})
})

function pointModifier(point) {
var p = closestPoint(path, pathLength, point);
// gsap.to(dragf, {
// rotation: p.rotation
// });
return p.point;
}

function closestPoint(pathNode, pathLength, point) {
var precision = 8,
best,
bestLength,
bestDistance = Infinity;

// linear scan for coarse approximation
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { best = scan, bestLength = scanLength, bestDistance = scanDistance; } } // binary search for precise estimate precision /= 2; while (precision > 0.5) {
var before,
after,
beforeLength,
afterLength,
beforeDistance,
afterDistance;
if ((beforeLength = bestLength – precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { best = before, bestLength = beforeLength, bestDistance = beforeDistance; } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { best = after, bestLength = afterLength, bestDistance = afterDistance; } else { precision /= 2; } } var len2 = bestLength + (bestLength === pathLength ? -0.1 : 0.1); var rotation = getRotation(best, pathNode.getPointAtLength(len2)); return { point: best, rotation: rotation * DEG, // distance: Math.sqrt(bestDistance), }; function distance2(p) { var dx = p.x - point.x, dy = p.y - point.y; return dx * dx + dy * dy; } } function getRotation(p1, p2) { var dx = p2.x - p1.x; var dy = p2.y - p1.y; return Math.atan2(dy, dx); }

By reconstructing your assessment above, you will have a more intimate connection to your vocal diagnostic profile. It’s like an evolving fingerprint: it is uniquely you and should be celebrated before you take the journey!