Hello and welcome to our community! Is this your first visit?
Enjoy an ad free experience by logging in. Not a member yet? Register.

# Thread: Find point if within circle and within a sector

1. Originally Posted by 007julien
Then with this new coordinates an Math.atan2(cy,cx) will give the angle to compare with the other pie chart sector radius.
Thanks for that. After much anguish, I finally figured out how to do this with only a single `canvas` element and no `img` or `map` elements by using the `atan2` method: https://patrick.dark.name/web.dev/demos/canvas.1/. The most difficult part was figuring out how to rotate the pie chart and all of its effects using a single `startAngleOffset` value.

I also restructured the code to make it reusable; it can be used to create multiple pie charts on the same page.

Code:
```<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="application/xml" href="../style.sheets/boilerplate.xslt"?>
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:x="http://www.w3.org/1999/xhtml">
<title>Demo C for CodingForums.com Thread 316069: Find point if within circle and within a sector</title>
<meta itemprop="see_also" content="../svg.2a/"/>
<meta itemprop="see_also" content="../svg.2b/"/>
<body>
<h1 id="demo.title">Demo C for CodingForums.com Thread 316069: <cite>Find point if within circle and within a sector</cite></h1>
<section id="demo.description">
<h1>Demo Description</h1>
<p>This demo demonstrates how to use a <code>canvas</code> element (in conjunction with JavaScript) to create a pie chart with dynamically‐positioned, interactive pie slices that generate a tooltip when hovered over by the cursor.</p>
<p>A data‐equivalent table is nested within the <code>canvas</code> element for semantic value. (The table can be viewed by disabling JavaScript.)</p>
<p class="version.info">There are three versions of this demo. This version, C, uses the <code>canvas</code> element whereas the other two versions, <a href="../svg.2a/">version A</a> and <a href="../svg.2b/">version B</a>, were written using a more appropriate technology, <abbr title="Scalable Vector Graphics">SVG</abbr>, and are, therefore, significantly less complex.</p>
</section>
<section id="demo">
<h1>Demo</h1>
<figure id="marbles.pie.chart">
<style scoped="">
@namespace "http://www.w3.org/1999/xhtml";
#tooltip { border: 0.15rem solid hsla(0, 0%, 0%, 1); /* black */; border-radius: 0.25rem; padding: 0.25rem 0.4rem; background-color: hsla(0, 0%, 95%, 1); /* off‐white */ color: hsla(0, 0%, 5%, 1); /* off‐black */ line-height: 1; }
</style>
<canvas width="300" height="300">
<table>
<style scoped="">
@namespace "http://www.w3.org/1999/xhtml";
th, td { border: 0 none currentcolor; color: white; }
thead th { background-color: hsla(0, 0%, 35%, 1); /* medium‐dark gray */ }
tr.blue > * { background-color: hsla(210, 70%, 35%, 1); /* blue */ }
tr.blue:hover > * { background-color: hsla(210, 86%, 45%, 1); /* lighter blue */ }
tr.green > * { background-color: hsla(120, 70%, 35%, 1); /* green */ }
tr.green:hover > * { background-color: hsla(120, 86%, 45%, 1); /* lighter green */ }
tr.pink > * { background-color: hsla(340, 70%, 35%, 1); /* pink */ }
tr.pink:hover > * { background-color: hsla(340, 86%, 45%, 1); /* lighter pink */ }
tr.yellow > * { background-color: hsla(50, 70%, 35%, 1); /* yellow */ }
tr.yellow:hover > * { background-color: hsla(50, 86%, 45%, 1); /* lighter yellow */ }
</style>
<caption class="redundant">Marbles by Color</caption>
<tr>
<th>Color</th>
<th>Percentage</th>
</tr>
<tbody>
<tr class="blue">
<th>Blue</th>
<td class="number">14</td>
</tr>
<tr class="green">
<th>Green</th>
<td class="number">43</td>
</tr>
<tr class="pink">
<th>Pink</th>
<td class="number">4</td>
</tr>
<tr class="yellow">
<th>Yellow</th>
<td class="number">39</td>
</tr>
</tbody>
</table>
</canvas>
<figcaption>Marbles by Color</figcaption>
</figure>
<script>
// <![CDATA[
// This script was validated at http://jshint.com/ using the following settings:
/* jshint browser: true, curly: true, eqeqeq: true, devel: false, forin: true, immed: true, latedef: true, newcap: false, noarg: true, noempty: true, nonew: true, plusplus: true, quotmark: double, undef: true, unused: strict, strict: true, trailing: true */
(function () {
"use strict";
var namespaces = Object.create(null);
var pieChart = Object.create(null);
var marblesPieChart = Object.create(pieChart);
namespaces.null = "";
namespaces.XHTML = "http://www.w3.org/1999/xhtml";
Math.completeArcAngle = (2 * Math.PI);
Math.normalizeAngle = function (angle) {
// Normalize angles to the range [0, 2ᴨ).
if (angle < 0) {
angle += Math.completeArcAngle;
}
else if (angle > Math.completeArcAngle) {
angle %= Math.completeArcAngle;
}
return angle;
};
Math.distance = function (xCoord1, yCoord1, xCoord2, yCoord2) {
// Distance Formula: √((x₂ - x₁)² + (y₂ - y₁)²)
return Math.sqrt(Math.pow(xCoord2 - xCoord1, 2) + Math.pow(yCoord2 - yCoord1, 2));
};
Math.angleFromOrigin = function (xCoord, yCoord, originXCoord, originYCoord) {
var xOffsetFromOrigin = (xCoord - originXCoord);
var yOffsetFromOrigin = (yCoord - originYCoord);
var angleFromOrigin = Math.atan2(yOffsetFromOrigin, xOffsetFromOrigin);
return Math.normalizeAngle(angleFromOrigin);

};
Math.offsetAngle = function(angle, angleOffset) {
angle -= angleOffset;
return Math.normalizeAngle(angle);
};
CanvasRenderingContext2D.prototype.clear = function () {
this.clearRect(0, 0, this.canvas.width, this.canvas.height);
};
pieChart.destroyTooltip = function () {
var tooltip = document.getElementById("tooltip");
if (tooltip instanceof HTMLElement) {
tooltip.parentNode.removeChild(tooltip);
}
};
pieChart.createTooltip = function (cursorEvent, slice) {
var tooltip = document.createElementNS(namespaces.XHTML, "dialog");
var tooltipValueText = document.createElementNS(namespaces.XHTML, "data");
var tooltipDescriptionText = document.createTextNode(slice.tooltipInfoText.description);
var tooltipInfo = document.createElementNS(namespaces.XHTML, "p");
tooltip.setAttributeNS(namespaces.null, "id", "tooltip");
tooltip.setAttributeNS(namespaces.null, "open", "");
tooltip.style.setProperty("position", "absolute");
tooltip.style.setProperty("left", (cursorEvent.clientX + 15).toString() + "px");
tooltip.style.setProperty("top", cursorEvent.clientY.toString() + "px");
tooltipValueText.setAttributeNS(namespaces.null, "value", slice.percentage.toString());
tooltipValueText.textContent = slice.tooltipInfoText.value;
tooltipInfo.appendChild(tooltipValueText);
tooltipInfo.appendChild(tooltipDescriptionText);
tooltip.appendChild(tooltipInfo);
// This will break if the figure element is not statically positioned.
this.figure.appendChild(tooltip);
};
pieChart.moveTooltip = function (cursorEvent) {
var tooltip = document.getElementById("tooltip");
tooltip.style.setProperty("left", (cursorEvent.clientX + 15).toString() + "px");
tooltip.style.setProperty("top", cursorEvent.clientY.toString() + "px");
};
pieChart.getArcIntersectionXCoord = function (arcPercentage) {
// (radius * cos(angle)) + center‐x
return ((this.radius * Math.cos(arcPercentage * Math.completeArcAngle)) + this.centerX);
};
pieChart.getArcIntersectionYCoord = function (arcPercentage) {
// (radius * sin(angle)) + center‐y
return ((this.radius * Math.sin(arcPercentage * Math.completeArcAngle)) + this.centerY);
};
pieChart.drawSlice = function (sliceStartPercentage, sliceEndPercentage, sliceFill) {
var sliceArcStartXCoord = this.getArcIntersectionXCoord(sliceStartPercentage);
var sliceArcStartYCoord = this.getArcIntersectionYCoord(sliceStartPercentage);
var sliceStartAngle = (sliceStartPercentage * Math.completeArcAngle);
var sliceEndAngle = (sliceEndPercentage * Math.completeArcAngle);
this.context.save();
this.context.translate(this.centerX, this.centerY);
this.context.rotate(this.angleStartOffset);
this.context.translate(-this.centerX, -this.centerY);
this.context.beginPath();
this.context.moveTo(this.centerX, this.centerY);
this.context.lineTo(sliceArcStartXCoord, sliceArcStartYCoord);
this.context.closePath();
this.context.fillStyle = sliceFill;
this.context.fill();
this.context.restore();
};
pieChart.drawBasicChart = function () {
this.context.save();
this.context.fill();
this.context.restore();
var sliceIndex = 0;
var slice = null;
var sliceStartPercentage = 0;
var sliceEndPercentage = null;
while (sliceIndex < this.slices.length) {
slice = this.slices[sliceIndex];
sliceEndPercentage = (sliceStartPercentage + slice.percentage);
this.drawSlice(sliceStartPercentage, sliceEndPercentage, slice.fill);
sliceStartPercentage += slice.percentage;
sliceIndex += 1;
}
};
pieChart.drawStrokes = function () {
var sliceIndex = 0;
var slice = null;
var sliceStartPercentage = 0;
var sliceEndPercentage = null;
while (sliceIndex < this.slices.length) {
slice = this.slices[sliceIndex];
sliceEndPercentage = (sliceStartPercentage + slice.percentage);
this.drawSlice(sliceStartPercentage, sliceEndPercentage, "transparent");
this.context.stroke();
sliceStartPercentage += slice.percentage;
sliceIndex += 1;
}
};
pieChart.applyFocusEffects = function (canvasRelativeCursorXCoord, canvasRelativeCursorYCoord, cursorEvent) {
var sliceIndex = 0;
var slice = null;
var cursorAngleFromCenter = Math.angleFromOrigin(canvasRelativeCursorXCoord, canvasRelativeCursorYCoord, this.centerX, this.centerY);
var cursorOffsetAngleFromCenter = Math.offsetAngle(cursorAngleFromCenter, this.angleStartOffset);
var sliceEndPercentage = this.slices[0].percentage;
var sliceEndAngle = null;
var sliceStartPercentage = 0;
while (sliceIndex <= this.slices.length) {
slice = this.slices[sliceIndex];
sliceEndAngle = sliceEndPercentage * Math.completeArcAngle;
if (cursorOffsetAngleFromCenter < sliceEndAngle) {
sliceEndPercentage = (sliceStartPercentage + slice.percentage);
break;
}
sliceStartPercentage += slice.percentage;
sliceIndex += 1;
sliceEndPercentage += this.slices[sliceIndex].percentage;
}
if (slice !== this.focusedSlice) {
this.focusedSlice = slice;
this.context.clear();
this.drawBasicChart();
this.drawSlice(sliceStartPercentage, sliceEndPercentage, slice.focusFill);
this.drawStrokes();
this.destroyTooltip();
this.createTooltip(cursorEvent, slice);
}
else {
this.moveTooltip(cursorEvent);
}
};
pieChart.removeFocusEffects = function () {
this.focusedSlice = null;
this.context.clear();
this.drawBasicChart();
this.drawStrokes();
this.destroyTooltip();
};
pieChart.trackCursorMovement = function (cursorEvent) {
var canvasRect = this.canvas.getBoundingClientRect();
var canvasRelativeCursorXCoord = (cursorEvent.clientX - canvasRect.left);
var canvasRelativeCursorYCoord = (cursorEvent.clientY - canvasRect.top);
var distanceFromCenter = Math.distance(this.centerX, this.centerY, canvasRelativeCursorXCoord, canvasRelativeCursorYCoord);
this.applyFocusEffects(canvasRelativeCursorXCoord, canvasRelativeCursorYCoord, cursorEvent);
}
else if (this.focusedSlice !== null) {
this.removeFocusEffects();
}
};
pieChart.createPieChart = function () {
this.drawBasicChart();
this.context.lineWidth = this.strokeWidth;
this.context.lineJoin = this.strokeJoin;
this.drawStrokes();
this.captionElement.textContent = this.captionText;
};
marblesPieChart.figure = document.getElementById("marbles.pie.chart");
marblesPieChart.captionElement = marblesPieChart.figure.getElementsByTagNameNS(namespaces.XHTML, "figcaption").item(0);
marblesPieChart.captionText = "Pie Chart Representing Marbles by Color";
marblesPieChart.canvas = marblesPieChart.figure.getElementsByTagNameNS(namespaces.XHTML, "canvas").item(0);
marblesPieChart.context = marblesPieChart.canvas.getContext("2d");
marblesPieChart.xOffset = 10;
marblesPieChart.yOffset = 10;
marblesPieChart.centerX = 150;
marblesPieChart.centerY = 150;
marblesPieChart.angleStartOffset = (1.5 * Math.PI);
marblesPieChart.slices = [
{
percentage: 0.04,
fill: "hsla(340, 70%, 45%, 1)", // pink
focusFill: "hsla(340, 86%, 60%, 1)", // lighter pink
tooltipHeading: "Pie Slice Representing Pink Marbles",
tooltipInfoText: {
value: "Four percent",
description: " of marbles are pink."
}
},
{
percentage: 0.14,
fill: "hsla(210, 70%, 45%, 1)", // blue
focusFill: "hsla(210, 86%, 60%, 1)", // lighter blue
tooltipHeading: "Pie Slice Representing Blue Marbles",
tooltipInfoText: {
value: "14 percent",
description: " of marbles are blue."
}
},
{
percentage: 0.39,
fill: "hsla(50, 70%, 45%, 1)", // yellow
focusFill: "hsla(50, 86%, 60%, 1)", // lighter yellow
tooltipHeading: "Pie Slice Representing Yellow Marbles",
tooltipInfoText: {
value: "39 percent",
description: " of marbles are yellow."
}
},
{
percentage: 0.43,
fill: "hsla(120, 70%, 45%, 1)", // green
focusFill: "hsla(120, 86%, 60%, 1)", // lighter green
tooltipHeading: "Pie Slice Representing Green Marbles",
tooltipInfoText: {
value: "43 percent",
description: " of marbles are green."
}
}
];
marblesPieChart.shadowColor = "hsla(0, 0%, 0%, 1)"; // black
marblesPieChart.strokeWidth = 3;
marblesPieChart.strokeJoin = "round";
marblesPieChart.focusedSlice = null;
marblesPieChart.createPieChart();
})();
// ]]>
</script>
</section>
</body>
</html>```

