Finished creating generic triangle and inverted triangle WITH offset to enable creation of 4 triangle graphics as specified
This commit is contained in:
parent
c11b0cee20
commit
6e64bd6f6e
65
IndexUsingJS.html
Normal file
65
IndexUsingJS.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>3-Axis Radar Chart with Ticks</title>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<script src="./drawtriangle.js"></script>
|
||||
<script src="./drawtriangleinverted.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
svg {
|
||||
background: white;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.axis line {
|
||||
stroke: black;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
line.tick {
|
||||
stroke: black;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
polygon.grid {
|
||||
fill: none;
|
||||
stroke: rgba(200, 200, 200, 0.5); /* light grey with 50% opacity */
|
||||
stroke-dasharray: 2,2;
|
||||
}
|
||||
|
||||
//.area {
|
||||
// fill: rgba(215, 96, 35, 0.1); /* steelblue with transparency */
|
||||
// stroke: rgba(215, 96, 35, 0.6);
|
||||
// stroke-width: 2;
|
||||
//}
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-anchor: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="drawtriangle('#svg1','Roles','#316443',[0.5,0.6,0.5],'red', { x: 0, y: 350 },-0.7);
|
||||
drawtriangle('#svg1','Actions','#6e6e6e',[0.1,0.6,0.5],'green',{ x: 370, y: 350 },2.3);
|
||||
drawtriangle('#svg1','Approach','#d76a23',[0.3,0.6,0.5],'#ffbf00',{ x: 185, y: 30 },1.4);
|
||||
drawtriangleinverted('#svg1','Impact','#9f84b8',[0.7,0.6,0.8],'green',{ x: 185, y: 244},2.4);
|
||||
">
|
||||
<svg id="svg1" width="1000" height="1000"></svg>
|
||||
|
||||
<svg id="svg2" width="500" height="500"></svg>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
168
drawtriangle.js
Normal file
168
drawtriangle.js
Normal file
@ -0,0 +1,168 @@
|
||||
|
||||
function drawtriangle(id,text,colour,values,rag,offset,mean)
|
||||
{
|
||||
|
||||
const svg = d3.select(`${id}`);
|
||||
const width = 500; // Fixed size. Was +svg.attr("width");
|
||||
const height = 500; // Fixed size. Was +svg.attr("height");
|
||||
const radius = 200;
|
||||
const center = { x: (width / 2) + offset.x, y: (height / 2) + offset.y};
|
||||
|
||||
let textcolour = colour;
|
||||
let polycolour = colour;
|
||||
let opacity = 0.3;
|
||||
|
||||
if (rag !== "")
|
||||
{
|
||||
polycolour = rag;
|
||||
textcolour = "black";
|
||||
console.log("rag: "+rag);
|
||||
opacity = 0.6;
|
||||
}
|
||||
|
||||
|
||||
// Define axes with A at the top (rotate -90 degrees)
|
||||
const axes = [
|
||||
{ name: "Feature A", angle: 0 },
|
||||
{ name: "Feature B", angle: 120 },
|
||||
{ name: "Feature C", angle: 240 }
|
||||
];
|
||||
axes.forEach(d => d.angle = (d.angle - 90) * Math.PI / 180);
|
||||
|
||||
// Sample data: [0-1] scale
|
||||
// const values = [0.8, 0.1, 0.76]; Now passed in function args
|
||||
|
||||
// Draw axis lines
|
||||
svg.append("g")
|
||||
.attr("class", "axis")
|
||||
.selectAll("line")
|
||||
.data(axes)
|
||||
.join("line")
|
||||
.attr("x1", center.x)
|
||||
.attr("y1", center.y)
|
||||
.attr("x2", d => center.x + radius * Math.cos(d.angle))
|
||||
.attr("y2", d => center.y + radius * Math.sin(d.angle));
|
||||
|
||||
// Draw grid rings (polygons)
|
||||
const levels = 7;
|
||||
for (let i = 1; i <= levels; i++) {
|
||||
const r = (radius / levels) * i;
|
||||
const points = axes.map(d => {
|
||||
return [
|
||||
center.x + r * Math.cos(d.angle),
|
||||
center.y + r * Math.sin(d.angle)
|
||||
];
|
||||
});
|
||||
svg.append("polygon")
|
||||
.attr("class", "grid")
|
||||
.attr("points", points.map(p => p.join(",")).join(" "));
|
||||
}
|
||||
|
||||
|
||||
// Draw axis ticks
|
||||
const tickLength = 5;
|
||||
axes.forEach(axis => {
|
||||
for (let i = 1; i <= levels; i++) {
|
||||
const r = (radius / levels) * i;
|
||||
const angle = axis.angle;
|
||||
|
||||
const x1 = center.x + r * Math.cos(angle);
|
||||
const y1 = center.y + r * Math.sin(angle);
|
||||
|
||||
// Perpendicular tick
|
||||
const perpAngle = angle + Math.PI / 2;
|
||||
const x2 = x1 + tickLength * Math.cos(perpAngle);
|
||||
const y2 = y1 + tickLength * Math.sin(perpAngle);
|
||||
|
||||
svg.append("line")
|
||||
.attr("x1", x1)
|
||||
.attr("y1", y1)
|
||||
.attr("x2", x2)
|
||||
.attr("y2", y2)
|
||||
.attr("class", "tick");
|
||||
}
|
||||
});
|
||||
|
||||
// Convert values to coordinates
|
||||
const valuePoints = axes.map((d, i) => {
|
||||
const r = radius * values[i];
|
||||
return [
|
||||
center.x + r * Math.cos(d.angle),
|
||||
center.y + r * Math.sin(d.angle)
|
||||
];
|
||||
});
|
||||
|
||||
// Draw data area
|
||||
svg.append("polygon")
|
||||
.attr("class", "area")
|
||||
.attr("points", valuePoints.map(p => p.join(",")).join(" "))
|
||||
.attr("fill", polycolour) // #d76a23 with 30% opacity (translucent)
|
||||
.attr("fill-opacity", opacity)
|
||||
.attr("stroke", polycolour) // Outer line color
|
||||
.attr("stroke-width", 1); // Thin outer line
|
||||
|
||||
// Draw outer triangle border (goes to edge of chart)
|
||||
const outerPoints = axes.map(d => [
|
||||
center.x + radius * Math.cos(d.angle),
|
||||
center.y + radius * Math.sin(d.angle)
|
||||
]);
|
||||
|
||||
// Draw border separately (on top)
|
||||
svg.append("polygon")
|
||||
.attr("points", outerPoints.map(p => p.join(",")).join(" "))
|
||||
.attr("fill","none")
|
||||
.attr("stroke", colour) // Outer line color
|
||||
.attr("stroke-width", 3); // Thin outer line
|
||||
|
||||
// Put label in bottom centre of triangle
|
||||
const bottomLeft = outerPoints[1];
|
||||
const bottomRight = outerPoints[2];
|
||||
|
||||
const midX = (bottomLeft[0] + bottomRight[0]) / 2;
|
||||
const midY = (bottomLeft[1] + bottomRight[1]) / 2;
|
||||
|
||||
svg.append("text")
|
||||
.attr("x", midX)
|
||||
.attr("y", midY - 15) // Alter for correct height
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", textcolour) /// My Colour
|
||||
.attr("font-size", "24px")
|
||||
.attr("font-weight", "bold")
|
||||
.text(`${text}`);
|
||||
|
||||
// And now a central number
|
||||
svg.append("text")
|
||||
.attr("x", midX)
|
||||
.attr("y", midY -60)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "black") // 🎨 customize color
|
||||
.attr("font-size", "22px")
|
||||
.attr("font-weight", "bold")
|
||||
.attr("font-family", "Courier New, monospace")
|
||||
.text(`${mean}`);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Add axis labels
|
||||
/*
|
||||
svg.selectAll(".label")
|
||||
.data(axes)
|
||||
.join("text")
|
||||
.attr("class", "label")
|
||||
.attr("x", d => center.x + (radius + 20) * Math.cos(d.angle))
|
||||
.attr("y", (d,i) => {
|
||||
if (i === 0) { //adjust for 1st access, rather than using d.name
|
||||
return center.y + (radius + 20) * Math.sin(d.angle)
|
||||
} else
|
||||
return center.y + (radius + 20 + 30) * Math.sin(d.angle)
|
||||
})
|
||||
.text(d => d.name);
|
||||
*/
|
||||
|
||||
|
||||
}
|
146
drawtriangleinverted.js
Normal file
146
drawtriangleinverted.js
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
|
||||
|
||||
function drawtriangleinverted(id,text,colour,values,rag,offset,mean)
|
||||
{
|
||||
|
||||
const svg = d3.select(`${id}`);
|
||||
const width = 500; // Fixed size. Was +svg.attr("width");
|
||||
const height = 500; // Fixed size. Was +svg.attr("height");
|
||||
const radius = 200;
|
||||
const center = { x: (width / 2) + offset.x, y: (height / 2) + offset.y};
|
||||
|
||||
let textcolour = colour;
|
||||
let polycolour = colour;
|
||||
let opacity = 0.3;
|
||||
|
||||
if (rag !== "")
|
||||
{
|
||||
polycolour = rag;
|
||||
textcolour = "black";
|
||||
console.log("rag: "+rag);
|
||||
opacity = 0.6;
|
||||
}
|
||||
|
||||
// Update angles for flat top triangle
|
||||
const axes = [
|
||||
{ name: "Feature A", angle: 60 }, // Left axis
|
||||
{ name: "Feature B", angle: 180 }, // Top axis (flat)
|
||||
{ name: "Feature C", angle: 300 } // Right axis
|
||||
];
|
||||
|
||||
// Convert angles to radians (subtract 90 degrees to rotate the whole chart)
|
||||
axes.forEach(d => d.angle = (d.angle - 90) * Math.PI / 180);
|
||||
|
||||
// Sample data: [0-1] scale
|
||||
// const values = [0.8, 0.1, 0.76]; Now passed in function args
|
||||
|
||||
// Draw axis lines
|
||||
svg.append("g")
|
||||
.attr("class", "axis")
|
||||
.selectAll("line")
|
||||
.data(axes)
|
||||
.join("line")
|
||||
.attr("x1", center.x)
|
||||
.attr("y1", center.y)
|
||||
.attr("x2", d => center.x + radius * Math.cos(d.angle))
|
||||
.attr("y2", d => center.y + radius * Math.sin(d.angle));
|
||||
|
||||
// Draw grid rings (polygons)
|
||||
const levels = 7;
|
||||
for (let i = 1; i <= levels; i++) {
|
||||
const r = (radius / levels) * i;
|
||||
const points = axes.map(d => {
|
||||
return [
|
||||
center.x + r * Math.cos(d.angle),
|
||||
center.y + r * Math.sin(d.angle)
|
||||
];
|
||||
});
|
||||
svg.append("polygon")
|
||||
.attr("class", "grid")
|
||||
.attr("points", points.map(p => p.join(",")).join(" "));
|
||||
}
|
||||
|
||||
// Draw axis ticks
|
||||
const tickLength = 5;
|
||||
axes.forEach(axis => {
|
||||
for (let i = 1; i <= levels; i++) {
|
||||
const r = (radius / levels) * i;
|
||||
const angle = axis.angle;
|
||||
|
||||
const x1 = center.x + r * Math.cos(angle);
|
||||
const y1 = center.y + r * Math.sin(angle);
|
||||
|
||||
// Perpendicular tick
|
||||
const perpAngle = angle + Math.PI / 2;
|
||||
const x2 = x1 + tickLength * Math.cos(perpAngle);
|
||||
const y2 = y1 + tickLength * Math.sin(perpAngle);
|
||||
|
||||
svg.append("line")
|
||||
.attr("x1", x1)
|
||||
.attr("y1", y1)
|
||||
.attr("x2", x2)
|
||||
.attr("y2", y2)
|
||||
.attr("class", "tick");
|
||||
}
|
||||
});
|
||||
|
||||
// Convert values to coordinates
|
||||
const valuePoints = axes.map((d, i) => {
|
||||
const r = radius * values[i];
|
||||
return [
|
||||
center.x + r * Math.cos(d.angle),
|
||||
center.y + r * Math.sin(d.angle)
|
||||
];
|
||||
});
|
||||
|
||||
// Draw data area
|
||||
svg.append("polygon")
|
||||
.attr("class", "area")
|
||||
.attr("points", valuePoints.map(p => p.join(",")).join(" "))
|
||||
.attr("fill", polycolour) // #9f84b8
|
||||
.attr("fill-opacity", opacity) // with 30% opacity (translucent)
|
||||
.attr("stroke", polycolour) // Outer line color
|
||||
.attr("stroke-width", 1); // Thin outer line
|
||||
|
||||
// Draw outer triangle border (goes to edge of chart)
|
||||
const outerPoints = axes.map(d => [
|
||||
center.x + radius * Math.cos(d.angle),
|
||||
center.y + radius * Math.sin(d.angle)
|
||||
]);
|
||||
|
||||
// Draw border separately (on top)
|
||||
svg.append("polygon")
|
||||
.attr("points", outerPoints.map(p => p.join(",")).join(" "))
|
||||
.attr("fill","none")
|
||||
.attr("stroke", colour) // Outer line color
|
||||
.attr("stroke-width", 3); // Thin outer line
|
||||
|
||||
// Put label in bottom centre of triangle (adjust for flat top)
|
||||
const bottomLeft = outerPoints[0];
|
||||
const bottomRight = outerPoints[2];
|
||||
|
||||
const midX = (bottomLeft[0] + bottomRight[0]) / 2;
|
||||
const midY = (bottomLeft[1] + bottomRight[1]) / 2;
|
||||
|
||||
svg.append("text")
|
||||
.attr("x", midX)
|
||||
.attr("y", midY + 30) // Adjust for correct height (moved to bottom)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", textcolour) // My Colour
|
||||
.attr("font-size", "24px")
|
||||
.attr("font-weight", "bold")
|
||||
.text(`${text}`);
|
||||
|
||||
// And now a central number
|
||||
svg.append("text")
|
||||
.attr("x", midX)
|
||||
.attr("y", midY + 80)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "black") // 🎨 customize color
|
||||
.attr("font-size", "22px")
|
||||
.attr("font-weight", "bold")
|
||||
.attr("font-family", "Courier New, monospace")
|
||||
.text(`${mean}`);
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user