a web component (using lit-element) for rating stars - web-component

It can be used to rate something.
The rating is already available (e.g. average of ratings provided by large number of users), we just need to depict the current rating by using stars (consider value up to 1 decimal point in case of rating in decimals)

Here is a vanilla Web Component version from my DEV.to post
For a Lit version, you only have to add some extra lines of code...
customElements.define("star-rating", class extends HTMLElement {
set rating(rate) {
if (!String(rate).includes("%")) rate = Number(rate) / this.stars * 100 + "%";
this.querySelector(":nth-child(2)").setAttribute("width", rate); //2nd rect
}
connectedCallback() {
let {bgcolor,stars,nocolor,color,rating} = this.attributes;
let repeat = (count, func) => Array(count).fill().map(func);
this.stars = ~~stars.value || 5;
this.innerHTML = `<svg viewBox="0 0 ${this.stars*100} 100" style=cursor:pointer>` +
`<rect height=100 fill=${nocolor.value} width=100% />` +
`<rect height=100 fill=${color.value} />` +
repeat(this.stars , (i, n) => `<path fill=${bgcolor.value} d="m${ n*100 } 0h102v100h-102v-100m91 42a6 6 90 00-4-10l-22-1a1 1 90 01-1 0l-8-21a6 6 90 00-11 0l-8 21a1 1 90 01-1 1l-22 1a6 6 90 00-4 10l18 14a1 1 90 010 1l-6 22a6 6 90 008 6l19-13a1 1 90 011 0l19 13a6 6 90 006 0a6 6 90 002-6l-6-22a1 1 90 010-1z"/>`) +
repeat(this.stars * 2, (i, n) => `<rect x=${ n*50 } n=${n} opacity=0 width=50 height=100 ` +
` onclick="this.closest('star-rating').dispatchEvent(new Event('click'))" ` +
` onmouseover="this.closest('star-rating').rating=${(n+1)/2}"/>`) +
"</svg>";
this.rating = rating.value;
}
});
<star-rating stars=5 rating="3.2"
bgcolor="green" nocolor="grey" color="gold"></star-rating>
<br>
<star-rating stars=7 rating="50%"
bgcolor="rebeccapurple" nocolor="beige" color="goldenrod"></star-rating>

Related

SCILAB code for searching the center of figure

*Hello everyone, I need your help, I'm looking for the center of the figure in green in terms of a point with these coordinates (X, Y) that I posted on the following link: https://imgur.com/6841jk4
I tried to apply Guldin's method? ? , look for the center of gravity, the moment of inertia but in vain.
could you help me and provide me the code in scilab, because I've been looking for the solution in terms of code and mathematical analysis for a long time.
you will find attached the scilab code.
enter code here
function y = h(x)
if x < 50 | 210 < x then
error("Out of range");
elseif x <= 90 then
y= -57.376067 +9.3746343*x -0.2175008*x^2 +0.0013792*x^3
//disp('50-90')
return;
elseif x <=100 then
y= 10330.932 -336.90229*x +3.6300206*x^2 -0.0128709*x^3;
//disp('90-100')
elseif x <= 130 then
y=-6387.7416 +164.65791*x -1.3855814*x^2 +0.0038478*x^3;
//disp('100-130')
return;
else
y = 5028.1996 -98.786888*x +0.640917*x^2 -0.0013484*x^3;
//disp('130-210')
end
endfunction
t=[50:210];
plot(t,feval(t,h),'r*')
l=[50 60 90 100 130 150 210]
k=[40 20 30 70 55 80 60]
plot(l,k,'d')
for i=[40 20 30 70 55 80 60]
teta=[0: 220]
beta=linspace(100,100,221)
plot(teta,beta,'*')
teta1=[100:160:221]
beta1=linspace(100,160-2*rand(),221)
plot2d3(teta1,beta1)
end
a=gca()
a.sub_ticks = [5,5]
a.grid_thickness = [0.05,0.05];
a.grid = [-1,-1]
a.grid_position = "foreground"
//a.grid_thickness = [0.05,0.05]
xgrid(0)
C=[50 60 90 100 130 150 210]
//for j=1:size(C,'c')
//C(j)
//if c(k)<=50 then
// (m=k+1& c(m))
J=numderivative(h,t) /*jacobien*/
//f=C(j)
// J=numderivative(h,i) /*jacobien*/
deff('[z] = h2(k)', 'z = h(k)-100');
// disp(C(j))
//for i=[157.56011:204.1084]
[x,fx,v]=fsolve([150,200],h2)
disp(x)
disp(fx)
plot(x,fx+100,)
disp(v)
//plot(x,h2,'d')
//end
//po=[50 60 90 100 130 150 210]
//co=[40 20 30 70 55 80 60]
lh=linspace(157.56011,204.1084);
lpo=feval(lh,h);
xfpoly(lh,lpo)
e=gce()
e.background=13

