resetting the particular sudoku box in p5? - p5.js

I am trying to implement a Sudoku table in p5.js, I am able to implement a cell where I can write a number (1-9) down. But I cannot remove a number, i.e. if I write '1' first on a cell and write '2', it writes on top of it.
So, how can I remove the existing number and write a different one?
EDIT:
my code till now:
//make a grid
function makegrid(rows, cols) {
var regrid = new Array(rows);
for (var i = 0; i < rows; i++) {
regrid[i] = new Array(cols);
}
return regrid;
}
var numrec = [];
var rows = 9;
var grid = [];
var cols = 9;
var w = 50;
var input;
//show the boxes
function show(i, j) {
this.i = i;
this.j = j;
var x = w * this.i;
var y = w * this.j;
noFill();
//input = createInput();
//input.position(x+5,y+5,w);
strokeWeight(1);
rect(x + 5, y + 5, w, w); //just to see line clearly
}
//there must be a better way to do it but for now ---> here we go for separation
// of tables
function boldlines() {
strokeWeight(4);
stroke(51);
line(5, 5, 5, w * 9 + 5);
line(w * 3 + 5, 5, w * 3 + 5, w * 9 + 5);
line(w * 6 + 5, 5, w * 6 + 5, w * 9 + 5);
line(5, w * 9 + 5, w * 9 + 5, w * 9 + 5);
line(5, 5, w * 9 + 5, 5);
line(w * 9 + 5, 5, w * 9 + 5, w * 9 + 5);
line(5, w * 3 + 5, w * 9 + 5, w * 3 + 5);
line(5, w * 6 + 5, w * 9 + 5, w * 6 + 5);
}
function setup() {
createCanvas(600, 600);
grid = makegrid(rows, cols);
numrec = makegrid(rows, cols);
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
numrec[i][j] = 1;
}
}
}
function draw() {
boldlines();
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
show(i, j);
//console.log(mouseX,mouseY);
}
}
}
function numput(x, y, num) {
//background(0);
if (num === 0) {
num = "";
}
textSize(30);
//textAlign(CENTER);
text(num, x + 30, y + 40);
}
function mousePressed() {
var mx = mouseX;
var my = mouseY;
var i = floor((mx + 5) / w);
var j = floor((my + 5) / w);
if (i >= 0 && j >= 0 && i < rows && j < cols) {
//console.log(i, j);
var num = numrec[i][j];
if (num === 10) {
num = 0;
}
numrec[i][j] = num + 1;
numput(i * w, j * w, num);
}
}

I figured out a way to hide old number in that cell and retain the content of all the other cells. I hid the previous number by drawing a rectangle on top of it with the same dimensions of that of a cell and then updating the cell with new number.
function numput(x, y, num) {
//background(0);
if (num === 0) {
num = "";
}
textSize(30);
//textAlign(CENTER);
//Newly added
strokeWeight(0);
fill(255,255,255);
rect(x+4 , y+4 , w, w);
boldlines();
text(num, x + 30, y + 40);
}
I have done some changes in function numput() to achieve it.

Related

Which point makes difference of execution speed in JS?

