NVD3.js (d3.js) Scale Break - d3.js

I want to create a chart with scale break on y axis. I don't want to use a non-linear scale but using a scale break make lower values more visible when having data anomalies.
What is the best way to implement that in nvd3 or in general in d3?

Lacking other options I created a brute force solution, manipulating the vertical <path> of the Y axis.
Using pseudo code, you use it like this:
var domainPath = yAxis
.select('path.domain');
domainPath.attr('d', breakScale(domainPath.attr('d'), 14, 6, 4, 7);
The breakScale function is not very special nor elegant, but here it goes:
function breakScale(pathString, amplitude, wavelength, periods, dist){
var parts = pathString.match(/(.*)(H-\d+)/);
var first = parts[1];
var last = parts[2];
first = first.replace(/(.*?V)(\d+)/, function(match, p1, p2) { return p1 + (p2-dist-(wavelength)*periods) });
var newPath = first;
for(var i=0; i<periods+1; i++){
if(i === 0){
newPath += 'l-' + (amplitude/2) + ',' + (wavelength/2);
}
else if(i == periods){
newPath += 'l' + (i%2?'':'-') + (amplitude/2) + ',' + (wavelength/2);
}
else {
newPath += 'l' + (i%2?'':'-') + amplitude + ',' + wavelength;
}
}
newPath += 'v' + dist + last;
return newPath;
}
dist defines the distance from the start of the axis that the wavy thing should begin. The function probably works best with even numbers for amplitude and wavelength because they are both halved for the first and last part of the wave. periodsshould be at least 3.
Given a typical domain axis path string like M-6,0H0V376H-6, it will spit back something like M-6,0H0V345l-7,3l14,6l-14,6l14,6l-7,3v7H-6 which would look like this:

Related

How to rotate my character while jumping (Gamemaker Studio 2)

I would like to make a simple animation of the character rotating itself when it jumps. I'm making an indie platformer so this should be simple to do, I think, but I'm too newbie for this.
Here's the movement code.
//------------------------- MOVEMENT INPUT
xMove = kRight - kLeft;
xSpd = xMove * mSpd;
ySpd += 0.65;
//------------------------- JUMP
onGround = place_meeting(x,y+1,oSolid);
if(onGround) airJump = 1;
if(kJump){
if(onGround or airJump > 0){
ySpd = -12;
airJump = 0;
}
}
//------------------------- FINAL MOVEMENT
if(place_meeting(x + xSpd, y, oSolid)){
while(!place_meeting(x + sign(xSpd), y, oSolid)) x += sign(xSpd);
xSpd = 0;
}
if(place_meeting(x + xSpd, y + ySpd, oSolid)){
while(!place_meeting(x + xSpd, y + sign(ySpd), oSolid)) y += sign(ySpd);
ySpd = 0;
}
x += xSpd;
y += ySpd;
if xSpd < 0 dir = -1;
if xSpd > 0 dir = 1;
The player is a simple square, so I would like to make it rotate 360 degrees while on the air.
You should be able to use image_angle for this, changing the value will change the angle of the sprite, and continiously increasing/decreasing that value will simulate a rotation.
However, keep in mind that if you rotate the sprite, the hitbox of the sprite will rotate as well. You can probably set the hitbox apart from the sprite so it won't interrupt with each other.
Example:
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Asset_Management/Sprites/Sprite_Instance_Variables/image_angle.htm
For player movement collision handling you want to avoid using image_angle variable by using your own variable for the image rotation with the draw_sprite_ext function. Also by change you end up wanting to use the image angle for anything its good to wrap it mostly later if your trying to use fov and what not.
For example
function Scr_Player_Create(){
image_offset = 0;
}
function Scr_Player_Step(){
image_offset += (keyboard_check(vk_right) - keyboard_check(vk_left)) * 10;
image_offset = wrap(image_offset, 0, 359);
}
function Scr_Player_Draw(){
draw_sprite_ext( sprite_index, image_index, x, y, image_xscale, image_yscale,
image_angle + image_offset, image_blend, image_alpha );
draw_text(10, 10, image_offset);
}
function wrap(wrap_value, wrap_minimum, wrap_maximum){
// Credit: Juju from GMLscripts forums!
var _mod = ( wrap_value - wrap_minimum ) mod ( wrap_maximum - wrap_minimum );
if ( _mod < 0 ) return _mod + wrap_maximum else return _mod + wrap_minimum;
}
Another approach you could do to avoid image_angle effecting your collision is this
var _angle = image_angle;
image_angle += image_offset;
draw_self();
image_angle = _angle;

2D Circular search pattern

I need an algorithm to give me coordinates to the nearest cells (in order of distance) to another cell in a 2D grid. Its for a search algorithm that then checks those coordinates for all sorts of things for suitability. Anyways, so far I came up with this:
function testy(cx, cy, idx) {
var radius = Math.floor(Math.sqrt(idx / Math.PI));
var segment = Math.round(idx - (radius * Math.PI));
var angle = segment / radius;
var x = Math.round(cx + radius * Math.cos(angle));
var y = Math.round(cy + radius * Math.sin(angle));
return [x, y];
}
addEventListener("load", function() {
var canv = document.createElement("canvas");
document.body.appendChild(canv);
canv.width = 800;
canv.height = 600;
var ctx = canv.getContext("2d");
var scale = 5;
var idx = 0;
var idx_end = 10000;
var func = function() {
var xy = testy(0,0,idx++);
var x = xy[0] * scale + canv.width / 2;
var y = xy[1] * scale + canv.height / 2;
ctx.rect(x, y, scale, scale);
ctx.fill();
if (idx < idx_end) setTimeout(func, 0);
}
func();
});
but as you can tell, its kinda crap because it skips some cells. There's a few assumptions I'm making there:
That the circumference of a circle of a certain radius corresponds to the number of cells on the path of that circle. I didn't think that would be too great of a problem though since the actual number of cells in a radius should be lower than the circumference leading to duplication(which in small amounts is ok) but not exclusion(not ok).
That the radius of a circle by the n-th index specified would be slightly more than Math.floor(Math.sqrt(idx / Math.PI)) because each increase of 1 to the radius corresponds to 2 * Math.PI being added to the circumference of the circle. Again, should lead to slight duplication but no exclusion.
Other than that I have no idea what could be wrong with it, I fail at math any more complex than this so probably something to do with that.
Perhaps there is another algorithm like this already out there though? One that doesn't skip cells? Language doesn't really matter, I'm using js to prototype it but it can be whatever.
Instead of thinking about the full circle, think about a quadrant. Adapting that to the full circle later should be fairly easy. Use (0,0) as the center of the circle for convenience. So you want to list grid cells with x,y ≥ 0 in order of non-decreasing x² + y².
One useful data structure is a priority queue. It can be used to keep track of the next y value for every x value, and you can extract the one with minimal x² + y² easily.
q = empty priority queue, for easy access to element with minimal x²+y²
Insert (0,0) into queue
while queue is not empty:
remove minimal element from queue and call it (x,y)
insert (x,y+1) into queue unless y+1 is off canvas
if y = 0:
insert (x+1,0) into queue unless x+1 is off canvas
do whatever you want to do with (x,y)
So for a canvas of size n this will enumerate all the n² points, but the priority queue will only contain n elements at most. The whole loop runs in O(n² log(n)). And if you abort the loop eraly because you found what you were looking for, it gets cheaper still, in contrast to simply sorting all the points. Another benefit is that you can use integer arithmetic exclusively, so numeric errors won't be an issue. One drawback is that JavaScript does not come with a priority queue out of the box, but I'm sure you can find an implementation you can reuse, e.g. tiniqueue.
When doing full circle, you'd generate (−x,y) unless x=0, and likewise for (x,−y) and (−x,−y). You could exploit symmetry a bit more by only having the loop over ⅛ of the circle, i.e. not inserting (x,y+1) if x=y, and then also generating (y,x) as a separate point unless x=y. Difference in performance should be marginal for many use cases.
"use strict";
function distCompare(a, b) {
const a2 = a.x*a.x + a.y*a.y;
const b2 = b.x*b.x + b.y*b.y;
return a2 < b2 ? -1 : a2 > b2 ? 1 : 0;
}
// Yields points in the range -w <= x <= w and -h <= y <= h
function* aroundOrigin(w,h) {
const q = TinyQueue([{x:0, y:0}], distCompare);
while (q.length) {
const p = q.pop();
yield p;
if (p.x) yield {x:-p.x, y:p.y};
if (p.y) yield {x:p.x, y:-p.y};
if (p.x && p.y) yield {x:-p.x, y:-p.y};
if (p.y < h) q.push({x:p.x, y:p.y+1});
if (p.y == 0 && p.x < w) q.push({x:p.x + 1, y:0});
}
}
// Yields points around (cx,cy) in range 0 <= x < w and 0 <= y < h
function* withOffset(cx, cy, w, h) {
const delegate = aroundOrigin(
Math.max(cx, w - cx - 1), Math.max(cy, h - cy - 1));
for(let p of delegate) {
p = {x: p.x + cx, y: p.y + cy};
if (p.x >= 0 && p.x < w && p.y >= 0 && p.y < h) yield p;
}
}
addEventListener("load", function() {
const canv = document.createElement("canvas");
document.body.appendChild(canv);
const cw = 800, ch = 600;
canv.width = cw;
canv.height = ch;
const ctx = canv.getContext("2d");
const scale = 5;
const w = Math.ceil(cw / scale);
const h = Math.ceil(ch / scale);
const cx = w >> 1, cy = h >> 1;
const pointgen = withOffset(cx, cy, w, h);
let cntr = 0;
var func = function() {
const {value, done} = pointgen.next();
if (done) return;
if (cntr++ % 16 === 0) {
// lighten older parts so that recent activity is more visible
ctx.fillStyle = "rgba(255,255,255,0.01)";
ctx.fillRect(0, 0, cw, ch);
ctx.fillStyle = "rgb(0,0,0)";
}
ctx.fillRect(value.x * scale, value.y*scale, scale, scale);
setTimeout(func, 0);
}
func();
});
<script type="text/javascript">module={};</script>
<script src="https://cdn.rawgit.com/mourner/tinyqueue/54dc3eb1/index.js"></script>

Rotate every arc of pie chart 180 (like sun) with D3 JS. How to calculate translate parameters

I am working on pie chart with d3 js. I want to rotate every arc of my pie chart 180. I know that I am unable to explain completely show here is my fiddle link.
[fiddle]: https://jsfiddle.net/dsLonquL/
How can i get dynamic parameters for translate() function.
Basically you need to work out the centre point of the edge of each arc. I used this example for help : How to get coordinates of slices along the edge of a pie chart?
This works okay, but I needed to rotate the points to get them in the correct positions. As it is in radians the rotation is the following :
var rotationInRadians = 1.5708 * 1.5;
Now using the example before I used the data for the paths, so the start and end angle and got the center points like so :
var thisAngle = (d.startAngle + rotationInRadians + (d.endAngle + rotationInRadians - d.startAngle + rotationInRadians) / 2);
var x = centreOfPie[0] + radius * 2 * Math.cos(thisAngle)
var y = centreOfPie[1] + radius * 2 * Math.sin(thisAngle)
I created a function to show circles at these points to clarify :
function drawCircle(points, colour) {
svg.append('circle')
.attr('cx', points[0])
.attr('cy', points[1])
.attr('r', 5)
.attr('fill', colour);
}
Called it inside the current function like so :
drawCircle([x, y], color(d.data.label))
And then translated and rotated accordingly :
return 'translate(' + (x) + ',' + y + ') rotate(180)';
I added a transition so you can see it working. Here is the final fiddle :
https://jsfiddle.net/thatOneGuy/dsLonquL/7/
EDIT
In your comments you say you want the biggest segment to be kept in the middle. So we need to run through the segments and get the biggest. I have also taken care of duplicates, i.e if two or more segments are the same size.
Here is the added code :
var biggestSegment = {
angle: 0,
index: []
};
path.each(function(d, i) {
var thisAngle = (d.endAngle - d.startAngle).toFixed(6);//i had to round them as the numbers after around the 7th or 8th decimal point tend to differ tet theyre suppose to be the same value
if (i == 0) {
biggestSegment.angle = thisAngle
} else {
if (biggestSegment.angle < thisAngle) {
biggestSegment.angle = thisAngle;
biggestSegment.index = [i];
} else if (biggestSegment.angle == thisAngle) {
console.log('push')
biggestSegment.index.push(i);
}
}
})
Now this goes through each path checks if its bigger than the current value, if it is overwrite the biggest value and make note of the index. If its the same, add index to index array.
Now when translating the paths, you need to check the current index against the index array above to see if it needs rotating. Like so :
if (biggestSegment.index.indexOf(i) > -1) {
return 'translate(' + (centreOfPie[0]) + ',' + (centreOfPie[1]) + ')' // rotate(180)';
} else {
return 'translate(' + (x) + ',' + y + ') rotate(180)';
}
Updated fiddle : https://jsfiddle.net/thatOneGuy/dsLonquL/8/
I have editted 3 values to be different to the rest. Go ahead and change these, see what you think :)
This is a pure middle school geometry job.
CASE 1: The vertex of each sector rotation is on the outer line of the circle
fiddle
// ... previous code there
.attr('fill', function(d, i) {
return color(d.data.label);
})
.attr("transform", function(d, i) {
var a = (d.endAngle + d.startAngle) / 2, // angle of vertex
dx = 2 * radius * Math.sin(a), // shift/translate is two times of the vertex coordinate
dy = - 2 * radius * Math.cos(a); // the same
return ("translate(" + dx + " " + dy + ") rotate(180)"); // output
});
CASE 2: The vertex on the center of the chord
fiddle
// ... previous code there
.attr('fill', function(d, i) {
return color(d.data.label);
})
.attr("transform", function(d, i) {
var dx = radius * (Math.sin(d.endAngle) + Math.sin(d.startAngle)), // shift/translation as coordinate of vertex
dy = - radius * (Math.cos(d.endAngle) + Math.cos(d.startAngle)); // the same for Y
return ("translate(" + dx + " " + dy + ") rotate(180)"); // output
});