Calculate position of a point on an arc

I'm building a gauge. I took off from an example that was a half circle, see from the image:
To transform percentage into the angle the original chart had these three functions:
percToDeg: function (perc) {
return perc * 360;
},
percToRad: function (perc) {
return this.degToRad(this.percToDeg(perc));
},
degToRad: function (deg) {
return deg * Math.PI / 180;
},
Now this all looks and works great, however I wanted to adjust the gauge so that the arc extends another 45 degrees in both directions, see this:
However now percToDeg function doesn't work anymore. Can you please help me figure out a function that for a given percentage places the point (tip of the needle in my case) of on the arc correctly - 0% should be 225 degrees, 50% 90 degrees and 100% -45 degrees?
Thanks
You want to use something called linear interpolation, with your starting range being [0, 100] and your destination range being [225, -45].
The general equation for this, for an x in range [a, b] to y in range [c, d]
y = c + (x - a) * (d - c) / (b - a)
In your case,
a = 0
b = 100
c = 225
d = -45
For example, if you want to find where 50 maps to in the range:
y = 225 + (50 - 0) * (-45 - 225) / (100 - 0)
y = 225 + 50 * -270 / 100
y = 225 - 135
y = 90

An equation which gives me 10 = 0 but 100 = 100

In one line, how to get an equation which will give me 0 if I pass 10, but 100 if I pass 100?
So that, for the following numbers, the value will be something like:
10 -> 0
100 -> 100
Of course my number can be anything between 10 to 100.
int input = ...;
int result = (input <= 10 ? 0 : 100 * (input - 10) / 90);
Finally I got my answer, the equation is something like this:
X = (Y*10-100)/9
Now try putting any value between 10 to 100 in it.
You can do
if (i == 0) return 10;
if (i == 100) return 100;
Anything else is left to your imagination.
return i * 9 / 10 + 10;
or
return (i * i + 1110) / 111;

Calculate correct sprite direction image in bird's view game? (Math here might be speed vector to degrees angle?)

