Is there any way to define the {z} variable in the vector tiles portion of the script on the fly based on zoom? I want to define different numbers for each zoom than the default mapbox {z}:
map.addSource('PWS', {
type: 'vector',
tiles: ['//features.mvt?x={x}&y={y}&z={z}&apiKey={akey}&tile-size=512&time='+time[0]+'&time='+time[1]+'&time='+time[2]+'&time='+time[3]+'&stepped=true'],
tileSize: 512,
});
I have tried using this method, and it works OK, but it has a hard time with cached tiles...
map.on('zoomend', function() {
zm = map.getZoom();
if (zm > 0 && zm < 2){
zoomM = 4;
}
else if (zm >= 2 && zm < 3){
zoomM = 4;
}
else if (zm >= 3 && zm < 4){
zoomM = 4;
}
else if (zm >= 4 && zm < 5){
zoomM = 4;
}
else if (zm >= 5 && zm < 6){
zoomM = 5;
}
else if (zm >= 6 && zm < 7){
zoomM = 7;
}
else if (zm >= 7 && zm < 8){
zoomM = 8;
}
else if (zm >= 8 && zm < 9){
zoomM = 9;
}
else if (zm >= 9 && zm < 10){
zoomM = 10;
}
else if (zm >= 10 && zm < 11){
zoomM = 11;
}
else {
zoomM = 12;
}
console.log(zoomM,zm);
const source = map.getSource('PWS');
source.tiles = ['//features.mvt?x={x}&y={y}&z='+zoomM+'&apiKey={akey}&tile-size=512&time='+pwstime[0]+'&time='+pwstime[1]+'&time='+pwstime[2]+'&time='+pwstime[3]+'&stepped=true'];
map.style.sources['PWS'].reload();
});
There has got to be a better way to do this...
I have also tried this: Recommended way to switch tile urls in mapbox-gl-js
And seen this: https://github.com/mapbox/mapbox-gl-js/issues/2941
Is there a reason why you can't use transformRequest to change the z on the fly as the tile requests are made?
https://www.mapbox.com/mapbox-gl-js/api/
Related
I'm a iOS game developer and I saw an interesting physics & draw game "Sugar, Sugar" recently. In the game, there are lots of pixel particles (thousands of them) generated from the screen and free falling to the ground. Player can draw any shape of lines, which can guide those particles to certain cups. A image from google:
I'm trying to achieve similar effect using SpriteKit with Swift. Here's what I got:
Then I encounter a performance problem. Once the number of particles > 100. The CPU and energy costs are very high. (I use iPhone 6s). So I believe the Physics Engine in "Sugar, Sugar" is much simpler than the realistic SpriteKit. But I don't know what's the physics engine there and how can I achieve this in SpriteKit?
PS:
I use one single image as texture for all those particles, only loaded once to save performance. I only use SKSpriteNode, no ShapeNode is used for performance reason too.
I have not done a sand sim for a long time so I thought I would create a quick demo for you.
It is done in javascript, left mouse adds sand, right mouse draws lines. Depending on the machine it will handle thousands of grains of sand.
It works by creating an array of pixels, each pixel has a x,y position a delta x,y and a flag to indicate it is inactive (dead). Every frame I clear the display and then add the walls. Then for each pixel I check if there are pixels to the sides or below (depending on the direction of movement) and add sideways slippage, bounce of wall, or gravity. If a pixel has not moved for some time I set it as dead and only draw it to save time on the calculations.
The sim is very simple, the first pixel (grain) will never bump into another because it is drawn with a clear display, pixels can only see pixels created before them. But this works well as they self organize and will not overlap each other.
You can find the logic in the function display, (second function from bottom) there is some code for auto demo, then code for drawing the walls, displaying the walls, getting the pixel data and then doing the sim for each pixel.
Its not perfect (like the game you have mentioned) but it is just a quick hack to show how it is done. Also I made it to big for the inset window so best viewed full page.
/** SimpleFullCanvasMouse.js begin **/
const CANVAS_ELEMENT_ID = "canv";
const U = undefined;
var w, h, cw, ch; // short cut vars
var canvas, ctx, mouse;
var globalTime = 0;
var createCanvas, resizeCanvas, setGlobals;
var L = typeof log === "function" ? log : function(d){ console.log(d); }
createCanvas = function () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
c.id = CANVAS_ELEMENT_ID;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.width = cs.height = "100%";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); }
canvas.width = Math.floor(window.innerWidth/4);
canvas.height = Math.floor(window.innerHeight/4);
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); }
}
setGlobals = function(){ cw = (w = canvas.width) / 2; ch = (h = canvas.height) / 2; }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === U) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
e.preventDefault();
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
}
}
return mouse;
})();
var done = function(){
window.removeEventListener("resize",resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = U;
L("All done!")
}
resizeCanvas(); // create and size canvas
mouse.start(canvas,true); // start mouse on canvas and block context menu
window.addEventListener("resize",resizeCanvas); // add resize event
var simW = 200;
var simH = 200;
var wallCanvas = document.createElement("canvas");
wallCanvas.width = simW;
wallCanvas.height = simH;
var wallCtx = wallCanvas.getContext("2d");
var bounceDecay = 0.7;
var grav = 0.5;
var slip = 0.5;
var sandPerFrame = 5;
var idleTime = 50;
var pixels = [];
var inactiveCounter = 0;
var demoStarted;
var lastMouse;
var wallX;
var wallY;
function display(){ // Sim code is in this function
var blocked;
var obstructed;
w = canvas.width;
h = canvas.height;
var startX = Math.floor(w / 2) - Math.floor(simW / 2);
var startY = Math.floor(h / 2) - Math.floor(simH / 2);
if(lastMouse === undefined){
lastMouse = mouse.x + mouse.y;
}
if(lastMouse === mouse.x + mouse.y){
inactiveCounter += 1;
}else{
inactiveCounter = 0;
}
if(inactiveCounter > 10 * 60){
if(demoStarted === undefined){
wallCtx.beginPath();
var sy = simH / 6;
for(var i = 0; i < 4; i ++){
wallCtx.moveTo(simW * (1/6) - 10,sy * i + sy * 1);
wallCtx.lineTo(simW * (3/ 6) - 10,sy * i + sy * 2);
wallCtx.moveTo(simW * (5/6) + 10,sy * i + sy * 0.5);
wallCtx.lineTo(simW * (3/6) +10,sy * i + sy * 1.5);
}
wallCtx.stroke();
}
mouse.x = startX * 4 + (simW * 2);
mouse.y = startY * 4 + (simH * 2 )/5;
lastMouse = mouse.x + mouse.y;
mouse.buttonRaw = 1;
}
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0,0,w,h);
ctx.strokeRect(startX+1,startY+1,simW-2,simH-2)
ctx.drawImage(wallCanvas,startX,startY); // draws the walls
if(mouse.buttonRaw & 4){ // if right button draw walls
if(mouse.x/4 > startX && mouse.x/4 < startX + simW && mouse.y/4 > startY && mouse.y/4 < startY + simH){
if(wallX === undefined){
wallX = mouse.x/4 - startX
wallY = mouse.y/4 - startY
}else{
wallCtx.beginPath();
wallCtx.moveTo(wallX,wallY);
wallX = mouse.x/4 - startX
wallY = mouse.y/4 - startY
wallCtx.lineTo(wallX,wallY);
wallCtx.stroke();
}
}
}else{
wallX = undefined;
}
if(mouse.buttonRaw & 1){ // if left button add sand
for(var i = 0; i < sandPerFrame; i ++){
var dir = Math.random() * Math.PI;
var speed = Math.random() * 2;
var dx = Math.cos(dir) * 2;
var dy = Math.sin(dir) * 2;
pixels.push({
x : (Math.floor(mouse.x/4) - startX) + dx,
y : (Math.floor(mouse.y/4) - startY) + dy,
dy : dx * speed,
dx : dy * speed,
dead : false,
inactive : 0,
r : Math.floor((Math.sin(globalTime / 1000) + 1) * 127),
g : Math.floor((Math.sin(globalTime / 5000) + 1) * 127),
b : Math.floor((Math.sin(globalTime / 15000) + 1) * 127),
});
}
if(pixels.length > 10000){ // if over 10000 pixels reset
pixels = [];
}
}
// get the canvas pixel data
var data = ctx.getImageData(startX, startY,simW,simH);
var d = data.data;
// handle each pixel;
for(var i = 0; i < pixels.length; i += 1){
var p = pixels[i];
if(!p.dead){
var ind = Math.floor(p.x) * 4 + Math.floor(p.y) * 4 * simW;
d[ind + 3] = 0;
obstructed = false;
p.dy += grav;
var dist = Math.floor(p.y + p.dy) - Math.floor(p.y);
if(Math.floor(p.y + p.dy) - Math.floor(p.y) >= 1){
if(dist >= 1){
bocked = d[ind + simW * 4 + 3];
}
if(dist >= 2){
bocked += d[ind + simW * 4 * 2 + 3];
}
if(dist >= 3){
bocked += d[ind + simW * 4 * 3 + 3];
}
if(dist >= 4){
bocked += d[ind + simW * 4 * 4 + 3];
}
if( bocked > 0 || p.y + 1 > simH){
p.dy = - p.dy * bounceDecay;
obstructed = true;
}else{
p.y += p.dy;
}
}else{
p.y += p.dy;
}
if(d[ind + simW * 4 + 3] > 0){
if(d[ind + simW * 4 - 1] === 0 && d[ind + simW * 4 + 4 + 3] === 0 ){
p.dx += Math.random() < 0.5 ? -slip/2 : slip/2;
}else
if(d[ind + 4 + 3] > 0 && d[ind + simW * 4 - 1] === 0 ){
p.dx -= slip;
}else
if(d[ind - 1] + d[ind - 1 - 4] > 0 ){
p.dx += slip/2;
}else
if(d[ind +3] + d[ind + 3 + 4] > 0 ){
p.dx -= slip/2;
}else
if(d[ind + 1] + d[ind + 1] > 0 && d[ind + simW * 4 + 3] > 0 && d[ind + simW * 4 + 4 + 3] === 0 ){
p.dx += slip;
}else
if(d[ind + simW * 4 - 1] === 0 ){
p.dx += -slip/2;
}else
if(d[ind + simW * 4 + 4 + 3] === 0 ){
p.dx += -slip/2;
}
}
if(p.dx < 0){
if(Math.floor(p.x + p.dx) - Math.floor(p.x) <= -1){
if(d[ind - 1] > 0){
p.dx = -p.dx * bounceDecay;
}else{
p.x += p.dx;
}
}else{
p.x += p.dx;
}
}else
if(p.dx > 0){
if(Math.floor(p.x + p.dx) - Math.floor(p.x) >= 1){
if(d[ind + 4 + 3] > 0){
p.dx = -p.dx * bounceDecay;
}else{
p.x += p.dx;
}
}else{
p.x += p.dx;
}
}
var ind = Math.floor(p.x) * 4 + Math.floor(p.y) * 4 * simW;
d[ind ] = p.r;
d[ind + 1] = p.g;
d[ind + 2] = p.b;
d[ind + 3] = 255;
if(obstructed && p.dx * p.dx + p.dy * p.dy < 1){
p.inactive += 1;
if(p.inactive > idleTime){
p.dead = true;
}
}
}else{
var ind = Math.floor(p.x) * 4 + Math.floor(p.y) * 4 * simW;
d[ind ] = p.r;
d[ind + 1] = p.g;
d[ind + 2] = p.b;
d[ind + 3] = 255;
}
}
ctx.putImageData(data,startX, startY);
}
function update(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
// continue until mouse right down
if (!(mouse.buttonRaw & 2)) { requestAnimationFrame(update); } else { done(); }
}
requestAnimationFrame(update);
/** SimpleFullCanvasMouse.js end **/
* { font-family: arial; }
canvas { image-rendering: pixelated; }
<p>Right click drag to draw walls</p>
<p>Left click hold to drop sand</p>
<p>Demo auto starts in 10 seconds is no input</p>
<p>Sim resets when sand count reaches 10,000 grains</p>
<p>Middle button quits sim</p>
This is really basic question. I have an array "relevant_IDs". I need to store 1 to 100 values in it when variable category is 1. Similarly, 101 to 200 when category is 2. So on till 901 to 1000 when category is 10.
I have written code for it but it is not inserting 100 values in it.
Code:
for i=1: 1000
if(category==1 && i>0 && i< 101)
relevant_IDs(i) = i;
end
if(category==2 && i>100 && i< 201)
relevant_IDs(i) = i;
end
if(category==3 && i>200 && i< 301)
relevant_IDs(i) = i;
end
if(category==4 && i>300 && i< 401)
relevant_IDs(i) = i;
end
if(category==5 && i>400 && i< 501)
relevant_IDs(i) = i;
end
if(category==6 && i>500 && i< 601)
relevant_IDs(i) = i;
end
if(category==7 && i>600 && i< 701)
relevant_IDs(i) = i;
end
if(category==8 && i>700 && i< 801)
relevant_IDs(i) = i;
end
if(category==9 && i>800 && i< 901)
relevant_IDs(i) = i;
end
if(category==10 && i>900 && i< 1001)
relevant_IDs(i) = i;
end
end
Something like this should work and be much quicker:
relevant_IDs = (category - 1) * 100 + (1:100);
You could also just generate the whole thing (numbers from 1 to 1000), then index into the matrix using category value as index to get the desired relevant_IDs:
relevant_IDs = reshape(1:1000, [100,10]).';
relevant_IDs(category,:) % this will return a 1x100 row vector
% (category is a number from 1 to 10)
I'm updating some inherited legacy code and have found an interesting "approach" to a probelm that I am at a loss to optimize.
There is a set of data that can be somewhat simulated using the following:
approximateEntries = 5e6;
maxEntryValue = 5e12;
maxExtraEvents = 10e3;
event1 = randperm( maxEntryValue, approximateEntries + randi( maxExtraEvents ) );
event1 = sort(event1);
event2 = randperm( maxEntryValue, approximateEntries + randi( maxExtraEvents ) );
event2 = sort(event2);
event3 = randperm( maxEntryValue, approximateEntries + randi( maxExtraEvents ) );
event3 = sort(event3);
event4 = randperm( maxEntryValue, approximateEntries + randi( maxExtraEvents ) );
event4 = sort(event4);
data = [[ones(length(event1),1); 2*ones(length(event2),1); 3*ones(length(event3),1); 4*ones(length(event4),1)], ...
[event1'; event2'; event3'; event4']];
data = sortrows(data,2);
clear approximateEntries;
clear maxEntryValue;
clear maxExtraEvents;
Which is to say that thanks to the way the code before is written, I have access to four arrays, each with about five million elements and one large array containing the four arrays concatenated, sorted and marked in another column with which array the element originally came from. If i can avoid using the single large array in the solution to this problem, it can be eliminated from the code as it is not required later.
We are looking to find different characteristics of near (to within +/- 1000) matches between values across the four "event" matrices. the values may not necessarily be at the same indices but each "event" matrix will be sorted in ascending order. We are looking for:
Total number of times a value from each "event" matrix is unique, within +/- 1000, to that matrix.
Total number of times a value from each "event" matrix occurs +/- 1000 in just two matrices, with a seperate total for each possible pair of matrices.
Total number of times a value from each "event" matrix +/- 1000 occurs in just three matrices, with a sperate total for each triplet of matrices.
Total number of times a value +/- 1000 occurs in all four matrices
The legacy code attempts to do this in the following incredible way (I know it is not totally correct, that is why it is being updated)
onlyEvent1 = 0;
onlyEvent2 = 0;
onlyEvent3 = 0;
onlyEvent4 = 0;
onlyEvent1Event2 = 0;
onlyEvent1Event3 = 0;
onlyEvent1Event4 = 0;
onlyEvent2Event3 = 0;
onlyEvent2Event4 = 0;
onlyEvent3Event4 = 0;
onlyEvent1Event2Event3 = 0;
onlyEvent1Event2Event4 = 0;
onlyEvent1Event3Event4 = 0;
onlyEvent2Event3Event4 = 0;
allEvents = 0;
maxIndex = length(data);
workingIndex = 1;
fullGate = 2000;
while workingIndex < maxIndex
subIndex = 0;
withinRange = true;
% Prepare a buffer
buffer = zeros(200,2);
while ((workingIndex + subIndex) < maxIndex) && (withinRange == true)
if subIndex > 0
timeDifference = data(workingIndex + subIndex,2) - buffer(subIndex,2);
if timeDifference <= fullGate
buffer(subIndex + 1,:) = data(workingIndex + subIndex,:);
subIndex = subIndex + 1;
else
withinRange = false;
end % if
else
buffer(subIndex + 1,:) = data(workingIndex + subIndex,:);
subIndex = subIndex + 1;
end % if
end % while
event1 = false;
event2 = false;
event3 = false;
event4 = false;
compareIndex = 1;
while (buffer(compareIndex) ~= 0) && (compareIndex < length(buffer))
if buffer(compareIndex,1) == 1
event1 = true;
else
if buffer(compareIndex,1) == 2
event2 = true;
else
if buffer(compareIndex,1) == 3
event3 = true;
else
% Should really only be four
event4 = true;
end % if is 3
end % if is 2
end % if is 1
compareIndex = compareIndex + 1;
end % while buffer
if (event1 == true) && (event2 == true) && (event3 == true) && (event4 == true)
allEvents = allEvents + 1;
else
if (event1 == true) && (event2 == true) && (event3 == true) && (event4 == false)
onlyEvent1Event2Event3 = onlyEvent1Event2Event3 + 1;
else
if (event1 == true) && (event2 == true) && (event3 == false) && (event4 == true)
onlyEvent1Event2Event4 = onlyEvent1Event2Event4 + 1;
else
if (event1 == true) && (event2 == false) && (event3 == true) && (event4 == true)
onlyEvent1Event3Event4 = onlyEvent1Event3Event4 + 1;
else
if (event1 == false) && (event2 == true) && (event3 == true) && (event4 == true)
onlyEvent2Event3Event4 = onlyEvent2Event3Event4 + 1;
else
if (event1 == true) && (event2 == true) && (event3 == false) && (event4 == false)
onlyEvent1Event2 = onlyEvent1Event2 + 1;
else
if (event1 == true) && (event2 == false) && (event3 == true) && (event4 == false)
onlyEvent1Event3 = onlyEvent1Event3 + 1;
else
if (event1 == true) && (event2 == false) && (event3 == false) && (event4 == true)
onlyEvent1Event4 = onlyEvent1Event4 + 1;
else
if (event1 == false) && (event2 == true) && (event3 == true) && (event4 == false)
onlyEvent2Event3 = onlyEvent2Event3 + 1;
else
if (event1 == false) && (event2 == true) && (event3 == false) && (event4 == true)
onlyEvent2Event4 = onlyEvent2Event4 + 1;
else
if (event1 == false) && (event2 == false) && (event3 == true) && (event4 == true)
onlyEvent3Event4 = onlyEvent3Event4 + 1;
else
if (event1 == true) && (event2 == false) && (event3 == false) && (event4 == false)
onlyEvent1 = onlyEvent1 + 1;
else
if (event1 == false) && (event2 == true) && (event3 == false) && (event4 == false)
onlyEvent2 = onlyEvent2 + 1;
else
if (event1 == false) && (event2 == false) && (event3 == true) && (event4 == false)
onlyEvent3 = onlyEvent3 + 1;
else
onlyEvent4 = onlyEvent4 + 1;
end % if 3
end % if 2
end % if 1
end % if 3&4
end % if 2&4
end % if 2&3
end % if 1&4
end % if 1&3
end % if 1&2
end % if 2,3&4
end % if 1,3&4
end % if 1,2&4
end % if 1,2&3
end % if all events
workingIndex = workingIndex + subIndex;
end % while
I came up with the following basis for an approach:
temp=bsxfun(#plus, event1', -1000:1000);
matchOneTwo=sum(ismember(event2,temp));
matchOneThree=sum(ismember(event3,temp));
matchOneFour=sum(ismember(event4,temp));
temp=bsxfun(#plus, event2', -1000:1000);
etc...
but this fails due to lack of memory when generating "temp". Can anyone help with another approach?
[EDIT] To provide a small scale example as requested by Dennis Jaheruddin. This was produced by hand and is not real data. The periodicity was just to help the reader see the events used in comaprisons.
event1=[125, 1500, 5000, 15000, 22349,25000, 35000, 45000, 55000, 60325, 65000, 75000, 85000, 91117, 95000];
event2=[1750, 7000, 17000, 21562, 27000, 37000, 47000, 57000, 60256, 67000, 77000, 87000, 97000];
event3=[1126, 9000, 19000, 29000, 30130, 39000, 49000, 59000, 69000, 79000, 89000, 91560, 99000, 120000];
event4=[1, 1975, 11000, 21000, 31000, 31159, 41000, 51000, 60112, 61000, 71000, 81000, 91000, 91001, 101000, 130000];
Then:
Match 1, 2, 3, & 4 = 1
Match 1 & 2 = 1
Match 3 & 4 = 1
Match 1, 2, & 4 = 1
Match 1, 3, & 4 = 1
All the rest have zero matches.
Based on the question and comment I think the key is the ismemberf File Exchange submission
It allows you to check which values of one set occur in another set with a certain tolerance.
For example:
% How many elements from event1 are close to an element of event2?
% 3 elements
sum(ismemberf(event1,event2,'tol',1000))
% At how many positions are both event1 and event2 are close to an element in event4?
% At exactly 1 position
sum(ismemberf(event1(1:13),event4,'tol',1000)&ismemberf(event2,event4,'tol',1000))
Probably this is not all that you need, but it should be easy to build up from here.
I'm looking to implement SLAB6 into my raycaster, especially the kv6 support for voxelmodels. However the SLAB6 source by Ken Silverman is totally unreadably (mostly ASM) so I was hoping someone could point me to a proper C / Java source to load kv6 models or maybe to explain me the workings in some pseudocode preferably (since I want to know how to support the kv6, I know how it works). Thanks, Kaj
EDIT: the implementation would be in Java.
I found some code in an application called VoxelGL (author not mentioned in sourcecode):
void CVoxelWorld::generateSlabFromData(unsigned char *data, VoxelData *vdata, Slab *slab)
{
int currentpattern = 1;
int i = 0;
int n, totalcount, v, count;
n = 0;
v = 0;
while (1)
{
while (data[i] == currentpattern)
{
if (currentpattern == 1)
v++;
i++;
if (i == 256)
break;
}
n++;
if (i == 256)
{
if (currentpattern == 0)
n--;
break;
}
currentpattern ^= 1;
}
slab->nentries = n;
if (slab->description != 0)delete [] slab->description;
if (slab->data != 0)delete [] slab->data;
slab->description = new int[n];
slab->data = new VoxelData[v];
totalcount = 0;
v = 0;
currentpattern = 1;
for (i = 0; i < n; i++)
{
count = 0;
while (data[totalcount] == currentpattern)
{
count++;
totalcount++;
if (totalcount == 256)
break;
}
slab->description[i] = count-1;
if (i % 2 == 0)
{
memcpy(slab->data + v, vdata + totalcount - count, 3 * count);
v += count;
}
currentpattern ^= 1;
}
}
And:
#define clustersize 8
Slab *CVoxelWorld::getSlab(int x, int z)
{
int xgrid = x / clustersize;
int ygrid = z / clustersize;
int clusteroffset = xgrid * 1024 * clustersize + ygrid * clustersize * clustersize;
return &m_data[clusteroffset + (x & (clustersize - 1)) + (z & (clustersize - 1)) * clustersize];
}
And:
int CVoxelWorld::isSolid(int x, int y, int z)
{
Slab *slab;
if (y < 0 || y > 256)
return 0;
slab = getSlab(x, z);
int counter = 0;
for (int i = 0; i < slab->nentries; i++)
{
int height = slab->description[i] + 1;
if (i % 2 == 0)
{
if (y >= counter && y < counter + height)
return 1;
}
counter += height;
}
return 0;
}
I'm working on a charting algorithm that will give me a set n array of y axis values I would use on my graph.
The main problem is that I also want to calculate the number of number of steps to use and also use nice numbers for them. It must be able to take integers and doubles and be able to handle small ranges (under 1) and large ranges (over 10000 etc).
For example, if I was given a range of 0.1 - 0.9, ideally i would have values of 0, 0.2, 0.4, 0.6, 0.8, 1 but if I were given 0.3 to 0.7 I might use 0.3, 0.4, 0.5, 0.6, 0.7
This is what I have so far, it works well with small ranges, but terribly in large ranges, and doesn't give me nice numbers
-(double*)yAxisValues:(double)min (double):max {
double diff = max - min;
double divisor = 1.0;
if (diff > 1) {
while (diff > 1) {
diff /= 10;
divisor *= 10;
}
} else {
while (diff < 1) {
diff *= 10;
divisor *= 10;
}
}
double newMin = round(min * divisor) / divisor;
double newMax = round(max * divisor) / divisor;
if (newMin > min) {
newMin -= 1.0/divisor;
}
if (newMax < max) {
newMax += 1.0/divisor;
}
int test2 = round((newMax - newMin) * divisor);
if (test2 >= 7) {
while (test2 % 6 != 0 && test2 % 5 != 0 && test2 % 4 != 0 && test2 % 3 != 0) {
test2++;
newMax += 1.0/divisor;
}
}
if (test2 % 6 == 0) {
test2 = 6;
} else if (test2 % 5 == 0) {
test2 = 5;
} else if (test2 % 4 == 0 || test2 == 2) {
test2 = 4;
} else if (test2 % 3 == 0) {
test2 = 3;
}
double *values = malloc(sizeof(double) * (test2 + 1));
for (int i = 0; i < test2 + 1; i++) {
values[i] = newMin + (newMax - newMin) * i / test2;
}
return values;
}
Any suggestions?
Here's a snippet of code that does something similar, though has a slightly different approach. The "units" refer to what your are plotting on the graph. So if your scale is so that one unit on your graph should be 20 pixels on screen, this function would return how many units each step should be. With that information you can then easily calculate what the axis values are and where to draw them.
- (float)unitsPerMajorGridLine:(float)pixelsPerUnit {
float amountAtMinimum, orderOfMagnitude, fraction;
amountAtMinimum = [[self minimumPixelsPerMajorGridLine] floatValue]/pixelsPerUnit;
orderOfMagnitude = floor(log10(amountAtMinimum));
fraction = amountAtMinimum / pow(10.0, orderOfMagnitude);
if (fraction <= 2) {
return 2 * pow(10.0, orderOfMagnitude);
} else if (fraction <= 5) {
return 5 * pow(10.0, orderOfMagnitude);
} else {
return 10 * pow(10.0, orderOfMagnitude);
}
}
Simple adaptation for JavaScript (thanks a lot to Johan Kool for source)
const step = (() => {let pixelPerUnit = height / (end - size)
, amountAtMinimum = minimumPixelsPerMajorGridLine / pixelPerUnit
, orderOfMagnitude = Math.floor(Math.log10(amountAtMinimum))
, fraction = amountAtMinimum / Math.pow(10.0, orderOfMagnitude);
let result;
if (fraction <= 2) {
result = 2 * Math.pow(10.0, orderOfMagnitude);
} else if (fraction <= 5) {
result = 5 * Math.pow(10.0, orderOfMagnitude);
} else {
result = 10 * Math.pow(10.0, orderOfMagnitude);
}})();
let arr = [];
arr.push(start);
let curVal = start - start % step + step
, pxRatio = height / (end - start);
while (curVal < end) {
arr.push(curVal);
curVal += step;
}
arr.push(end);