How can I draw an autoscaling D3.js graph that plots a mathematical function?

I have a working jsfiddle that I made using JSXGraph, a graphing toolkit for mathematical functions. I'd like to port it to D3.js for personal edification, but I'm having a hard time getting started.
The jsfiddle graphs the value of -ke(-x/T) + k, where x is an independent variable and the values of k and t come from sliders.
board.create('functiongraph',
[
// y = -k * e(-x/t) + k
function(x) { return -k.Value()*Math.exp(-x/t.Value()) + k.Value(); },
0
]
);
The three things I'm most stumped on:
Actually drawing the graph and its axes - it's not clear to me which of the many parts of the D3 API I should be using, or what level of abstraction I should be operating at.
Re-rendering the graph when a slider is changed, and making the graph aware of the value of the sliders.
Zooming out the graph so that the asymptote defined by y = k is always visible and not within the top 15% of the graph. I do this now with:
function getAestheticBoundingBox() {
var kMag = k.Value();
var tMag = t.Value();
var safeMinimum = 10;
var limit = Math.max(safeMinimum, 1.15 * Math.max(k.Value(), t.Value()));
return [0, Math.ceil(limit), Math.ceil(limit), 0];
}
What's the right way for me to tackle this problem?
I threw this example together really quick, so don't ding me on the code quality. But it should give you a good starting point for how you'd do something like this in d3. I implemented everything in straight d3, even the sliders.
As #LarKotthoff says, the key is that you have to loop your function and build your data:
// define your function
var func = function(x) {
return -sliders.k() * Math.exp(-x / sliders.t()) + sliders.k();
},
// your step for looping function
step = 0.01;
drawPlot();
function drawPlot() {
// avoid first callback before both sliders are created
if (!sliders.k ||
!sliders.t) return;
// set your limits
var kMag = sliders.k();
var tMag = sliders.t();
var safeMinimum = 10;
var limit = Math.max(safeMinimum, 1.15 * Math.max(kMag, tMag));
// generate your data
var data = [];
for (var i = 0; i < limit; i += step) {
data.push({
x: i,
y: func(i)
})
}
// set our axis limits
y.domain(
[0, Math.ceil(limit)]
);
x.domain(
[0, Math.ceil(limit)]
);
// redraw axis
svg.selectAll("g.y.axis").call(yAxis);
svg.selectAll("g.x.axis").call(xAxis);
// redraw line
svg.select('.myLine')
.attr('d', lineFunc(data))
}