im studying Algorithm with Javascript.
It is a problem abt dijkstra algorith.
but i always meet TLE(Time Limit Exceeded) in the last case.
could i know which point makes my code slower.
i tried many ways like below
without function constructor
console output with forEach
take input with require('readline')
without recursive heapify
it is the problem link.
https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/12/ALDS1_12_C
It is my solution.
function MinPQ() {
this.heap = [];
}
MinPQ.prototype.swap = function (i, j) {
const temp = this.heap[i];
this.heap[i] = this.heap[j];
this.heap[j] = temp;
};
MinPQ.prototype.insert = function (n) {
const parentIdx = (idx) => Math.floor((idx - 1) * 0.5);
this.heap.push(n);
let curr = this.heap.length - 1;
let parent = parentIdx(curr);
while (curr > 0 && this.compare(curr, parent)) {
this.swap(curr, parent);
curr = parent;
parent = parentIdx(curr);
}
};
MinPQ.prototype.shift = function () {
const heapify = (idx) => {
let l = idx * 2 + 1;
let r = idx * 2 + 2;
let minIdx = idx;
if (l < this.heap.length && this.compare(l, minIdx)) {
minIdx = l;
}
if (r < this.heap.length && this.compare(r, minIdx)) {
minIdx = r;
}
if (minIdx !== idx) {
this.swap(idx, minIdx);
heapify(minIdx);
}
};
this.swap(0, this.heap.length - 1);
const root = this.heap.pop();
heapify(0);
return root;
};
MinPQ.prototype.compare = function (i, j) {
return this.heap[i][1] < this.heap[j][1];
};
function solution(input) {
const n = Number(input.shift());
const G = Array(n);
for (let i = 0; i < n; i++) {
const [u, k, ...adjs] = input.shift().split(' ').map(Number);
G[u] = [];
for (let j = 0; j < k; j++) {
const v = adjs[2 * j];
const c = adjs[2 * j + 1];
G[u][v] = c;
}
}
return dijkstra(n, G)
.map((e, idx) => `${idx} ${e}`)
.join('\n');
}
function dijkstra(n, G) {
const minPQ = new MinPQ();
const d = Array(n).fill(Infinity);
let count = 0;
minPQ.insert([0, 0]);
while (count < n) {
const node = minPQ.shift();
const [u, cost] = node;
if (d[u] < Infinity) continue;
d[u] = cost;
count++;
G[u].forEach((e, idx) => {
minPQ.insert([idx, cost + e]);
});
}
return d;
}
(function (test) {
const printSolution = (input) => console.log(solution(input));
if (test) {
printSolution([
'5',
'0 3 2 3 3 1 1 2',
'1 2 0 2 3 4',
'2 3 0 3 3 1 4 1',
'3 4 2 1 0 1 1 4 4 3',
'4 2 2 1 3 3',
]);
console.log('--');
printSolution([
'9',
'0 2 1 1 3 13',
'1 3 0 1 2 1 4 11',
'2 2 5 1 1 1',
'3 3 4 1 0 13 6 1',
'4 4 1 11 5 1 3 1 7 4',
'5 3 2 1 8 7 4 1',
'6 2 3 1 7 1',
'7 3 4 4 6 1 8 1',
'8 2 5 7 7 1',
]);
return;
}
printSolution(
require('fs').readFileSync('/dev/stdin', 'utf-8').split('\n')
);
})(0);
It is the solution what passed last case.
var h = [],hs = 0;
h[0] = new Array(2);
h[0][0] = -1;h[0][1] = -1;
function insert(key){
h[++hs] = key;
var i = hs;
while(h[i][1] < h[Math.floor(i / 2)][1]){
var ex = h[i];
h[i] = h[Math.floor(i / 2)];
h[Math.floor(i / 2)] = ex;
i = Math.floor(i / 2);
}
}
function extract(){
if(hs <= 0)
return;
var ret = h[1];
h[1] = h[hs--];
i = 1;
while((i * 2 <= hs && h[i][1] > h[i * 2][1]) || (i * 2 + 1 <= hs && h[i][1] > h[i * 2 + 1][1])){
var l = i * 2;var r = i * 2;
if(i * 2 + 1 <= hs)
r++;
var m = h[l][1] <= h[r][1] ? l : r;
var ex = h[i];
h[i] = h[m];
h[m] = ex;
i = m;
}
return ret;
}
function Main(input){
input = input.split("\n");
var n = parseInt(input[0],10);
var graph = new Array(n);
for(var i = 0;i < n;i++){
input[i + 1] = input[i + 1].split(" ");
var u = parseInt(input[i + 1][0],10);
var k = parseInt(input[i + 1][1],10);
graph[u] = new Array(k);
for(var j = 0;j < k;j++)
graph[u][j] = new Array(2);
for(var j = 0;j < k;j++){
graph[u][j][0] = parseInt(input[i + 1][j * 2 + 2],10);
graph[u][j][1] = parseInt(input[i + 1][j * 2 + 3],10);
}
}
var count = 1;
var sum = Array(n);
for(var i = 0;i < n;i++)
sum[i] = 1000000000;
sum[0] = 0;
for(var i = 0;i < graph[0].length;i++){
insert(graph[0][i]);
}
while(count < n){
var aa = extract();
if(sum[aa[0]] < 1000000000)
continue;
sum[aa[0]] = aa[1];
count++;
for(var i = 0;i < graph[aa[0]].length;i++){
graph[aa[0]][i][1] += sum[aa[0]];
insert(graph[aa[0]][i]);
}
}
for(var i = 0;i < n;i++){
console.log(i + " " + sum[i]);
}
}
Main(require("fs").readFileSync("/dev/stdin","utf8"));

Dithering (Floyd-Steinberg) only updates part of graphics object in p5.js

