I'm struggling with the problem of showing additional serie that will contains maximum values for every x. Like this:
I was able to parse all series to aggregate points and also found cross points to include them. Currently I have a problem with cross point (A) that are below one of the series. (Solution A in fiddle)I was thinking about calculating line and check if the point A belongs to that line. This will fix scenario but will cause problem for point B bc it doesn't belong to calculated line but should be included in serie.
Could any one point me to the right direction?
var chart = Highcharts.chart('container', {
chart: {
type: 'area',
zoomType: 'x',
backgroundColor:null,
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%b %e',
year: '%b'
},
},
plotOptions: {
series: {
fillOpacity: 0.1,
marker: {
enabled: false
}
}
}
});
chart.addSeries({"id":0,"connectNulls":true,"data":[[1524469020000,30],[1524469080000,30],[1524469140000,30],[1524469200000,30],[1524469260000,30],[1524469320000,30],[1524469380000,30],[1524469440000,30],[1524469500000,30],[1524469560000,58],[1524469620000,4],[1524469680000,4],[1524469740000,4],[1524469800000,4],[1524469860000,4],[1524469920000,4],[1524469980000,4],[1524470040000,4],[1524470100000,4],[1524470160000,4],[1524470220000,4],[1524470280000,4],[1524470340000,4],[1524470400000,4],[1524470460000,4],[1524470520000,22],[1524470580000,22],[1524470640000,22],[1524470700000,22]],"name":"Serie A","color":"#30e430","yAxis":0});
chart.addSeries({"id":1,"connectNulls":true,"data":[[1524469020000,35],[1524469080000,35],[1524469140000,35],[1524469200000,35],[1524469260000,35],[1524469320000,35],[1524469380000,35],[1524469440000,35],[1524469500000,35],[1524469560000,25],[1524469620000,25],[1524469680000,25],[1524469740000,25],[1524469800000,25],[1524469860000,25],[1524469920000,25],[1524469980000,59],[1524470040000,59],[1524470100000,59],[1524470160000,59],[1524470220000,59],[1524470280000,59],[1524470340000,59],[1524470400000,59],[1524470460000,59],[1524470520000,59],[1524470580000,59],[1524470640000,59],[1524470700000,59]],"name":"Serie B","color":"#0cb5ed","yAxis":0});
chart.addSeries({"id":2,"connectNulls":true,"data":[[1524469020000,18],[1524469080000,18],[1524469140000,18],[1524469200000,18],[1524469260000,18],[1524469320000,18],[1524469380000,18],[1524469440000,18],[1524469500000,18],[1524469560000,18],[1524469620000,18],[1524469680000,18],[1524469740000,18],[1524469800000,18],[1524469860000,18],[1524469920000,18],[1524469980000,18],[1524470040000,18],[1524470100000,18],[1524470160000,18],[1524470220000,18],[1524470280000,18],[1524470340000,18],[1524470400000,18],[1524470460000,18],[1524470520000,18],[1524470580000,18],[1524470640000,18],[1524470700000,18]],"name":"Serie C","color":"#e8ad23","yAxis":0});
$('#button').click(function () {
var results = getChartPointValues();
var data = prepareSummarySeries(results);
//clean previously added virutal series
for(var i in chart.series){
var serie = chart.series[i];
if(serie.userOptions.is_virtual == true)
serie.remove();
}
chart.addSeries({
id: 'virtual_max_b',
is_virtual: true,
'connectNulls': true,
'data' : data.max_b,
'name' : 'Solution A',
'color' : '#000',
'yAxis': 0,
});
chart.addSeries({
id: 'virtual_max_a',
is_virtual: true,
'connectNulls': true,
'data' : data.max_a,
'name' : 'Base Solution',
'color' : '#ff0000',
'yAxis': 0,
});
});
/*
* Calculate max values for every point
*/
var prepareSummarySeries = function(data){
var tmp_keys = Object.keys(data); ///sort
tmp_keys = tmp_keys.sort();
var counter = tmp_keys.length;
var results = {
'max_a': [],
'max_b': [],
};
for(var k = 0; k < counter; k++){
var key = tmp_keys[k];
var x_pos = parseFloat(key);
if(x_pos % 1 !== 0)
{
var prev_point = results.max_b.slice(-1)[0];
var current_point = [x_pos, data[key][0]];
var next_point = [ parseFloat(tmp_keys[k+1]), Math.max.apply(null, data[tmp_keys[k+1]] )];
if( checkIfPointBelongsToChart(prev_point, current_point, next_point) ){
results.max_b.push([ x_pos, current_point[1] ]);
}
} else {
results.max_b.push([ x_pos, Math.max.apply(null, data[key]) ]);
}
results.max_a.push([ x_pos, Math.max.apply(null, data[key]) ]);
}
return results;
};
var get_line_intersection = function(p0,p1,p2,p3){
var p0_x = p0.x;
var p0_y = p0.y;
var p1_x = p1.x;
var p1_y = p1.y;
var p2_x = p2.x;
var p2_y = p2.y;
var p3_x = p3.x;
var p3_y = p3.y;
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
var s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
var t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
return [p0_x + (t * s1_x),p0_y + (t * s1_y)];
}
return false;
};
var checkIfPointBelongsToChart = function(prev_point, current_point, next_point)
{
var slope = (next_point[1] - prev_point[1]) / (next_point[0] - prev_point[0]);
var b = prev_point[1] - slope * prev_point[0];
var tmp_y = slope * current_point[0] + b;
return (tmp_y == current_point[1])? true : false;
};
// create array with every x point with every possible y value
// im not taking only max here bc later we could need min instead of max
var getChartPointValues = function(){
var results = {}
var self = this;
var points = [];
var checked_series = [];
for(var k =0; k < chart.series.length; k++)
{
var entry = chart.series[k];
if(entry.userOptions.is_virtual == true || entry.visible == false)
continue;
var s1_points = entry.data;
var c1 = s1_points.length;
//add all points from serie
for(var i = 0; i < c1; i++)
{
if(s1_points[i] == undefined || !s1_points[i].isInside)
continue;
if(points[s1_points[i].x] == undefined){
points[s1_points[i].x] = [];
}
points[s1_points[i].x].push(s1_points[i].y);
}
//check all points in all charts for crossing points
for(var s in chart.series){
var serie = chart.series[s];
if(serie.userOptions.is_virtual == true || serie.visible == false)
continue;
//skip serie if combination of series was already checked
var current_check = entry.userOptions.id + '_' + serie.userOptions.id;
if(checked_series.indexOf(current_check) != -1 || serie.userOptions.is_virtual == true)
continue;
checked_series.push(current_check);
checked_series.push(serie.userOptions.id + '_' + entry.userOptions.id);
if(serie.index != entry.index){
var s2_points = serie.data;
var c2 = s2_points.length;
for(var i = 0; i < c1; i++)
{
if(s1_points[i] == undefined || !s1_points[i].isInside)
continue;
for(var j = 0; j < c2; j++)
{
if(s2_points[j] == undefined || !s2_points[j].isInside)
continue;
var cross = [];
if(s1_points[i-1] != undefined && s2_points[j-1] != undefined){
if(cross = get_line_intersection(s1_points[i-1], s1_points[i], s2_points[j-1], s2_points[j])){
if(points[cross[0]] == undefined){
points[cross[0]] = [];
}
points[cross[0]].push(cross[1])
}
}
}
}
}
};
};
return points;
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<button id="button">Add Maximum series</button>
<div id="container" style="height: 400px"></div>
jsFiddle
I have tried to solve this using a slightly different method. The gist of it is:
Find the series that has the max point
Find the series that has the next max point
Check if there is an intersect between the lines of these two series
If there is an intersect, add that as a separate point
Add the next max point
There is one caveat with doing it like this, a sparse series has the ability to ruin the graph, like this: https://jsfiddle.net/ewolden/3koe86sx/15/
Solving that issue would be tedious, requiring you to find all x points, and for lines without all points present, fill in their values.
That being said, as long as the series have roughly the same number of points, this should work well.
var get_line_intersection = function(p0, p1, p2, p3) {
var p0_x = p0.x;
var p0_y = p0.y;
var p1_x = p1.x;
var p1_y = p1.y;
var p2_x = p2.x;
var p2_y = p2.y;
var p3_x = p3.x;
var p3_y = p3.y;
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x;
s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x;
s2_y = p3_y - p2_y;
var s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
var t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
return [p0_x + (t * s1_x), p0_y + (t * s1_y)];
}
return false;
};
//Gets the next point
function getNextPoint(series, current_x, current_y) {
nextPoint = {
next_x: 0,
next_y: 0,
x: 0,
y: -1,
prev_x: 0,
prev_y: 0
}
for (var i = 0; i < series.length; i++) {
for (var j = 1; j < series[i].length; j++) {
if (series[i][j].x > current_x) { //Goes one step past current timestamp
if (series[i][j].y > nextPoint.y || nextPoint.y == -1) { //Checks that this is the max number, could be changed to find min as well
if (j < series[i].length - 1) { //Checks if this is the last point (to avoid going past last index)
nextPoint = {
next_x: series[i][j + 1].x,
next_y: series[i][j + 1].y,
x: series[i][j].x,
y: series[i][j].y,
prev_x: series[i][j - 1].x,
prev_y: series[i][j - 1].y,
}
} else {
nextPoint = {
x: series[i][j].x,
y: series[i][j].y,
prev_x: series[i][j - 1].x,
prev_y: series[i][j - 1].y,
}
}
}
break;
}
}
}
return nextPoint
}
function getAllSeries(chart) {
var allSeries = []
for (var i = 0; i < chart.series.length; i++) {
allSeries.push(chart.series[i].data)
}
return allSeries
}
var chart = Highcharts.chart('container', {
chart: {
type: 'area',
zoomType: 'x',
backgroundColor: null,
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%b %e',
year: '%b'
},
},
plotOptions: {
series: {
fillOpacity: 0.1,
marker: {
enabled: false
}
}
}
});
chart.addSeries({
"id": 0,
"connectNulls": true,
"data": [
[1524469020000, 30],
[1524469080000, 30],
[1524469140000, 30],
[1524469200000, 30],
[1524469260000, 30],
[1524469320000, 30],
[1524469380000, 30],
[1524469440000, 30],
[1524469500000, 30],
[1524469560000, 58],
[1524469620000, 4],
[1524469680000, 4],
[1524469740000, 4],
[1524469800000, 4],
[1524469860000, 4],
[1524469920000, 4],
[1524469980000, 4],
[1524470040000, 4],
[1524470100000, 4],
[1524470160000, 4],
[1524470220000, 4],
[1524470280000, 4],
[1524470340000, 4],
[1524470400000, 4],
[1524470460000, 4],
[1524470520000, 22],
[1524470580000, 22],
[1524470640000, 22],
[1524470700000, 22]
],
"name": "Serie A",
"color": "#30e430",
"yAxis": 0
});
chart.addSeries({
"id": 1,
"connectNulls": true,
"data": [
[1524469020000, 35],
[1524469080000, 35],
[1524469140000, 35],
[1524469200000, 35],
[1524469260000, 35],
[1524469320000, 35],
[1524469380000, 35],
[1524469440000, 35],
[1524469500000, 35],
[1524469560000, 25],
[1524469620000, 25],
[1524469680000, 25],
[1524469740000, 25],
[1524469800000, 25],
[1524469860000, 25],
[1524469920000, 25],
[1524469980000, 59],
[1524470040000, 59],
[1524470100000, 59],
[1524470160000, 59],
[1524470220000, 59],
[1524470280000, 59],
[1524470340000, 59],
[1524470400000, 59],
[1524470460000, 59],
[1524470520000, 59],
[1524470580000, 59],
[1524470640000, 59],
[1524470700000, 59]
],
"name": "Serie B",
"color": "#0cb5ed",
"yAxis": 0
});
chart.addSeries({
"id": 2,
"connectNulls": true,
"data": [
[1524469020000, 18],
[1524469080000, 18],
[1524469140000, 18],
[1524469200000, 18],
[1524469260000, 18],
[1524469320000, 18],
[1524469380000, 18],
[1524469440000, 18],
[1524469500000, 18],
[1524469560000, 18],
[1524469620000, 18],
[1524469680000, 18],
[1524469740000, 18],
[1524469800000, 18],
[1524469860000, 18],
[1524469920000, 18],
[1524469980000, 18],
[1524470040000, 18],
[1524470100000, 18],
[1524470130000, 80],
[1524470160000, 18],
[1524470220000, 18],
[1524470280000, 18],
[1524470340000, 18],
[1524470400000, 18],
[1524470460000, 18],
[1524470520000, 18],
[1524470580000, 18],
[1524470640000, 18],
[1524470700000, 18]
],
"name": "Serie C",
"color": "#e8ad23",
"yAxis": 0
});
$('#button').click(function() {
series = getAllSeries(chart)
var currentPoint = {
next_x: 0,
next_y: 0,
x: -1,
y: 0,
prev_x: 0,
prev_y: 0
}
var max_x = 0;
//finds the first point
for (var i = 0; i < series.length; i++) {
if (currentPoint.y < series[i][0].y || currentPoint.x == -1) { //makes sure this is the largest point
currentPoint = {
prev_x: series[i][0].x,
prev_y: series[i][0].y,
x: series[i][0].x,
y: series[i][0].y,
next_x: series[i][1].x,
next_y: series[i][1].y
}
}
if (max_x < series[i][series[i].length - 1].x) {
max_x = series[i][series[i].length - 1].x;
}
}
result = []
result.push({ //since the first point comes from the above code, we need to add it explicitly
x: currentPoint.x,
y: currentPoint.y
})
while (currentPoint.x != max_x) { //loop through all points
nextPoint = getNextPoint(series, currentPoint.x, currentPoint.y);
let intersect = get_line_intersection({
x: nextPoint.prev_x,
y: nextPoint.prev_y
}, {
x: nextPoint.x,
y: nextPoint.y
}, {
x: currentPoint.x,
y: currentPoint.y
}, {
x: currentPoint.next_x,
y: currentPoint.next_y
})
if (intersect != false) { //if there is an intersect point, make sure to add it
result.push({
x: intersect[0],
y: intersect[1]
})
}
result.push({
x: nextPoint.x,
y: nextPoint.y
});
currentPoint = nextPoint
}
chart.addSeries({
name: 'Max Points',
lineColor: 'red',
//dashStyle: 'LongDash',
data: result
})
})
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<button id="button">Add Maximum series</button>
<div id="container" style="height: 400px"></div>
jsfiddle example: https://jsfiddle.net/ewolden/3koe86sx/14/
Related
I'm making a Tetris game with javascript, but there's a bug I haven't been able to fix yet. It only happens when pieces Z, S, and T collide with an object while going down. I thought the problem was with the 3rd row of 0s, but the other pieces draw fine when I add it to other pieces. I also can't remove the rows because I need them to rotate the pieces.
If anyone can find the error I'd really appreciate it! Thank you.
Here's the code on repl.it:
https://repl.it/#julkothegu1/Oke-Oke
Raw:
const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
context.scale(20, 20);
function arena_sweep(){
let row_count = 1
outer: for (let y = arena.length -1; y > 0; y--){
for (let x = 0; x < arena[y].length; x++){
if (arena[y][x] == 0){
continue outer
}
}
const row = arena.splice(y, 1)[0].fill(0);
arena.unshift(row);
++y;
player.score += row_count * 10
row_count *= 2
}
}
function draw(){
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
draw_matrix(arena, {x:0, y:0})
draw_matrix(player.matrix, player.pos)
}
function create_matrix(w, h){
const matrix = []
while (h--){
matrix.push(new Array(w).fill(0))
}
return matrix
}
function player_reset(){
const pieces = 'ILJOZST'
player.matrix = create_piece(pieces[pieces.length * Math.random() | 0])
player.pos.y = 0
player.pos,x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0)
if (collide(arena, player)){
arena.forEach(row => row.fill(0))
player.score = 0
update_score()
}
}
function create_piece(type)
{
if (type === 'I') {
return [
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
];
} else if (type === 'L') {
return [
[0, 2, 0],
[0, 2, 0],
[0, 2, 2],
];
} else if (type === 'J') {
return [
[0, 3, 0],
[0, 3, 0],
[3, 3, 0],
];
} else if (type === 'O') {
return [
[4, 4],
[4, 4],
];
} else if (type === 'Z') {
return [
[5, 5, 0],
[0, 5, 5],
[0, 0, 0],
];
} else if (type === 'S') {
return [
[0, 6, 6],
[6, 6, 0],
[0, 0, 0],
];
} else if (type === 'T') {
return [
[0, 7, 0],
[7, 7, 7],
[0, 0, 0],
];
}
}
function draw_matrix(matrix, offset){
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0){
context.fillStyle = colors[value]
context.fillRect(x + offset.x, y + offset.y, 1, 1)
}
});
});
}
function merge(arena, player){
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0){
arena[y + player.pos.y][x + player.pos.x] = value;
}
});
});
}
function collide(arena, player){
const m = player.matrix;
const o = player.pos;
for (let y = 0; y < m.length; y++){
for (let x = 0; x < m[y].length; x++){
if (m[y][x] != 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) != 0){
return true
}
}
}
return false
}
let drop_counter = 0
let last_time = 0
let drop_interval = 400
function update(time = 0){
const delta_time = time - last_time
drop_counter += delta_time
if (drop_counter > drop_interval){
player_drop()
}
last_time = time
draw()
requestAnimationFrame(update)
}
function player_drop(){
player.pos.y++
if (collide(arena, player)){
player.pos.y--
merge(arena, player)
player_reset()
arena_sweep()
update_score()
}
drop_counter = 0
}
function player_move(dir){
player.pos.x += dir
if (collide(arena, player)){
player.pos.x -= dir
}
}
function player_rotate(dir){
const pos = player.pos.x
let offset = 1
rotate(player.matrix, dir)
while (collide(arena, player)){
player.pos.x += offset
offset = -(offset + (offset > 0 ? 1 : -1))
if (offset > player.matrix[0].length){
rotate(player.matrix, -dir)
player.pos.x = pos
return
}
}
}
function update_score(){
document.getElementById('score').innerText = player.score
}
function rotate(matrix, dir){
for (let y = 0; y < matrix.length; y++){
for (let x = 0; x < y; x++){
[
matrix[x][y],
matrix[y][x],
] = [
matrix[y][x],
matrix[x][y],
];
}
}
if (dir > 0){
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
const colors = [
null,
'green',
'blue',
'violet',
'red',
'purple',
];
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
}
const arena = create_matrix(12, 20)
document.addEventListener('keydown', event => {
if (event.keyCode === 37){
player_move(-1);
}else if (event.keyCode == 39){
player_move(1);
}else if (event.keyCode == 40){
player_drop();
}else if (event.keyCode == 81){
player_rotate(-1)
}else if (event.keyCode == 87){
player_rotate(1)
}
});
update_score()
player_reset()
update()
Never mind, I found the error. I forgot to give 7 colors to the pieces, so it was looping on colors it shouldn't have been.
When converting numbers to roman numerals, I need if the first number "2000" is larger than what is on the array provided, add the maximum value of the array until it comes to 2000.
I run into a problem after it is done putting out the numerals for 2000. It goes to the next number in the array, which is ZERO! [2000,0,80,4]
I need to skip if it is zero. I attempted a finalnum[i]!=0.
for (i = 0; i < finalnum.length; i++) {
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
}
var romanNum = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
var romanEquiv = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M'];
function convertToRoman(num) {
//convert num to separate numbers
var indices = [];
var romans = [];
//split out the number first
var nextint = num.toString().split("");
var nextarr = [];
for (var i = 0; i < nextint.length; i++) {
var firstnum = "0".repeat(nextint.length - i - 1);
nextarr.push(nextint[i] + firstnum);
var finalnum = nextarr.map(Number);
}
//find the romannumerals
for (i = 0; i < finalnum.length; i++) {
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
return romans.join('');
}
console.log(convertToRoman(2084));
Test if finalnum[i] == 0, and skip to the next number with continue;.
However, the rest of your code doesn't seem to work correctly. It still just returns "MM". There are other problems in your algorithm that
You should also use return in the function. Do the console.log() in the caller.
BTW, the reduce loop can be replaced with:
var max = Math.max.apply(Math, romanNum);
I'm not really sure why you need this variable, and you certainly don't need to calculate it each time through the loop, since the romanNum array never changes. It seems to be part of your confused algorithm. There are lots of other questions on SO (and also Code Review) about converting to Roman numerals, you can look at them.
var romanNum = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
var romanEquiv = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M'];
function convertToRoman(num) {
//convert num to separate numbers
var indices = [];
var romans = [];
//split out the number first
var nextint = num.toString().split("");
var nextarr = [];
for (var i = 0; i < nextint.length; i++) {
var firstnum = "0".repeat(nextint.length - i - 1);
nextarr.push(nextint[i] + firstnum);
var finalnum = nextarr.map(Number);
}
//find the romannumerals
for (i = 0; i < finalnum.length; i++) {
if (finalnum[i] == 0) { // skip zeroes
continue;
}
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
return romans.join('');
}
console.log(convertToRoman(2084));
function convertToRoman(num) {
var thisMap = {
1:[1],
2:[1, 1],
3:[1, 1, 1],
4:[1, 5],
5:[5],
6:[5, 1],
7:[5, 1, 1],
8:[5, 1, 1, 1],
9:[1, 10],
0:[0]
};
var numMap = {
1000:"M",
500:"D",
100:"C",
50:"L",
10:"X",
5:"V",
1:"I"
};
numArr = num.toString().split("");
var thisIndex = 1;
var tallyArr = [];
for (var i = numArr.length - 1; i >= 0; i--) {
tallyArr.unshift(thisMap[numArr[i]]);
}
thisIndex = Math.pow(10, tallyArr.length - 1);
checkArr = [];
<<<BUG HERE>>>
for (var x = 0; x < tallyArr.length; x++) {
for (var y = 0; y < tallyArr[x].length; y++) {
tallyArr[x][y] *= thisIndex;
}
thisIndex = thisIndex / 10;
}
<<</BUG HERE>>>
var finalArr = [];
for (var a = 0; a < tallyArr.length; a++) {
for (var b = 0; b < tallyArr[a].length; b++) {
finalArr.push(numMap[tallyArr[a][b]]);
}
}
finalAnswer = finalArr.join("");
return finalAnswer;
}
convertToRoman(88);
So this is my function for converting a number into a Roman Numeral in Javascript. It basically formats every number into the right format using thisMap, then uses thisIndex to multiply by either 1000, 100 or 10, and then compares to numMap to get the correct Roman Numeral.
It seems to work in most of the test cases, except with 44, 99, or 3999.
In these cases, it seems to multiply the numbers by the wrong amount, so 44 becomes XLXL, when it should be XLIV.
I think the bug is between the <<>> tags I've inserted, because that is where the numbers seem to be multiplied wrong.
However, I can't spot the problem.
Thanks.
I tried a different approach.
function convertToRoman(num) {
let arabicArray = [ 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000, 4000, 5000, 5001]
let romanArray = ['I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M', 'MV', 'V', 'limit of 5000']
let roman = ""
loop()
function loop() {
for (let i = 0; i < arabicArray.length; i++) {
if (num < arabicArray[i]) {
roman += romanArray[i - 1]
num -= arabicArray[i - 1]
while (num != 0) {loop()} break;
}
}
}
return roman
}
console.log(convertToRoman(3))
However this gives you a limit to 5000.
Try this: x loop should run through all the length of tallyArr except the last one.
function convertToRoman(num) {
// ... code ...
for (var x = 0; x < tallyArr.length - 1; x++) {
for (var y = 0; y < tallyArr[x].length; y++) {
tallyArr[x][y] *= thisIndex;
}
thisIndex = thisIndex / 10;
}
// ... more code ...
}
Your solution seems a bit over engineered and overly complicated sometimes simpler is better and what might seem like a clever answer and looking for the overly eloquent solution can trip you up.
function convertToRoman(num) {
var output = "";
var numMap = [
{ limit: 1000, value: "M" },
{ limit: 900, value: "CM" },
{ limit: 500, value: "D" },
{ limit: 400, value: "CD" },
{ limit: 100, value: "C" },
{ limit: 90, value: "XC" },
{ limit: 50, value: "L" },
{ limit: 40, value: "XL" },
{ limit: 10, value: "X" },
{ limit: 9, value: "IX" },
{ limit: 5, value: "V" },
{ limit: 4, value: "IV" },
{ limit: 1, value: "I" }
];
for(var index = 0; index < numMap.length; index++) {
var value = numMap[index].value,
limit = numMap[index].limit;
while(num >= limit) {
output += value;
num -= limit;
}
}
return output;
}
alert(convertToRoman(1));
alert(convertToRoman(4));
alert(convertToRoman(5));
alert(convertToRoman(9));
alert(convertToRoman(10));
alert(convertToRoman(88));
alert(convertToRoman(2016));
JSFiddle
var romanNumber = [
[1, 'I'], [2, 'II'], [3, 'III'],[4, 'IV'],
[5, 'V'], [6, 'VI'],[7, 'VII'], [8, 'VIII'],
[9, 'IX'],[10, 'X']
];
function convertToRoman(number) {
if (number === 0) {
return '';
}
for (var i = 0; i < romanNumber.length; i++) {
if (number === romanNumber[i][0]) {
return romanNumber[i][1];
}
}
}
console.log(convertToRoman(1));
console.log(convertToRoman(2));
console.log(convertToRoman(3));
console.log(convertToRoman(4));
console.log(convertToRoman(5));
console.log(convertToRoman(6));
console.log(convertToRoman(7));
console.log(convertToRoman(8));
console.log(convertToRoman(9));
console.log(convertToRoman(10));
i currently have something which rolls onto a number but i'd like to change it so instead of displaying the number it rolled on, it displays the name which the number is apart of. A snippet of what i have is below:
function spin(m) {
var x = m.roll;
play_sound("roll");
var order = [1, 14, 2, 13, 3, 12, 4, 0, 11, 5, 10, 6, 9, 7, 8];
var index = 0;
for (var i = 0; i < order.length; i++) {
if (x == order[i]) {
index = i;
break
}
}
var max = 32;
var min = -32;
var w = Math.floor(m.wobble * (max - min + 1) + min);
var dist = index * 70 + 36 + w;
dist += 1050 * 5;
animStart = new Date().getTime();
vi = getVi(dist);
tf = getTf(vi);
isMoving = true;
setTimeout(function() {
finishRoll(m, tf)
}, tf);
render()
}
As you can see I have 1, 14, 2, 13 etc.
I would like it so if it rolls onto 1, 2, 3, 4, 5, 6 or 7 it shows 'Group1' instead of the number it has landed on.
The same goes with 14, 13, 12, 11, 10, 9 and 8 except i'd like it to show 'Group2' instead of the number it has landed on.
Now for 0, i'd like it so it shows 'Green' instead of 0 when it lands on it.
These are the main snippets in the file.
1.
function cd(ms, cb) {
$("#counter").finish().css("width", "100%");
$("#counter").animate({
width: "0%"
}, {
"duration": ms * 1000,
"easing": "linear",
progress: function(a, p, r) {
var c = (r / 1000).toFixed(2);
$BANNER.html("Rolling in " + c + "...");
},
complete: cb
});
}
2.
function finishRoll(m, tf) {
$BANNER.html("Rolled number " + m.roll + "!");
addHist(m.roll, m.rollid);
play_sound("finish");
for (var i = 0; i < m.nets.length; i++) {
$("#panel" + m.nets[i].lower + "-" + m.nets[i].upper).find(".total").countTo(m.nets[i].swon > 0 ? m.nets[i].swon : -m.nets[i].samount, {
"color": true,
"keep": true
});
}
var cats = [
[0, 0],
[1, 7],
[8, 14]
];
for (var i = 0; i < cats.length; i++) {
var $mytotal = $("#panel" + cats[i][0] + "-" + cats[i][1]).find(".mytotal");
if (m.roll >= cats[i][0] && m.roll <= cats[i][1]) {
$mytotal.countTo(m.won, {
"color": true,
"keep": true
});
} else {
var curr = parseFloat($mytotal.html());
if ($("#settings_dongers").is(":checked")) {
curr *= 1000;
}
$mytotal.countTo(-curr, {
"color": true,
"keep": true
});
}
}
if (m.balance != null) {
$("#balance").countTo(m.balance, {
"color": true
});
checkplus(m.balance);
}
setTimeout(function() {
cd(m.count);
$(".total,.mytotal").removeClass("text-success text-danger").html(0);
$(".betlist li").remove();
snapRender();
$(".betButton").prop("disabled", false);
showbets = true;
}, m.wait * 1000 - tf);
}
3.
function snapRender(x, wobble) {
CASEW = $("#case").width();
if (isMoving) return;
else if (typeof x === 'undefined') view(snapX);
else {
var order = [1, 14, 2, 13, 3, 12, 4, 0, 11, 5, 10, 6, 9, 7, 8];
var index = 0;
for (var i = 0; i < order.length; i++) {
if (x == order[i]) {
index = i;
break
}
}
var max = 32;
var min = -32;
var w = Math.floor(wobble * (max - min + 1) + min);
var dist = index * 70 + 36 + w;
dist += 1050 * 5;
snapX = dist;
view(snapX)
}
}
How about this?
// assumes n is always a number between 0 and 14 (inclusive)
function numberToName(n) {
if (n === 0) {
return "Green";
} else if (n < 8) {
return "Group1";
} else {
return "Group2";
}
}
Just call this with the chosen number and then use the returned text however you want.
I want to modify an array of numbers and output it to a new range using d3.scale but with custom interpolation function. The interpolation function should be one of the easing functions used in transitions, e.g. easeInOutQuad:
easeInOutQuad = function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
So my input array [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] would become, more less, something like [0, 2, 5, 10, 20, 40, 70, 90, 95, 98, 100] where numbers increase slower at the beginning of the array, then gradually faster towards the middle and then again slower towards the end.
My code so far:
var inputArr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
linearArr = [],
easingArr = [],
easing = d3.interpolate, // ?
min = d3.min(inputArr),
max = d3.max(inputArr),
linearScale = d3.scale.linear()
.domain([min,max])
.range([0,100]),
easingScale = d3.scale.linear()
.domain([min,max])
.interpolate(easing) // ?
.range([0,100]);
for (var i = 0; i < inputArr.length; i++) {
linearArr[i] = linearScale(inputArr[i]);
easingArr[i] = easingScale(inputArr[i]);
}
console.log(linearArr); // 0,10,20,30,40,50,60,70,80,90,100
console.log(easingArr); // 0,10,20,30,40,50,60,70,80,90,100
Thanks for any suggestions/examples of how such an easing function could be used with d3.interpolate.
Thanks to this helpful example, it's solved now, so a linear array of numbers can be 'eased':
linear = [0,25,50,75,100] --> eased = [0,12.5,50,87.5,100]
Here's the code:
var steps = 5,
zStart = 0,
zEnd = 100,
linearArr = [],
easingArr = [],
linearScale = d3.scaleLinear()
.domain([0,1])
.range([zStart,zEnd]),
easingScale = d3.scaleLinear()
.domain([0,1])
.interpolate(easeInterpolate(d3.easeQuadInOut))
.range([zStart,zEnd]);
for (var i = 0; i < steps; i++) {
linearArr[i] = linearScale(i/(steps-1));
easingArr[i] = easingScale(i/(steps-1));
}
console.log("linear (" + linearArr.length + "): " + linearArr);
console.log("easing (" + easingArr.length + "): " + easingArr);
function easeInterpolate(ease) {
return function(a, b) {
var i = d3.interpolate(a, b);
return function(t) {
return i(ease(t));
};
};
}