diff --git a/IndexUsingJS.html b/IndexUsingJS.html new file mode 100644 index 0000000..2cd70ba --- /dev/null +++ b/IndexUsingJS.html @@ -0,0 +1,65 @@ + + + + + 3-Axis Radar Chart with Ticks + + + + + + + + + + + + + + + diff --git a/drawtriangle.js b/drawtriangle.js new file mode 100644 index 0000000..dfcc02a --- /dev/null +++ b/drawtriangle.js @@ -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); +*/ + + +} diff --git a/drawtriangleinverted.js b/drawtriangleinverted.js new file mode 100644 index 0000000..abbcfa6 --- /dev/null +++ b/drawtriangleinverted.js @@ -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}`); + +}