I'm trying to implement Floyd-Steinberg dithering in a P5.js sketch by pre-dithering a bunch of circles in a graphics object (in setup) and then drawing them later.
However, I keep running into the issue where only part of the circle is dithered, and the rest looks normal. Any suggestions are welcome as I'm really stumped as to what is going on.
setup():
let circs;
function setup() {
//...
createCanvas(1000,1000);
let size = 200;
circs = [];
circs.push({
gfx: createGraphics(size, size),
size: size,
color: color(random(255))
});
for (let i = 0; i < circs.length; i++)
dither(circs[i]);
// ...
}
draw():
function draw() {
if (!paused) {
background(bg);
drawShadow(4); // just a call to the drawingContext shadow
for (let i = 0; i < circs.length; i++) {
push();
translate(width / 2, height / 2);
imageMode(CENTER);
image(circs[i].gfx, 0, 0);
pop();
}
}
}
floyd-steinberg - based on https://openprocessing.org/sketch/1192123
function index(x, y, g) {
return (x + y * g.width) * 4;
}
function dither(g) {
g.loadPixels();
for (let y = 0; y < g.height - 1; y++) {
for (let x = 1; x < g.width - 1; x++) {
let oldr = g.pixels[index(x, y, g)];
let oldg = g.pixels[index(x, y, g) + 1];
let oldb = g.pixels[index(x, y, g) + 2];
let factor = 1.0;
let newr = round((factor * oldr) / 255) * (255 / factor);
let newg = round((factor * oldg) / 255) * (255 / factor);
let newb = round((factor * oldb) / 255) * (255 / factor);
g.pixels[index(x, y, g)] = newr;
g.pixels[index(x, y, g) + 1] = newg;
g.pixels[index(x, y, g) + 2] = newb;
g.pixels[index(x + 1, y, g)] += ((oldr - newr) * 7) / 16.0;
g.pixels[index(x + 1, y, g) + 1] += ((oldr - newr) * 7) / 16.0;
g.pixels[index(x + 1, y, g) + 2] += ((oldr - newr) * 7) / 16.0;
g.pixels[index(x - 1, y + 1, g)] += ((oldr - newr) * 3) / 16.0;
g.pixels[index(x - 1, y + 1, g) + 1] += ((oldr - newr) * 3) / 16.0;
g.pixels[index(x - 1, y + 1, g) + 2] += ((oldr - newr) * 3) / 16.0;
g.pixels[index(x, y + 1, g)] += ((oldr - newr) * 5) / 16.0;
g.pixels[index(x, y + 1, g) + 1] += ((oldr - newr) * 5) / 16.0;
g.pixels[index(x, y + 1, g) + 2] += ((oldr - newr) * 5) / 16.0;
g.pixels[index(x + 1, y + 1, g)] += ((oldr - newr) * 1) / 16.0;
g.pixels[index(x + 1, y + 1, g) + 1] += ((oldr - newr) * 1) / 16.0;
g.pixels[index(x + 1, y + 1, g) + 2] += ((oldr - newr) * 1) / 16.0;
}
}
g.updatePixels();
}
I'm not sure what I'm missing as the dithering algorithm loops over the height and width and then should be updating, but I think I'm missing something.
p5.Graphics objects have a pixelDensity inherited from the sketch. When the pixel density is > 1 as it is for high DPI displays you need to account for this when you are computing your pixels indices:
function index(x, y, g) {
const d = g.pixelDensity();
return (x + y * g.width * d) * 4;
}
And when you are processing pixels you will need to double the maximum values for x and y.
Here's a demonstration of the effects of pixelDensity (and whether or not you handle it):
let g;
function setup() {
createCanvas(400, 400);
g = createGraphics(width, height);
redrawGraphics();
noLoop();
setInterval(
() => {
redrawGraphics(frameCount % 2);
redraw();
},
2000
);
}
function index(x, y, g, d) {
return (x + y * g.width * d) * 4;
}
function redrawGraphics(hdpi) {
const d = hdpi ? pixelDensity() : 1;
g.background(0);
g.loadPixels();
for (let y = 0; y < height * 2; y++) {
for (let x = 0; x < width * 2; x++) {
let ix = index(x, y, g, d);
let r = map(sin((x - y) / width * TWO_PI), -1, 1, 0, 255);
g.pixels[ix] = r;
g.pixels[ix + 1] = 0;
g.pixels[ix + 2] = 0;
g.pixels[ix + 3] = 255;
}
}
g.updatePixels();
}
function draw() {
image(g, 0, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

How does recursive division to generate maze work? [duplicate]

I am currently developing a random maze generator that stores the maze in a 2-dimensional array called grid. This will then be used later on to generate a real 3D maze that the user can then walk through.
After doing some research, I attempted to create this maze generator using the recursive division algorithm, however due to the nature of the format of the maze, this isn't really working for me.
From what I understand, the recursive division method does not treat walls as cells.
For instance, my grid would look like this:
a b c d e f g h
1 - - - - - - - -
2 | | | | |
3 | | |
4 | - - | - |
5 | | | |
6 | - | - |
7 x |
8 - - - - - - - -
The point that I'm trying to get across here is that the grid I am trying to create will be represented something like this:
w w w w w w w w
w w w w w
w w w
w w w w w w
w w w w
w w w w w
g w
w w w w w w w w
Where 'w' is a wall and 'g' is the entrance/exit. So walls are placed into the grid, e.g. grid[1][2] == 'w'
The problem with the recursive division algorithm is that walls are not treated as members of the cell. All of the 'cells' would essentially contain whitespace and the walls would be placed around them.
So when I tried to implement this algorithm in my situation, I ended up with a result like this: (the black squares are walls, the white squares are empty, and the red square is the entrance)
My JSFiddle is located here.
Essentially the user will start at the red square and have to go through the maze and find keys that will open the door (which is the red square) to escape, so all of the whitespace in the maze would have to be accessible.
Does anyone have any ideas on how I can rewrite this algorithm to make sure that there is always a path from the red square to any other space in the maze? Ideally, the path would never be more than one square wide.
Code:
var grid;
function generate(dimensions, numDoors) {
//numDoors is unused right now
grid = new Array();
for (var i = 0; i < dimensions; i++) {
grid[i] = new Array();
for (var j = 0; j < dimensions; j++) {
grid[i][j] = "";
}
}
addOuterWalls();
var ent = addEntrance();
addInnerWalls(true, 1, grid.length - 2, 1, grid.length - 2, ent);
}
function addOuterWalls() {
for (var i = 0; i < grid.length; i++) {
if (i == 0 || i == (grid.length - 1)) {
for (var j = 0; j < grid.length; j++) {
grid[i][j] = "w";
}
} else {
grid[i][0] = "w";
grid[i][grid.length - 1] = "w";
}
}
}
function addEntrance() {
var x = randomNumber(1, grid.length - 1);
grid[grid.length - 1][x] = "g";
return x;
}
function addInnerWalls(h, minX, maxX, minY, maxY, gate) {
if (h) {
if (maxX - minX < 2) {
return;
}
var y = randomNumber(minY, maxY);
addHWall(minX, maxX, y);
addInnerWalls(!h, minX, maxX, minY, y-1, gate);
addInnerWalls(!h, minX, maxX, y + 1, maxY, gate);
} else {
if (maxY - minY < 2) {
return;
}
var x = randomNumber(minX, maxX);
addVWall(minY, maxY, x);
addInnerWalls(!h, minX, x-1, minY, maxY, gate);
addInnerWalls(!h, x + 1, maxX, minY, maxY, gate);
}
}
function addHWall(minX, maxX, y) {
var hole = randomNumber(minX, maxX);
for (var i = minX; i <= maxX; i++) {
if (i == hole) grid[y][i] = "";
else grid[y][i] = "w";
}
}
function addVWall(minY, maxY, x) {
var hole = randomNumber(minY, maxY);
for (var i = minY; i <= maxY; i++) {
if (i == hole) grid[i][x] = "";
else grid[i][x] = "w";
}
}
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function display() {
document.getElementById("cnt").innerHTML = "";
for (var i = 0; i < grid.length; i++) {
var output = "<div>";
for (var j = 0; j < grid.length; j++) {
output += "<b " + grid[i][j] + "></b>";
}
output += "</div>";
document.getElementById("cnt").innerHTML += output;
}
}
generate(30, 1, 1);
display();
Put walls only in even cells, and doors in odd cells, and make "dimensions" odd.
http://jsfiddle.net/tPm3s/1/
Code:
var grid;
function generate(dimensions, numDoors) {
grid = new Array();
for (var i = 0; i < dimensions; i++) {
grid[i] = new Array();
for (var j = 0; j < dimensions; j++) {
grid[i][j] = "";
}
}
addOuterWalls();
var ent = addEntrance();
addInnerWalls(true, 1, grid.length - 2, 1, grid.length - 2, ent);
}
function addOuterWalls() {
for (var i = 0; i < grid.length; i++) {
if (i == 0 || i == (grid.length - 1)) {
for (var j = 0; j < grid.length; j++) {
grid[i][j] = "w";
}
} else {
grid[i][0] = "w";
grid[i][grid.length - 1] = "w";
}
}
}
function addEntrance() {
var x = randomNumber(1, grid.length - 1);
grid[grid.length - 1][x] = "g";
return x;
}
function addInnerWalls(h, minX, maxX, minY, maxY, gate) {
if (h) {
if (maxX - minX < 2) {
return;
}
var y = Math.floor(randomNumber(minY, maxY)/2)*2;
addHWall(minX, maxX, y);
addInnerWalls(!h, minX, maxX, minY, y-1, gate);
addInnerWalls(!h, minX, maxX, y + 1, maxY, gate);
} else {
if (maxY - minY < 2) {
return;
}
var x = Math.floor(randomNumber(minX, maxX)/2)*2;
addVWall(minY, maxY, x);
addInnerWalls(!h, minX, x-1, minY, maxY, gate);
addInnerWalls(!h, x + 1, maxX, minY, maxY, gate);
}
}
function addHWall(minX, maxX, y) {
var hole = Math.floor(randomNumber(minX, maxX)/2)*2+1;
for (var i = minX; i <= maxX; i++) {
if (i == hole) grid[y][i] = "";
else grid[y][i] = "w";
}
}
function addVWall(minY, maxY, x) {
var hole = Math.floor(randomNumber(minY, maxY)/2)*2+1;
for (var i = minY; i <= maxY; i++) {
if (i == hole) grid[i][x] = "";
else grid[i][x] = "w";
}
}
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function display() {
document.getElementById("cnt").innerHTML = "";
for (var i = 0; i < grid.length; i++) {
var output = "<div>";
for (var j = 0; j < grid.length; j++) {
output += "<b " + grid[i][j] + "></b>";
}
output += "</div>";
document.getElementById("cnt").innerHTML += output;
}
}
generate(31, 1, 1);
display();

Hash a Set of Integers from a Domain into a Set of Buckets

Say I have a set of integers ranging between 1-100. I will only have 5 of these integers drawn out of a hat. I want to then take those 5 integers and place them into 5 buckets guaranteed unique (without having to deduplicate or anything using something like quadratic probing). Wondering how to do that.
For example, say I have these numbers (random from 1-100):
1 5 20 50 100
I then want to take those numbers and place them into these 5 buckets:
a b c d e
Using some hash function to accomplish it. For example, perhaps like this:
hash(1) -> b
hash(5) -> a
hash(20) -> e
hash(50) -> d
hash(100) -> c
Wondering how to write the hash function so that it takes a number x from a domain of numbers D and a set of numbers D(X) from that domain, and outputs 1 bucket b from the set of buckets B.
H : D(X) -> B
Next time around I might have 6 numbers between 1 and 1,000, going into 6 buckets. So then I would need a new hash function that works using those constraints (6 numbers, 6 buckets, range 1-1,000).
The goal is as few steps as possible.
Note: The hash function for this example won't take integers in a domain larger than 10,000 lets say, as well as the size of the set of integers limited to some small number too like 1,000.
Update
Basically I am trying to get this to happen:
// var domain = [1, 2, ..., 100]
// var set = [1, 5, 20, 50, 100]
// var buckets = [1, 2, 3, 4, 5]
hash(1) // 2
hash(5) // 1
hash(20) // 5
hash(50) // 4
hash(100) // 3
function hash(integer) {
if (integer == 1) return 2
if (integer == 5) return 1
if (integer == 20) return 5
if (integer == 50) return 4
if (integer == 100) return 3
}
But I don't know how to construct that hash function dynamically.
One solution (in JavaScript) would be to just create a map like this:
var map = {
1: 2,
5: 1,
20: 5,
50: 4,
100: 3
}
But that's sort of cheating because the object in JavaScript is implemented as a hashtable underneath (or something like that). So I am looking for how to do this at a low level, just using basically what assembly gives you.
Pretty much, I want to do this:
1
5 |
| | 20
| | 50 |
| | 100 | |
[ slot1, slot2, slot3, slot4, slot5 ]
Where 1 is somehow "hashed" to go into that slot2 in an array of size 5 (that slot is arbitrary for this example), etc.
Suppose the domain of your integer values is the range from 0 to n-1, and you want the set of values [x0, x1, ..., xk-1] to map to values from 0 to k-1.
Create an array of n values containing the numbers from 0 to k-1 in roughly equal amounts, for example [a0 = 0, a1 = 1, ..., ak = 0, ..., an = n%k].
Then for each of the k values in the initial set (xi, where i = 0 .. k-1), change the k-th element of this array to i, either by direct assignment or by swapping with a value from elsewhere (taking care not to clobber a value set for a previous element of the initial set).
Then to hash a value y, just fetch the y-th value from this array.
DEMO
Here's a Javascript demo that basically implements the above algorithm, except that instead of pre-filling the array with values from 0 to k-1, it first inserts the hash values for the selected items, then fills the remaining items with the repeating sequence of numbers from 0 to k-1. You will probably get better collision resistance by using a random sequence instead of incrementing values, but I hope you get the picture.
var hash_array;
function generate_hash() {
var i, j, k;
var v = document.getElementById;
var n = document.getElementById("n").value;
// Create a new hash lookup table
hash_array = Array(n);
// Initialize every value to -1
for (i=0; i<n; i++) hash_array[i] = -1;
// Map the given values to the first k hash buckets
var initial_values = document.getElementById("init").value.split(/ +/);
k = initial_values.length;
for (i=0; i<k; i++) {
hash_array[initial_values[i]] = i;
}
// Fill the remaining buckets with values from 0 to k-1
// This could be done by selecting values randomly, but
// here we're just cycling through the values from 0 to k-1
for (i=j=0; i<hash_array.length; i++) {
if (hash_array[i] == -1) {
hash_array[i] = j;
j = (j + 1) % k;
}
}
document.getElementById("gen").innerHTML = "Hash lookup table:<br>" + hash_array.join(", ");
}
<h2>Demo</h2>
<p>Creating a hash function that works on integer values less than <i>n</i>. What is the value of <i>n</i>?<br>
<input type="number" id="n" min="6" max="100" value="20"/></p>
<p>Enter a few different values separated by spaces. These will hash to the first buckets<br/>
<input type="text" size="40" id="init" value="2 3 5 6 9"/></p>
<p id="gen"><button onclick="generate_hash(); return false">Generate hash table</button></p>
Something like this should work:
Create a set of bucket IDs and populate it ahead of hashing (assumption here is that set guarantees uniqueness). This means that you have to know in advance how many buckets you want.
For each element from the input set calculate hash(element) modulo bucketIds.size to find index of the next ID to use.
Remove the resulting bucket ID from the set of bucked IDs
Repeat (until you are done or the set of IDs is exhausted)
Feel free to inspect the noddy implementation in JS using arrays (Node8).
If you'd like a function that's not a straight map, you could also experiment with Polynomial Regression.
Here's a JavaScript example using some free code under the GNU license.
/***************************************************************************
* Copyright (C) 2018 by Paul Lutus *
* lutusp#arachnoid.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
// classic Gauss-Jordan matrix manipulation functions
var gj = gj || {}
gj.divide = function(A, i, j, m) {
for (var q = j + 1; q < m; q++) {
A[i][q] /= A[i][j];
}
A[i][j] = 1;
}
gj.eliminate = function(A, i, j, n, m) {
for (var k = 0; k < n; k++) {
if (k != i && A[k][j] != 0) {
for (var q = j + 1; q < m; q++) {
A[k][q] -= A[k][j] * A[i][q];
}
A[k][j] = 0;
}
}
}
gj.echelonize = function(A) {
var n = A.length;
var m = A[0].length;
var i = 0;
var j = 0;
var k;
var swap;
while (i < n && j < m) {
//look for non-zero entries in col j at or below row i
k = i;
while (k < n && A[k][j] == 0) {
k++;
}
// if an entry is found at row k
if (k < n) {
// if k is not i, then swap row i with row k
if (k != i) {
swap = A[i];
A[i] = A[k];
A[k] = swap;
}
// if A[i][j] is != 1, divide row i by A[i][j]
if (A[i][j] != 1) {
gj.divide(A, i, j, m);
}
// eliminate all other non-zero entries
gj.eliminate(A, i, j, n, m);
i++;
}
j++;
}
}
// a simple data class
function Pair(x,y) {
this.x = x;
this.y = y;
};
Pair.prototype.toString = function() {return x + ',' + y};
// matrix functions
var matf = matf || {}
// a weak substitue for printf()
matf.number_format = function(n,p,w) {
s = n.toExponential(p);
while(s.length < w) {
s = ' ' + s;
}
return s;
}
// produce a single y result for a given x
matf.regress = function(x, terms) {
var y = 0;
var m = 1;
for (var i = 0; i < terms.length;i++) {
y += terms[i] * m;
m *= x;
}
return y;
}
// compute correlation coefficient
matf.corr_coeff = function(data, terms) {
var r = 0;
var n = data.length;
var sx = 0;
var sx2 = 0, sy = 0, sy2 = 0, sxy = 0;
var x, y;
for (var i = 0;i < data.length;i++) {
pr = data[i];
var x = matf.regress(pr.x, terms);
var y = pr.y;
sx += x;
sy += y;
sxy += x * y;
sx2 += x * x;
sy2 += y * y;
}
var div = Math.sqrt((sx2 - (sx * sx) / n) * (sy2 - (sy * sy) / n));
if (div != 0) {
r = Math.pow((sxy - (sx * sy) / n) / div, 2);
}
return r;
}
// compute standard error
matf.std_error = function(data, terms) {
var r = 0;
var n = data.length;
if (n > 2) {
var a = 0;
for (var i = 0;i < data.length;i++) {
pr = data[i];
a += Math.pow((matf.regress(pr.x, terms) - pr.y), 2);
}
r = Math.sqrt(a / (n - 2));
}
return r;
}
// create regression coefficients
// for provided data set
// data = pair array
// p = polynomial degree
matf.compute_coefficients = function(data, p) {
p += 1;
var n = data.length;
var r, c;
var rs = 2 * p - 1;
//
// by request: read each datum only once
// not the most efficient processing method
// but required if the data set is huge
//
// create square matrix with added RH column
m = Array();
for (var i = 0; i < p; i++) {
mm = Array();
for (var j = 0; j <= p; j++) {
mm[j] = 0;
}
m[i] = mm;
}
//double[][] m = new double[p][p + 1];
// create array of precalculated matrix data
mpc = Array();
for(var i = 0;i < rs;i++) {
mpc[i] = 0;
}
mpc[0] = n;
for (var i = 0;i < data.length;i++) {
pr = data[i];
// process precalculation array
for (r = 1; r < rs; r++) {
mpc[r] += Math.pow(pr.x, r);
}
// process RH column cells
m[0][p] += pr.y;
for (r = 1; r < p; r++) {
m[r][p] += Math.pow(pr.x, r) * pr.y;
}
}
// populate square matrix section
for (r = 0; r < p; r++) {
for (c = 0; c < p; c++) {
m[r][c] = mpc[r + c];
}
}
// reduce matrix
gj.echelonize(m);
// extract result column
terms = Array();
for (var i = 0;i < m.length;i++) {
mc = m[i];
terms[i] = mc[p];
}
return terms;
}
// test the system using known data
matf.test = function() {
var xd = [-1,0,1,2,3,5,7,9];
var yd = [-1,3,2.5,5,4,2,5,4];
data = Array();
for(var i = 0;i < xd.length;i++) {
data[i] = new Pair(xd[i],yd[i]);
}
terms = compute_coefficients(data,6);
var prec = 16;
var width = 24;
for(var i = 0;i < terms.length;i++) {
print(number_format(terms[i],prec,width) + ' * x^' + i);
}
cc = corr_coeff(data,terms);
print ('cc = ' + number_format(cc,prec,width));
se = std_error(data,terms);
print('se = ' + number_format(se,prec,width));
}
//test();
// "data" is an array of Pair(x,y) data
// p = polynomial degree
matf.process_data = function(data,p) {
var terms = matf.compute_coefficients(data,p);
var cc = matf.corr_coeff(data,terms);
var se = matf.std_error(data,terms);
return [terms,cc,se];
}
/**** END Paul Lutus' code ****/
function f(cs, x){
let n = cs.length - 1;
let result = 0;
for (let i=0; i<cs.length; i++)
result += cs[i] * Math.pow(x, i);
return result;
}
var data = [[1,1], [5,2], [20,3], [50,4], [100,5]];
var xy_data = []
for (let i of data)
xy_data.push(new Pair(i[0], i[1]));
var result = matf.process_data(xy_data, xy_data.length - 1);
for (let i=0; i<data.length; i++)
console.log(data[i][0], f(result[0], data[i][0]));

Generating a sphere in OpenGL without high level libraries - what's wrong with my code?

I've tried to implement an inefficient function to generate the points, normals, tex coords, and index list of a sphere.
Ignoring the lines, when I draw the sphere with OpenGL I get the following output which is clearly wrong:
Can anyone help me understand what's wrong with my code?
public SphereObject()
{
super();
// inefficient but quick sphere data
int num_points = 16;
double as = Math.PI / num_points;
double theta, phi;
double [] p;
ArrayList<double []> points = new ArrayList<double []>();
ArrayList<Integer> edges = new ArrayList<Integer>();
ArrayList<double []> normals = new ArrayList<double []>();
ArrayList<double []> tex = new ArrayList<double []>();
theta = Math.PI;
phi = Math.PI / 2;
for(int row = 0; row < num_points; row++)
{
for(int col = 0; col < num_points; col++)
{
p = new double[3];
p[0] = Math.sin(theta) * Math.cos(phi - as);
p[1] = Math.cos(theta) * Math.cos(phi - as);
p[2] = Math.sin(phi - as);
points.add(p);
normals.add(p);
tex.add(new double [] {0, 0});
p = new double[3];
p[0] = Math.sin(theta + 2 * as) * Math.cos(phi - as);
p[1] = Math.cos(theta + 2 * as) * Math.cos(phi - as);
p[2] = Math.sin(phi - as);
points.add(p);
normals.add(p);
tex.add(new double [] {1, 0});
p = new double[3];
p[0] = Math.sin(theta + 2 * as) * Math.cos(phi);
p[1] = Math.cos(theta + 2 * as) * Math.cos(phi);
p[2] = Math.sin(phi);
points.add(p);
normals.add(p);
tex.add(new double [] {1, 1});
p = new double[3];
p[0] = Math.sin(theta) * Math.cos(phi);
p[1] = Math.cos(theta) * Math.cos(phi);
p[2] = Math.sin(phi);
points.add(p);
normals.add(p);
tex.add(new double [] {0, 1});
// make triangles
edges.add(points.size()-1);
edges.add(points.size()-3);
edges.add(points.size()-4);
edges.add(points.size()-1);
edges.add(points.size()-2);
edges.add(points.size()-3);
theta -= 2 * as;
}
phi -= as;
}
sphereVertices = new double[points.size() * 3];
sphereTexcoords = new double[tex.size() * 2];
sphereNormals = new double[normals.size() * 3];
sphereIndices = new short[edges.size() * 1];
for(int c1 = 0; c1 < points.size(); c1 += 3)
{
sphereVertices[c1] = points.get(c1)[0];
sphereVertices[c1+1] = points.get(c1)[1];
sphereVertices[c1+2] = points.get(c1)[2];
}
for(int c1 = 0; c1 < tex.size(); c1 += 2)
{
sphereTexcoords[c1] = tex.get(c1)[0];
sphereTexcoords[c1+1] = tex.get(c1)[1];
}
for(int c1 = 0; c1 < normals.size(); c1 += 3)
{
sphereNormals[c1] = normals.get(c1)[0];
sphereNormals[c1+1] = normals.get(c1)[1];
sphereNormals[c1+2] = normals.get(c1)[2];
}
for(int c1 = 0; c1 < edges.size(); c1++)
{
sphereIndices[c1] = edges.get(c1).shortValue();
}
mVertBuff = fillBuffer(sphereVertices);
mTexCoordBuff = fillBuffer(sphereTexcoords);
mNormBuff = fillBuffer(sphereNormals);
mIndBuff = fillBuffer(sphereIndices);
}
My OpenGL code is below. The getVertices() functions et al return the buffers created in the Sphere constructor above.
Matrix.translateM(modelViewMatrix, 0, 0, 0, kObjectScale);
Matrix.scaleM(modelViewMatrix, 0, kObjectScale, kObjectScale, kObjectScale);
GLES20.glUseProgram(shaderProgramID);
GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getVertices());
GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getNormals());
GLES20.glVertexAttribPointer(textureCoordHandle, 2, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getTexCoords());
GLES20.glEnableVertexAttribArray(vertexHandle);
GLES20.glEnableVertexAttribArray(normalHandle);
GLES20.glEnableVertexAttribArray(textureCoordHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures.get(2).mTextureID[0]);
Matrix.translateM(modelViewMatrix, 0, (float)result[0], (float)result[1], (float)result[2]);
Matrix.rotateM(modelViewMatrix, 0, 0, 1, 0, 0);
Matrix.rotateM(modelViewMatrix, 0, 0, 0, 1, 0);
Matrix.rotateM(modelViewMatrix, 0, 0, 0, 0, 1);
Matrix.scaleM(modelViewMatrix, 0, 5, 5, 5);
Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, modelViewProjection, 0);
GLES20.glUniform1i(texSampler2DHandle, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.getInstance().getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT, sphere.getInstance().getIndices());
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisableVertexAttribArray(vertexHandle);
GLES20.glDisableVertexAttribArray(normalHandle);
GLES20.glDisableVertexAttribArray(textureCoordHandle);
The fillBuffer code is as follows:
protected Buffer fillBuffer(double[] array)
{
// Convert to floats because OpenGL doesn't work on doubles, and manually
// casting each input value would take too much time.
// Each float takes 4 bytes
ByteBuffer bb = ByteBuffer.allocateDirect(4 * array.length);
bb.order(ByteOrder.LITTLE_ENDIAN);
for (double d : array)
bb.putFloat((float) d);
bb.rewind();
return bb;
}
The problem is when you add the points to the final arrays:
for(int c1 = 0; c1 < points.size(); c1 += 3)
{
sphereVertices[c1] = points.get(c1)[0];
sphereVertices[c1+1] = points.get(c1)[1];
sphereVertices[c1+2] = points.get(c1)[2];
}
instead of using the same index for both the array and the list us separate ones:
for(int c1 = 0, i= 0; c1 < points.size(); c1++)
{
sphereVertices[i++] = points.get(c1)[0];
sphereVertices[i++] = points.get(c1)[1];
sphereVertices[i++] = points.get(c1)[2];
}
same for the other arrays

Resources