2. A variant with an ellipse on this Pie Chart page...

Code:
```<!doctype html>
<html lang="fr">
<meta charset="utf-8">
<title>Pie Chart</title>
<style>
canvas {position:absolute; width:100%; height:100%; background:#333;overflow:hidden;}
</style>
<body>
<canvas id="cnv"></canvas>
<script>
/* Pie chart
An unique objet to define the pie chart
*/
var objItm=[{nat:'ten',val:10},{nat:'twenty',val:20},{nat:'thirty',val:30},{nat:'forty',val:40}];
var pieThk=30,mjrAxs=200,mnrAxs=130;;
/* Colors an old long script probably to update */
function HLS(h,l,s){this.h=h;this.l=l;this.s=s;this.toRGB=HLStoRGB;}
function RGB(r,g,b){this.r=bound(r);this.g=bound(g);this.b=bound(b);this.toHLS=RGBtoHLS;}
function bound(v) {return Math.min(1.0,Math.max(0.0,v));}
function getpartrgb(x,y,h){
if (h>=1.0) h-=1.0;if (h<0.0) h +=1.0;if (h<(1/6)) return (x+((y-x)*h*6.0));
if (h<0.5) return y;if (h<(2/3)) return (x+((y-x)*(4.0-(h*6.0))));
return x;}
function HLStoRGB(){var rgb=new RGB(0,0,0);if (this.s==0) rgb.r=rgb.g=rgb.b=this.l;
else if (this.l==0) rgb.r=rgb.g=rgb.b=0;
else {var x,y;if (this.l<=0.5) y=this.l*(1.0+this.s);
else y=this.l+this.s-(this.l*this.s);
x=(2.0*this.l)-y;rgb.r=getpartrgb(x,y,this.h+1/3);
rgb.g=getpartrgb(x,y,this.h);rgb.b=getpartrgb(x,y,this.h-1/3);
rgb.r=bound(rgb.r);rgb.g=bound(rgb.g);rgb.b=bound(rgb.b);}
return rgb;}
function RGBtoHLS(){var hls=new HLS(0,0,0);
var mmax=Math.max(this.r,Math.max(this.g,this.b));
var mmin=Math.min(this.r,Math.min(this.g,this.b));
var mdif=mmax-mmin;msom=mmin-(-mmax);
hls.l=msom/2.0;if (mdif < 0.00001)	{hls.h=0;hls.s=0;}
else {if (hls.l<0.5) hls.s=mdif/msom;
else hls.s=mdif/(2-msom);	mdif*=6.0;
if ((this.r>=this.g) && (this.r>=this.b))
hls.h=(this.g-this.b)/mdif;
else 	if (this.g >= this.b) hls.h=1/3+(this.b-this.r)/mdif;
else hls.h=2/3+(this.r-this.g)/mdif;
if (hls.h<0.0) hls.h+=1.0;else if (hls.h>=1.0) hls.h-=1.0;}
hls.h=bound(hls.h);hls.l=bound(hls.l);hls.s=bound(hls.s);return hls;
}
function RGBfromClr(s){if (s.substr(0,1)!='#') return null;
return new RGB(parseInt(s.substr(1,2),16)/255,parseInt(s.substr(3,2),16)/255,parseInt(s.substr(5,2),16)/255)}
function CfromRGB(c){return '#'+Hex(c.r*255)+Hex(c.g*255)+Hex(c.b*255);}
function Hex(n){var u=Math.round(n);if (u<0) u=0;if (255<u) u=255;return  Str(u>>4)+Str(u%16)}
function Str(n) {var r;if (n<10) r=n.toString();else r=String.fromCharCode(87+n);return r;}

// to build colors (clrItm) with the number of Items (we insert hues in HLS and convert in RGB)
var nbrItm=objItm.length;
var itm,clr=new HLS(0.77,0.5,0.8),clrItm=[],clrDlt=3/(nbrItm*4);
for (itm=0;itm<nbrItm;itm++) {clrItm[itm]=CfromRGB(clr.toRGB());clr.h+=clrDlt;if (1<clr.h) clr.h-=1;}

function drawEllipseFrmTo(x, y, a, b, rw, rh, ngl, clr, fill){
ctx.save();ctx.translate(x,y);ctx.rotate(ngl);ctx.scale(1,rh/rw);
ctx.beginPath();ctx.moveTo(0,0);
ctx.arc(0,0,rw,a,b,false);ctx.closePath();ctx.restore();
if (fill) {ctx.fillStyle=clr;ctx.fill();return}
ctx.strokeStyle=clr;ctx.stroke();
}
function drwPieChr(){
var ttlNgl=0,ttrA,ttrX,ttrY;
for (j=pieThk;0<j;j--) {ttlNgl=0;
for (itm=0;itm<nbrItm;itm++) drawEllipseFrmTo(0,j,ttlNgl,ttlNgl+=objItm[itm].val*Math.PI/50,mjrAxs,mnrAxs,0,clrItm[itm],true);}
for (itm=0;itm<nbrItm;itm++) {ttrA=ttlNgl+objItm[itm].val*Math.PI/100,ttrX=120*Math.cos(ttrA),ttrY=120*Math.sin(ttrA)*mnrAxs/mjrAxs
drawEllipseFrmTo(0,0,ttlNgl,ttlNgl+=objItm[itm].val*Math.PI/50,mjrAxs,mnrAxs,0,'#fff',false);
ctx.strokeText(objItm[itm].nat,ttrX-ctx.measureText(objItm[itm].nat).width/2,ttrY)
}
ctx.strokeStyle="#000";
if (pieMsg) {ctx.strokeText(pieMsg,-ctx.measureText(pieMsg).width/2,-20);}
requestAFrame(drwPieChr);
}
var cnv,ctx,cnvWdt,cnvHgt,pieMsg='';
function resize(){
cnv=document.getElementById('cnv');// Dimensionner le canvas avant
cnvWdt=cnv.width=document.body.clientWidth;
cnvHgt=cnv.height=document.body.clientHeight;
ctx=cnv.getContext('2d');
ctx.translate(cnvWdt>>1,cnvHgt>>1);
ctx.font="15px Segoe UI,Verdana,Arial,Sans-Serif";
}
window.onmousemove=function(e){
var isx=e.clientX,isy=e.clientY;
var dx=(isx-cnvWdt/2)/mjrAxs,dy=(isy-cnvHgt/2)/mnrAxs;
//	if (window.console) console.log(dx+' '+dy);
if (1<dx*dx+dy*dy) {pieMsg='';return}
var itm,ngl=Math.atan2(dy,dx),ttlItm=0;
if (ngl<0) ngl+=2*Math.PI;
itm=0;while ((ttlItm+=objItm[itm].val*Math.PI/50)<ngl) itm++;
pieMsg='The mouse is over the sector '+(objItm[itm].nat);
}

resize();
ctx.strokeStyle="#fff";
ctx.lineWidth=1;
drwPieChr();
}

// Define requestAFrame
window.requestAFrame=(function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function (callback) {return window.setTimeout(callback,1000/60)}
})();
</script>
</body>
</html>```
I'm not proud of taking in to account the thickness of the Pie hart...

Page 2 of 2 First 12

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•