Background
I have a primefaces line chart (date on x, integer >= 0 on y) extended with jqplot options:
function extender() {
this.cfg.axes = {
xaxis : {
renderer : $.jqplot.DateAxisRenderer,
rendererOptions : {
tickRenderer:$.jqplot.CanvasAxisTickRenderer
},
tickOptions : {
fontSize:'10pt',
fontFamily:'Tahoma',
angle:-40,
formatString:'%b-%y'
},
tickInterval:'2592000000'
},
yaxis : {
min: 0,
rendererOptions : {
tickRenderer:$.jqplot.CanvasAxisTickRenderer,
},
tickOptions: {
fontSize:'10pt',
fontFamily:'Tahoma',
angle:0,
formatString: '%d'
}
},
};
this.cfg.axes.xaxis.ticks = this.cfg.categories;
}
I'm using the jqplot extender to have custom date interval on the x-axis and this is working fine:
Problem
When I use the option min: 0 in the y-axis the formatting of numbers goes really funky, especially when there are small values:
Note that the minY attribute in primefaces doesn't work (probably because the extender overwrites it)
To fix that, I use formatString: %d. It works but it creates problem with the number of ticks:
As you see on the screenshot, there are several times the line for the value 1.
Question
How can make sure I don't get several times the same value on the y-axis?
I can't really have a static number of ticks because when the data grows large (let's say around 100), I do want several values on the y-axis (e.g 20, 40, etc...)
I managed to solve my issue using ideas from Mehasse's post.
Defining the max value like suggested by Mehasse didn't remove the unwanted tick lines but helped me to find the answer.
By default, primefaces/jqplot wants to have 4 y-axis tick lines. Thus, if the max value is below 4, there will be duplication in the y-axis label when they are rounded up (formatString: '%d').
What I basically want, is the tick interval to be either Max(y) \ 4 when Max(y) > 4, or 1 otherwise:
function actionPlanExtender() {
var series_max =maxSeries(this.cfg.data);
var numberOfTicks =4;
var tickInterval = Math.max(1, Math.ceil(series_max/numberOfTicks));
this.cfg.axes = {
xaxis : {
renderer : $.jqplot.DateAxisRenderer,
rendererOptions : {
tickRenderer:$.jqplot.CanvasAxisTickRenderer
},
tickOptions : {
fontSize:'10pt',
fontFamily:'Tahoma',
angle:-40,
formatString:'%b-%y'
},
tickInterval:'2592000000'
},
yaxis : {
min: 0,
rendererOptions : {
tickRenderer:$.jqplot.CanvasAxisTickRenderer,
},
tickOptions: {
fontSize:'10pt',
fontFamily:'Tahoma',
angle:0,
formatString: '%d',
},
tickInterval: tickInterval
},
};
this.cfg.axes.xaxis.ticks = this.cfg.categories;
}
To compute the y-max value, I'm getting the plot value using this.cfg.data which is of the form [series_1,..., series_n] with series_i = [[x_1, y_1],..., [x_m, y_m]]
The maxSeries function looks like:
function maxSeries(datas) {
var maxY = null;
var dataLength = datas.length;
for ( var dataIdx = 0; dataIdx < dataLength; dataIdx++) {
var data = datas[dataIdx];
var l = data.length;
for ( var pointIdx = 0; pointIdx < l; pointIdx++) {
var point = data[pointIdx];
var y = point[1];
if (maxY == null || maxY < y) {
maxY = y;
}
}
}
return maxY;
}
Note that in my case I know my case I don't have value below 0. This code should be updated if this is not the case.
I was surprised when I first started using JQPlot that it doesn't pick reasonable axis labels out of the box. It's actually easy to fix.
I'm going to assume that you want your y axis to start at zero. If that's not true, you'll need to modify this code a bit.
// This is YOUR data set
var series = [882, 38, 66, 522, 123, 400, 777];
// Instead of letting JQPlot pick the scale on the y-axis, let's figure out our own.
var series_max = Math.max.apply(null, series);
var digits = max.toString().length;
var scale = Math.pow(10, max_digits - 1);
var max = (Math.ceil(series_max / scale)) * scale;
$.jqplot(
'foo',
[series],
{
axes: {
yaxis: {
min: 0,
max: max
}
}
// Put your other config stuff here
}
)
The basic idea here is that we want to round up to some nice, round value near the maximum value in our series. In particular, we round up to the nearest number that has zeroes in all digits except the left-most. So the max 882 will result in a y-axis max of 1000.
Related
I'm trying to add enemies to my platformer game using the matter physics engine however using the this.matter.world.on collisionactive function only checks collision between the floor and enemy after the player jumps once. I'm currently using labels to check for collision. I have tried adding extra conditions but have only been able to allow the player to infinitely jump. i.e- it is checking the labels of what is colliding.
Collision checking code:
this.matter.world.on("collisionactive", (e,o1,o2) => {
if(o1.label == 'floor' && o2.label == 'player')
{
this.touchingGround = true;
console.log('touching')
}
});
Enemy creation function:
Right now the enemies are cubes that are created at the cursor when the player presses f
function createEnemy(scene,x,y)
{
enemy = scene.matter.add.image(x,y,'enemy').setScale(1.5)
enemy.body.label = 'enemy'
}
I don't completely understand the problem, but if you have an issue with checking/finding all current collisions, you can use the pairs property of the event-object-argument (link to the documentation), which is passed to the physics-callback function. This property should contain, the other collisions.
"...The event.pairs array may contain more colliding bodies. ..."
Here a short demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 11 * 16,
height: 6 * 16,
zoom: 2,
pixelArt: true,
scene: {
preload: preload,
create: create
},
physics: {
default: 'matter',
matter: {
gravity: {
y:.3
},
debug:true
}
},
banner: false
};
function preload (){
this.load.image('mario-tiles', 'https://labs.phaser.io/assets/tilemaps/tiles/super-mario.png');
}
function create (){
var level = [
'0'.repeat(11).split(''),
'0'.repeat(11).split(''),
'0'.repeat(11).split(''),
'0'.repeat(11).split(''),
'0'.repeat(11).split(''),
'0'.repeat(11).split('').map( _ => 14),
];
var map = this.make.tilemap({ data: level, tileWidth: 16, tileHeight: 16 });
var tiles = map.addTilesetImage('mario-tiles');
var layer = map.createLayer(0, tiles, 0, 0);
this.add.text(4, 4, 'click to jump', {color:'#9EE6FF', fontSize:10})
.setOrigin(0);
var info = this.add.text(4, 14, 'waiting ...', {color:'#ffffff', fontSize:10})
.setOrigin(0);
let enemy1 = this.add.rectangle(60.25, 5, 8, 8, 0xfff000)
.setOrigin(0.5);
let player = this.add.rectangle(60, 20, 8, 8, 0xffffff);
layer.setCollision([14]);
this.matter.add.gameObject(enemy1);
this.matter.add.gameObject(player);
this.matter.world.convertTilemapLayer(layer, {label:'floor'});
player.body.label = 'player';
enemy1.body.label = 'enemy';
this.input.on('pointerup', _=> player.setVelocityY(-3))
player.setVelocityY(-1)
this.matter.world.on("collisionactive", (e, o1, o2) => {
let text = 'p: ';
if( e.pairs.some (pair => pair.bodyA.label == 'player' && pair.bodyB.label =='floor' )) {
text += 'touch floor ';
}
if( e.pairs.some (pair => pair.bodyA.label == 'enemy' && pair.bodyB.label =='player' )) {
text += 'touch enemy ';
}
if( e.pairs.some (pair => pair.bodyA.label == 'enemy' && pair.bodyB.label =='floor' )) {
text += '\ne: touch floor';
}
info.setText(text);
});
}
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
EDIT: Here is a code pen.
There are two main goals:
Set different colors for slices based on the specific conditions.
Make the colors be gradient, based on the amount.
More info:
1. Set different colors for slices based on the specific conditions.
EDIT: As shown in the code pen, I managed to find a solution for this, but I don't know how good it is.
I want to set different colors for slices based on specific conditions, i.e. specific 'type' & 'ordering'.
For example:
if (ordering < 9999) => green
if (ordering >= 9999 && type === 'can-be-sold') => orange
if (type !== 'can-be-sold') => red
2) Make the colors be gradient, based on the amount.
Example:
There are 10 items that are green, each with a different amount. The slices with the biggest amount should have the color in its darker shade, whereas the slices with the smallest amount should have the color in its lightest shade.
I get the data through ajax:
$.ajax({
'url': '{$dataStockUrl}',
}).done(function(data) {
chart.data = data;
});
The data I get from $dataStockUrl is in the format of:
[{
"shop": "Lorem",
"type": "can-be-sold",
"amount": "23",
"ordering":"0"
},
{
"shop": "Ipsum",
"type": "can-not-be-sold",
"amount": "1",
"ordering":"9999"
},
....etc....
]
You should use an adapter for that:
pieSeries.slices.template.adapter.add('fill', (value, target, key) => {
if (!target.dataItem || !target.dataItem.dataContext) {
return value;
}
if (target.dataItem.dataContext.ordering < 9999) {
return am4core.color('rgba(121, 153, 0, 1)');
}
if (target.dataItem.dataContext.ordering >= 9999 && target.dataItem.dataContext.type === 'can-be-sold') {
return am4core.color('rgba(255, 165, 0, 1)');
}
if (target.dataItem.dataContext.type !== 'can-be-sold') {
return am4core.color('rgba(255, 0, 0, 1)');
}
return value;
});
I forked your code pen to show the complete example.
Here you can read more about colors and gradients in amcharts4. You can use .lighten() or .brighten() on the color, based on the amount value:
pieSeries.slices.template.adapter.add('fill', (value, target, key) => {
if (!target.dataItem || !target.dataItem.dataContext) {
return value;
}
let color;
if (target.dataItem.dataContext.ordering < 9999) {
color = am4core.color('rgba(121, 153, 0, 1)');
}
if (target.dataItem.dataContext.ordering >= 9999 && target.dataItem.dataContext.type === 'can-be-sold') {
color = am4core.color('rgba(255, 165, 0, 1)');
}
if (target.dataItem.dataContext.type !== 'can-be-sold') {
color = am4core.color('rgba(255, 0, 0, 1)');
}
if (!color) {
return value;
}
if (minAmount !== undefined && maxAmount !== undefined) {
const percent = target.dataItem.dataContext.amount / (maxAmount - minAmount);
color = color.brighten(percent - 0.5);
}
return color;
});
Here is another code pen for the final result.
The result looks like this:
I'm trying to load multiple sliders using noUISlider and it's not working. When using the first slider, the values changes for the second and third. How do I go about refactoring the code for one slider so I can easily add additional sliders with different options?
Here's a fiddle and below is my code.
// noUISlider
var actSlider = document.getElementById('act-slider');
noUiSlider.create(actSlider, {
start: [0, 50],
connect: false,
step: 1,
range: {
'min': 0,
'max': 50
},
format: {
to: function (value) {
return value + '';
},
from: function (value) {
return value.replace('', '');
}
}
});
// Connect bar
var connectBar = document.createElement('div'),
connectBase = actSlider.getElementsByClassName('noUi-base')[0],
connectHandles = actSlider.getElementsByClassName('noUi-origin');
// Give the bar a class for styling and add it to the slider.
connectBar.className += 'connect';
connectBase.appendChild(connectBar);
actSlider.noUiSlider.on('update', function (values, handle) {
// Pick left for the first handle, right for the second.
var side = handle ? 'right' : 'left',
// Get the handle position and trim the '%' sign.
offset = (connectHandles[handle].style.left).slice(0, -1);
// Right offset is 100% - left offset
if (handle === 1) {
offset = 100 - offset;
}
connectBar.style[side] = offset + '%';
});
// Value tooltips
var tipHandles = actSlider.getElementsByClassName('noUi-handle'),
tooltips = [];
// Add divs to the slider handles.
for (var i = 0; i < tipHandles.length; i++) {
tooltips[i] = document.createElement('div');
tooltips[i].className += 'value-tooltip';
tipHandles[i].appendChild(tooltips[i]);
}
// When the slider changes, write the value to the tooltips.
actSlider.noUiSlider.on('update', function (values, handle) {
tooltips[handle].innerHTML = values[handle];
});
You can wrap your slider creation in a function, and call it for all elements where you want a slider created.
noUiSlider also supports tooltips (from version 8.1.0) and connect options, so you don't have to implement those yourself if you don't want to make very specific changes.
As for different options for every slider, there are several ways to do this. I've added an example using data attributes for the step option.
The following code initializes a slider on all elements with a .slider class.
function data ( element, key ) {
return element.getAttribute('data-' + key);
}
function createSlider ( slider ) {
noUiSlider.create(slider, {
start: [0, 50],
connect: false,
step: Number(data(slider, 'step')) || 1,
range: {
'min': 0,
'max': 50
},
tooltips: true,
connect: true,
format: {
to: function (value) {
return value + '';
},
from: function (value) {
return value.replace('', '');
}
}
});
}
Array.prototype.forEach.call(document.querySelectorAll('.slider'), createSlider);
Here's a jsFiddle demonstrating this code.
var targetscript : Diamond;
var red : Color;
var orange : Color;
function Start () {
gameObject.camera.backgroundColor = red;
}
function Update () {
if (targetscript.score > 4) {
gameObject.camera.backgroundColor = Color.Lerp(red, orange, Time.time);
}
}
So right now, if the score is larger than 4 then it would change the camera background color to orange with lerp. But its too quick. I read on the internet that Time.time is 1 second. What is the alternative way but for 2 or 3 seconds? I tried this : http://answers.unity3d.com/questions/328891/controlling-duration-of-colorlerp-in-seconds.html I tried the code for the voted answer but it didn't work. It still lerps through quickly. Does anyone have any ideas? Thanks
Time.time gives you the time in seconds since the start of the game. What you want is Time.deltaTime which is the time difference between each frames.
var targetscript : Diamond;
var red : Color;
var orange : Color;
var t : float = 0;
var duration : float = 3.0; //however long you want it to be
function Start () {
gameObject.camera.backgroundColor = red;
}
function Update () {
if (targetscript.score > 4) {
gameObject.camera.backgroundColor = Color.Lerp(red, orange, t);
t += Time.deltaTime/duration;
}
}
Color.Lerp(Color a, Color b, float t), so:
-> t = 0, the Color result is a
-> t = 0.5, the Color result is half a and half b
-> t = 1.0, the Color result is b
Based on that definition, when Time.time return value >= 1.0 the Lerp will be completed. That's why your code finish in 1 seconds.
so, you can change the update function like this:
var duration : float = 3.0; //however long you want it to be
function Update ()
{
if (targetscript.score > 4)
{
gameObject.camera.backgroundColor = Color.Lerp(red, orange, Time.time / duration);
}
}
This will make the Lerp completes exactly as the duration. because Time.time / duration >= 1 when Time.time >= duration.
My chart shows up well but the two lines are of the same color. How do I specify different colors for the two lines? Here is my code (fragment) so far:
config.pointIndex = null;
config.areaPoints = new Array();
config.areaPoints[0] = pointsopens;
config.areaPoints[1] = pointsclicks;
var plotLinesopen = createPlotlines(pointsopens);
var plotLinesclick = createPlotlines(pointsclicks);
var options = {
chart : { renderTo : 'areaChart' },
colors: [
'#4572A7',
'#AA4643'
],
xAxis: {
plotLines1: plotLinesopen,
plotLines2: plotLinesclick
},
series : [ data.pointsopens, data.pointsclicks ]
};
if (length > 100) {
options.plotOptions = {
area : {
lineWidth: 1,
marker : { radius : 1 }
}
};
}
options = jQuery.extend(true, {}, areaChartDefault, options);
charts.area = new Highcharts.Chart(options);
Thank you.
PS, my code is now:
config.pointIndex = null;
config.areaPoints = new Array();
config.areaPoints[0] = pointsopens;
config.areaPoints[1] = pointsclicks;
var plotLinesopen = createPlotlines(pointsopens, '#AAAAAA');
var plotLinesclick = createPlotlines(pointsclicks, '#DDDDDD');
var options = {
chart : { renderTo : 'areaChart' },
xAxis: {
plotLines: [plotLinesopen, plotLinesclick]
},
series : [ data.pointsopens, data.pointsclicks ]
};
if (length > 100) {
options.plotOptions = {
area : {
lineWidth: 1,
marker : { radius : 1 }
}
};
}
options = jQuery.extend(true, {}, areaChartDefault, options);
charts.area = new Highcharts.Chart(options);
but it still gives me two dark blue plotlines. The createPlotlines function looks like so:
function createPlotlines(points, colour) {
// Create plotlines from point data
colour = typeof colour !== 'undefined' ? colour : '#CCCCCC';
alert ('colour=='+colour);
var plotLines = [];
var middleYval = 0;
for (var i in points) {
middleYval = Math.max(middleYval, points[i].y);
if (points[i].l) { // l property is true if label should be on for this point
plotLines.push({
color: colour,
id: 'plotline'+i,
value: points[i].x,
width: 1,
});
}
}
return plotLines;
}
Do you mean different colors for the xAxis and yAxis? I only see that this would make one of each. You can definitely set the colors of the axis lines independently.
See this: example, ref
EDIT:
For plotLines you can use this:
demo
If you take a look the reference you'll see that there's no attr named colors.
Other problem is that there's no attr plotLines1 and plotLines2 as you can see here
Solution: If you want to change a plot line color your have to pass your plotlines thrue an array, like the following and set theire color:
var options = {
chart : { renderTo : 'areaChart' },
xAxis: {
plotLines: [{
color: '#4572A7', //
width: 2,
value: 5.5,
id: 'plotline-1'
}, {
color: '#AA4643',
width: 2,
value: 8.5,
id: 'plotline-2'
}]
},
series : [ data.pointsopens, data.pointsclicks ]
};
Live example
Update1:
You're returning an array of objects in this function createPlotlines and then you put this array inside other array. That's the problem.
Replace your function to the following:
function createPlotlines(points, colour) {
// Create plotlines from point data
colour = colour || '#CCCCCC';
var middleYval = 0;
for (var i in points) {
middleYval = Math.max(middleYval, points[i].y);
if (points[i].l) { // l property is true if label should be on for this point
return {
color: colour,
id: 'plotline'+i,
value: points[i].x,
width: 1,
};
}
}
}
ok, here is what I needed, lineColor:
$data = array(
"name" => $name,
"color" => '#00FFFF',
"lineColor"=> '#00FFFF',
"data" => $rows
);
that code goes in a local function called compileChartData in my php.
Thanks for the effort and sorry for the mixup.
you can change plotLines to target single line.
Or else you can change the whole Yaxis or Xaxis
xAxis: {
type: 'datetime',
gridLineColor: '#EEEEEE'
},
yAxis: {
title: {
text: 'Calls'
},
gridLineColor: '#EEEEEE'
}