Midpoint Displacement 2D algorithm producing unusual patterns

I'm having difficulties with the Midpoint Displacement Algorithm using Haxe. I am implementing this by following the steps found here.
First, create an array that represents a blank map. You begin by giving the four corners a random value.
In this square, create the middle point by averaging the four corners and adding a small 'error', or random value. Then create the midpoints of the 4 sides by averaging the two corners each is between. After these steps, you are left with 4 squares. Repeat the steps:
Create the middle point by averaging the four corners and adding a small 'error'.
Create the midpoint of each side by averaging the two corners each point is between.
Each iteration, make the range of the RNG smaller. That way the original few points can have pretty large variation, but the later points only get tiny adjustments. This ensures the right amount of detail in an image.
Here is the function I've written to perform these steps and then normalize the values:
public static function generateFloatMatrix(Columns:Int, Rows:Int, RangeModifier:Float = 0.65):Array<Array<Float>>
{
//Blank 2D Array
var matrix:Array<Array<Float>> = InitFloatMatrix(Columns, Rows);
var range:Float = 1;
//Set Values for all four corners
matrix[0][0] = Math.random() * range;
matrix[Rows-1][0] = Math.random() * range;
matrix[0][Columns-1] = Math.random() * range;
matrix[Rows - 1][Columns - 1] = Math.random() * range;
//Calculates the amount of segments in base 2
var length = Math.sqrt((Columns * Columns) + (Rows * Rows));
var power:Int = Std.int(Math.pow(2, Math.ceil(Math.log(length) / Math.log(2))));
//Stores largest calculated value for normalization
var max:Float = 0;
var width:Int = Std.int(Columns);
var height:Int = Std.int(Rows);
var i:Int = 1;
while (i < power)
{
//Segment Size
width = Std.int(Columns / i);
height = Std.int(Rows / i);
for (y in 0...i)
{
for (x in 0...i)
{
//Top Left Coordinates per segment
var left = width * x;
var top = height * y;
//Find Midpoint
var xMid = Math.ceil(left + (width / 2));
var yMid = Math.ceil(top + (height / 2));
//Make sure right and bottom do not go out of bounds
var right:Int = (left + width < Columns ? left + width : Columns - 1);
var bottom:Int = (top + height < Rows ? top + height : Rows - 1);
//Sets midpoint value to average of all four corners.
matrix[yMid][xMid] =
(matrix[top][left] +
matrix[bottom][left] +
matrix[bottom][right] +
matrix[top][right]) / 4;
//trace ("Top: " + top + " - Left: " + left + " - Bottom: " + bottom + " - Right: " + right);
//Adds random value to midpoint
matrix[yMid][xMid] += Math.random() * range;
//Set side values to average of adjacent corners
matrix[top][xMid] = (matrix[top][left] + matrix[top][right]) / 2;
matrix[bottom][xMid] = (matrix[bottom][left] + matrix[bottom][right]) / 2;
matrix[yMid][left] = (matrix[top][left] + matrix[bottom][left]) / 2;
matrix[yMid][right] = (matrix[top][right] + matrix[bottom][right]) / 2;
max = Math.max(matrix[top][left], max);
}
}
//Reduces range
range *= RangeModifier;
i *= 2;
}
//Normalizes all values in matrix
for (y in 0...Rows)
{
for (x in 0...Columns)
{
matrix[y][x] /= max;
}
}
return matrix;
}
These are the images it is producing if I use each value to render each pixel to the specified coordinate. All the pixels that are rendered white have the value 0, black is value 1.
Your problem is that you don't necessarily hit the already populated pixels with your calculations if your map dimensions are not a power of two. For example if your map is 30 units wide, your grid width is 15 in the first pass and 7 in the second pass, where it bases its calculations on the yet untouched unit 14.
A solution is to do all calculations with floating-point arithmetic until you determine the unit indices, which must of course be integer:
while (i < power)
{
var width:Float = Columns / i; // Floating-point division
var height:Float = Rows / i;
for (y in 0...i)
{
for (x in 0...i)
{
var left:Int = Math.floor(width * x);
var top:Int = Math.floor(height * y);
var xMid:Int = Math.floor(width * (x + 0.5));
var yMid:Int = Math.floor(height * (y + 0.5));
var right:Int = Math.floor(width * (x +1));
var bottom:Int = Math.floor(height * (y + 1));
//Make sure right and bottom do not go out of bounds
if (right > Columns - 1) right = Columns - 1;
if (bottom > Rows - 1) bottom = Rows - 1;
// Do offset and interpolation stuff
}
}
}
This should give you a random map, graph-paper effect and all.
(Caveat: I'm not familiar with Haxe, but have tested this in Javascript, which doesn't have an integer type. I've used Math-floor throughout, where you'll want to do it the Haxe way.)
Finally, it looks to me that you do too many passes. I'd base the power on the maximum of the two dimensions instead of the diagonal. You can also skip the last step where wthe width is near one.

Resources