Background: I have 8 images for every sprite in my bird's view JavaScript game, representing top, top-right, right, right-bottom etc., depending on the player's space ship speed.
Question: Given the values sprite.speed.x and sprite.speed.y (which could be something like 4 and -2.5, or 2 and 0 for instance), how do I get the correct angle in degrees? Given that angle, I could then have a lookup for which degrees value represents which sprite image. Or perhaps there's an even easier way. (Currently I'm just using something like "if x below zero use left image" etc. which will result in diagonal images used almost all of the time.)
Searching around, I found ...
angle = Math.atan2(speed.y, speed.x);
... but somehow I'm still missing something.
PS: Zero speed can be ignored, these sprites will just use whatever was the last valid direction image.
Thanks so much for any help!
Good question! I liked tom10's answer (on the mark, +1), but wondered if it can be done without much trigonometry. Here's a solution in short, followed by an explanation.
// slope is a constant, 0.414...; calculate it just once
var slope = Math.tan(Math.PI/8);
// do this for each x,y point
var s1 = x * slope + y > 0 ? 0 : 1;
var s2 = y * slope + x > 0 ? 0 : 1;
var s3 = y * slope - x < 0 ? 0 : 1;
var s4 = x * slope - y > 0 ? 0 : 1;
var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
This sets the value of segment between 0 and 7. Here's an example with 2000 random points (full source code at the end of the answer). Using the x,y values of the sprite's speed, you can use the segment value to pick up the appropriate sprite image.
Tadaa!
So how does this work? Our segment expression does look a bit cryptic.
Observation one: we want to split the circle around the point into 8 segments of equal angular dimension. 360/8 = 45 degrees per segment. Four of the 8 segments are centered on one of the two sides of the x and y axes, sliced at 45/2 = 22.5 degrees each.
Observation two: The equation of a line on a plane, a*x + b*y + c = 0, when turned into an inequality, a*x + b*y + c > 0 can be used to test on which side of the line a point is located. All our four lines cross the origin (x=0, y=0), and hence force c=0. Further, they are all at a 22.5 degrees angle from either the x or the y axis. This gets us the four line equations:
y = x * tan(22.5); y = -x * tan(22.5);
x = y * tan(22.5); x = -y * tan(22.5)
Turned into inequalities we get:
x * tan(22.5) - y > 0;
x * tan(22.5) + y > 0;
y * tan(22.5) - x > 0;
y * tan(22.5) + x > 0
Testing the inequalities for a given point lets us know on each side of each line it lies:
Observation three: we can combine the test results to obtain the segment number pattern we want. Here's a visual breakdown:
In sequence: 4 * s4, 2 * (s2 ^ s4) and the sum 4 * s4 + 2 * (s2 ^ s4)
(The ^ symbol is the Javascript XOR operator.)
And here is s1 ^ s2 ^ s3 ^ s4, first on its own, and then added to 4 * s4 + 2 * (s2 ^ s4)
Extra credit: can we tweak the calculation to use only integer arithmetic? Yes -- if x and y are known to be integers, we could multiply both sides of the inequalities by some constant (and round off), resulting in completely integer math. (This would be lost, however, on Javascript, whose numbers are always double precision floating point.):
var s1 = x * 414 + y * 1000 > 0 ? 0 : 1;
var s2 = y * 414 + x * 1000 > 0 ? 0 : 1;
var s3 = y * 414 - x * 1000 < 0 ? 0 : 1;
var s4 = x * 414 - y * 1000 > 0 ? 0 : 1;
Full source code for our sample above: (just drop it in a new html file, and open in any browser)
(see as a live demo on jsbin)
<html>
<head>
<style type="text/css">
.dot { position: absolute; font: 10px Arial }
.d0 { color: #FF0000; }
.d1 { color: #FFBF00; }
.d2 { color: #7fcc00; }
.d3 { color: #00FF7F; }
.d4 { color: #00FFFF; }
.d5 { color: #5555FF; }
.d6 { color: #aF00FF; }
.d7 { color: #FF00BF; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
var $canvas = $("#canvas");
var canvasSize = 300;
var count = 2000;
var slope = Math.tan(Math.PI/8);
$canvas.css({ width: canvasSize, height: canvasSize });
for (var i = 0; i < count; ++i) {
// generate a random point
var x = Math.random() - 0.5;
var y = Math.random() - 0.5;
// draw our point
var $point = $("<div class='dot'></div>")
.css({
left: Math.floor((x + 0.5) * canvasSize) - 3,
top: Math.floor((y + 0.5) * canvasSize) - 6 })
.appendTo($canvas);
// figure out in what segment our point lies
var s1 = x * slope + y > 0 ? 0 : 1;
var s2 = y * slope + x > 0 ? 0 : 1;
var s3 = y * slope - x < 0 ? 0 : 1;
var s4 = x * slope - y > 0 ? 0 : 1;
var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
// modify the point's html content and color
// (via its CSS class) to indicate its segment
$point
.text(segment)
.addClass("d" + segment);
}
});
</script>
</head>
<body>
<div id="canvas" style="position: absolute; border: 1px solid blue">
</div>
</body>
</html>
What you suggest is exactly right! Note that the result of Math.atan2 is in radians, and you're probably more familiar with degrees; you can convert using angle_degrees = angle*(180./pi).
(Note also that you don't need to normalize as RCIX suggested, though you can if you want to. What you have, angle = Math.atan2(speed.y, speed.x);, should work just fine.)
You were on the right track. Normalize your speed vector (check for both components being 0 first) , call atan2 on it, and then convert the radians value you get to some sort of friendly direction enum or something that you can use to pick the right sprite.

How to map atan2() to degrees 0-360

atan2(y, x) has that discontinuity at 180° where it switches to -180°..0° going clockwise.
How do I map the range of values to 0°..360°?
here is my code:
CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);
I'm calculating the direction of a swiping touch event given the startPoint and endPoint, both XY point structs. The code is for the iPhone but any language that supports atan2f() will do.
Solution using Modulo
A simple solution that catches all cases.
degrees = (degrees + 360) % 360; // +360 for implementations where mod returns negative numbers
Explanation
Positive: 1 to 180
If you mod any positive number between 1 and 180 by 360, you will get the exact same number you put in. Mod here just ensures these positive numbers are returned as the same value.
Negative: -180 to -1
Using mod here will return values in the range of 180 and 359 degrees.
Special cases: 0 and 360
Using mod means that 0 is returned, making this a safe 0-359 degrees solution.
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
Add 360° if the answer from atan2 is less than 0°.
Or if you don't like branching, negate the two parameters and add 180° to the answer.
(Adding 180° to the return value puts it nicely in the 0-360 range, but flips the angle. Negating both input parameters flips it back.)
#erikkallen is close but not quite right.
theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);
This should work in C++: (depending on how fmod is implemented, it may be faster or slower than the conditional expression)
theta_deg = fmod(atan2(y,x)/M_PI*180,360);
Alternatively you could do this:
theta_deg = atan2(-y,-x)/M_PI*180 + 180;
since (x,y) and (-x,-y) differ in angles by 180 degrees.
I have 2 solutions that seem to work for all combinations of positive and negative x and y.
1) Abuse atan2()
According to the docs atan2 takes parameters y and x in that order. However if you reverse them you can do the following:
double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
degrees += 360;
}
2) Use atan2() correctly and convert afterwards
double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
degrees = 450 - degrees;
}
else
{
degrees = 90 - degrees;
}
#Jason S: your "fmod" variant will not work on a standards-compliant implementation. The C standard is explicit and clear (7.12.10.1, "the fmod functions"):
if y is nonzero, the result has the same sign as x
thus,
fmod(atan2(y,x)/M_PI*180,360)
is actually just a verbose rewriting of:
atan2(y,x)/M_PI*180
Your third suggestion, however, is spot on.
Here's some javascript. Just input x and y values.
var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
This is what I normally do:
float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
An alternative solution is to use the mod () function defined as:
function mod(a, b) {return a - Math.floor (a / b) * b;}
Then, with the following function, the angle between ini(x,y) and end(x,y) points is obtained. The angle is expressed in degrees normalized to [0, 360] deg. and North referencing 360 deg.
function angleInDegrees(ini, end) {
var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
return mod(radian * 180 / Math.PI + 90, 360);
}
angle = Math.atan2(x,y)*180/Math.PI;
I have made a Formula for orienting angle into 0 to 360
angle + Math.ceil( -angle / 360 ) * 360;
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);
This will return degree from 0°-360° counter-clockwise, 0° is at 3 o'clock.
A formula to have the range of values from 0 to 360 degrees.
f(x,y)=180-90*(1+sign(x))* (1-sign(y^2))-45*(2+sign(x))*sign(y)
-(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
The R packages geosphere will calculate bearingRhumb, which is a constant bearing line given an origin point and easting/northing. The easting and northing must be in a matrix or vector. The origin point for a wind rose is 0,0. The following code seems to readily resolve the issue:
windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)
theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
theta_rad = theta_rad + 2 * Math.PI; //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ; //convert from radian to degree
//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;
-1 deg becomes (-1 + 360) = 359 deg
-179 deg becomes (-179 + 360) = 181 deg
For your application I suspect you don't need exact degrees and would prefer a more approximate compass angle, eg 1 of 16 directions? If so then this code avoids atan issues and indeed avoids floating point altogether. It was written for a video game so uses 8 bit and 16 bit integers:
/*
349.75d 11.25d, tan=0.2034523
\ /
\ Sector /
\ 0 / 22.5d tan = ?2 - 1
15 | 1 33.75
| / 45d, tan = 1
14 | 2 _56.25
| / 67.5d, tan = 1 + ?2
13 | 3
| __ 78.75
|
12---------------+----------------4 90d tan = infty
| __ 101.25
|
11 | 5
|
10 | 6
|
9 | 7
8
*/
// use signs to map sectors:
static const int8_t map[4][5] = { /* +n means n >= 0, -n means n < 0 */
/* 0: +x +y */ {0, 1, 2, 3, 4},
/* 1: +x -y */ {8, 7, 6, 5, 4},
/* 2: -x +y */ {0, 15, 14, 13, 12},
/* 3: -x -y */ {8, 9, 10, 11, 12}
};
int8_t sector(int8_t x, int8_t y) { // x,y signed in range -128:127, result 0:15 from north, clockwise.
int16_t tangent; // 16 bits
int8_t quadrant = 0;
if (x > 0) x = -x; else quadrant |= 2; // make both negative avoids issue with negating -128
if (y > 0) y = -y; else quadrant |= 1;
if (y != 0) {
// The primary cost of this algorithm is five 16-bit multiplies.
tangent = (int16_t)x*32; // worst case y = 1, tangent = 255*32 so fits in 2 bytes.
/*
determine base sector using abs(x)/abs(y).
in segment:
0 if 0 <= x/y < tan 11.25 -- centered around 0 N
1 if tan 11.25 <= x/y < tan 33.75 -- 22.5 NxNE
2 if tan 33.75 <= x/y < tan 56.25 -- 45 NE
3 if tan 56.25 <= x/y < tan 78.75 -- 67.5 ExNE
4 if tan 78.75 <= x/y < tan 90 -- 90 E
*/
if (tangent > y*6 ) return map[quadrant][0]; // tan(11.25)*32
if (tangent > y*21 ) return map[quadrant][1]; // tan(33.75)*32
if (tangent > y*47 ) return map[quadrant][2]; // tan(56.25)*32
if (tangent > y*160) return map[quadrant][3]; // tan(78.75)*32
// last case is the potentially infinite tan(90) but we don't need to check that limit.
}
return map[quadrant][4];
}

Resources