Related
I know the theory of glFrustumf and glOrthof but I'm quite
puzzled by the fact that there is another parameter in android
method that sets projection which is mOffset which does not exist in normal OpenGL implementation.``
public static void orthoM(float[] m, int mOffset, <---- mOffset
float left, float right, float bottom, float top,
float near, float far)
In the implementation it seems that this mOffset parameter shifts matrix index by the value that is setted:
m[mOffset + 0] = x;
m[mOffset + 5] = y;
m[mOffset +10] = z;
m[mOffset +12] = tx;
m[mOffset +13] = ty;
m[mOffset +14] = tz;
m[mOffset +15] = 1.0f;
m[mOffset + 1] = 0.0f;
m[mOffset + 2] = 0.0f;
m[mOffset + 3] = 0.0f;
m[mOffset + 4] = 0.0f;
m[mOffset + 6] = 0.0f;
m[mOffset + 7] = 0.0f;
m[mOffset + 8] = 0.0f;
m[mOffset + 9] = 0.0f;
m[mOffset + 11] = 0.0f;
Can anyone please explain why would you ever need to offset matrix index for the projection? What is a purpouse of mOffset?
This is a follow-up question of my previous question in here: How to draw the middle half of a sphere (in code)
We've already created the middle half of a sphere. My problem is how can I split the middle part into 4 similar parts (each as shown with 90 degree)? I already did 4 vertical parts for a full sphere simply with phi0/=4; and phi1/=4; in the slice loop and it was working perfectly. I did the same trick for the middle body that we got, but I see some edging problems on top and bottom boundaries when the top cap and bottom cap are connecting to the body. What is wrong with my approach?
Here is the popular code for generating a sphere programmatically:
UPDATE: It is not easy for me to take a screenshot, now. So basically when I texture all parts, I see they are not smoothly connected at the top and bottom boundaries. Splitting within the middle body is just fine.
private void generateSphere(int stackNumber, int sliceNumber, boolean facingOut) {
int capVertexNumber = 3 * sliceNumber;
int bodyVertexNumber = 4 * sliceNumber * (stackNumber - 2);
int vertexNumber = (2 * capVertexNumber) + bodyVertexNumber;
int triangleNumber = (2 * capVertexNumber) + (6 * sliceNumber * (stackNumber - 2));
vertices = new float[3 * vertexNumber];
normals = new float[3 * vertexNumber];
texCoords = new float[2 * vertexNumber];
indices = new char[triangleNumber];
// bottom cap
// createCap(stackNumber, sliceNumber, false, facingOut);
// body
createBody(stackNumber, sliceNumber, facingOut);
// top cap
createCap(stackNumber, sliceNumber, true, facingOut);
}
private void createCap(int stackNumber, int sliceNumber, boolean top, boolean facingOut) {
float stackPercentage0;
float stackPercentage1;
if (!top) {
stackPercentage0 = ((float) (stackNumber - 1) / stackNumber);
stackPercentage1 = 1.0f;
} else {
stackPercentage0 = (1.0f / stackNumber);
stackPercentage1 = 0.0f;
}
float t0 = stackPercentage0;
float t1 = stackPercentage1;
double theta0 = stackPercentage0 * Math.PI;
double theta1 = stackPercentage1 * Math.PI;
double cosTheta0 = Math.cos(theta0);
double sinTheta0 = Math.sin(theta0);
double cosTheta1 = Math.cos(theta1);
double sinTheta1 = Math.sin(theta1);
for (int slice = 0; slice < sliceNumber; slice++) {
float slicePercentage0 = ((float) (slice) / sliceNumber);
float slicePercentage1 = ((float) (slice + 1) / sliceNumber);
double phi0 = slicePercentage0 * 2.0 * Math.PI;
double phi1 = slicePercentage1 * 2.0 * Math.PI;
float s0, s1;
if (facingOut) {
s0 = 1 - slicePercentage0;
s1 = 1 - slicePercentage1;
} else {
s0 = slicePercentage0;
s1 = slicePercentage1;
}
float s2 = (s0 + s1) / 2.0f;
double cosPhi0 = Math.cos(phi0);
double sinPhi0 = Math.sin(phi0);
double cosPhi1 = Math.cos(phi1);
double sinPhi1 = Math.sin(phi1);
float x0 = (float) (sinTheta0 * cosPhi0);
float y0 = (float) cosTheta0;
float z0 = (float) (sinTheta0 * sinPhi0);
float x1 = (float) (sinTheta0 * cosPhi1);
float y1 = (float) cosTheta0;
float z1 = (float) (sinTheta0 * sinPhi1);
float x2 = (float) (sinTheta1 * cosPhi0);
float y2 = (float) cosTheta1;
float z2 = (float) (sinTheta1 * sinPhi0);
vertices[vertexCount + 0] = x0;
vertices[vertexCount + 1] = y0;
vertices[vertexCount + 2] = z0;
vertices[vertexCount + 3] = x1;
vertices[vertexCount + 4] = y1;
vertices[vertexCount + 5] = z1;
vertices[vertexCount + 6] = x2;
vertices[vertexCount + 7] = y2;
vertices[vertexCount + 8] = z2;
if (facingOut) {
normals[vertexCount + 0] = x0;
normals[vertexCount + 1] = y0;
normals[vertexCount + 2] = z0;
normals[vertexCount + 3] = x1;
normals[vertexCount + 4] = y1;
normals[vertexCount + 5] = z1;
normals[vertexCount + 6] = x2;
normals[vertexCount + 7] = y2;
normals[vertexCount + 8] = z2;
} else {
normals[vertexCount + 0] = -x0;
normals[vertexCount + 1] = -y0;
normals[vertexCount + 2] = -z0;
normals[vertexCount + 3] = -x1;
normals[vertexCount + 4] = -y1;
normals[vertexCount + 5] = -z1;
normals[vertexCount + 6] = -x2;
normals[vertexCount + 7] = -y2;
normals[vertexCount + 8] = -z2;
}
texCoords[texCoordCount + 0] = s0;
texCoords[texCoordCount + 1] = t0;
texCoords[texCoordCount + 2] = s1;
texCoords[texCoordCount + 3] = t0;
texCoords[texCoordCount + 4] = s2;
texCoords[texCoordCount + 5] = t1;
if ((facingOut && top) || (!facingOut && !top)) {
indices[indexCount + 0] = (char) (triangleCount + 1);
indices[indexCount + 1] = (char) (triangleCount + 0);
indices[indexCount + 2] = (char) (triangleCount + 2);
} else {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 1);
indices[indexCount + 2] = (char) (triangleCount + 2);
}
vertexCount += 9;
texCoordCount += 6;
indexCount += 3;
triangleCount += 3;
}
}
private void createBody(int stackNumber, int sliceNumber, boolean facingOut) {
for (int stack = 1; stack < stackNumber - 1; stack++) {
float stackPercentage0 = ((float) (stack) / stackNumber);
float stackPercentage1 = ((float) (stack + 1) / stackNumber);
float t0 = stackPercentage0;
float t1 = stackPercentage1;
double theta0 = stackPercentage0 * Math.PI;
double theta1 = stackPercentage1 * Math.PI;
double cosTheta0 = Math.cos(theta0);
double sinTheta0 = Math.sin(theta0);
double cosTheta1 = Math.cos(theta1);
double sinTheta1 = Math.sin(theta1);
for (int slice = 0; slice < sliceNumber; slice++) {
float slicePercentage0 = ((float) (slice) / sliceNumber);
float slicePercentage1 = ((float) (slice + 1) / sliceNumber);
double phi0 = slicePercentage0 * 2.0 * Math.PI;
double phi1 = slicePercentage1 * 2.0 * Math.PI;
float s0, s1;
if (facingOut) {
s0 = 1.0f - slicePercentage0;
s1 = 1.0f - slicePercentage1;
} else {
s0 = slicePercentage0;
s1 = slicePercentage1;
}
double cosPhi0 = Math.cos(phi0);
double sinPhi0 = Math.sin(phi0);
double cosPhi1 = Math.cos(phi1);
double sinPhi1 = Math.sin(phi1);
float x0 = (float) (sinTheta0 * cosPhi0);
float y0 = (float) cosTheta0;
float z0 = (float) (sinTheta0 * sinPhi0);
float x1 = (float) (sinTheta0 * cosPhi1);
float y1 = (float) cosTheta0;
float z1 = (float) (sinTheta0 * sinPhi1);
float x2 = (float) (sinTheta1 * cosPhi0);
float y2 = (float) cosTheta1;
float z2 = (float) (sinTheta1 * sinPhi0);
float x3 = (float) (sinTheta1 * cosPhi1);
float y3 = (float) cosTheta1;
float z3 = (float) (sinTheta1 * sinPhi1);
vertices[vertexCount + 0] = x0;
vertices[vertexCount + 1] = y0;
vertices[vertexCount + 2] = z0;
vertices[vertexCount + 3] = x1;
vertices[vertexCount + 4] = y1;
vertices[vertexCount + 5] = z1;
vertices[vertexCount + 6] = x2;
vertices[vertexCount + 7] = y2;
vertices[vertexCount + 8] = z2;
vertices[vertexCount + 9] = x3;
vertices[vertexCount + 10] = y3;
vertices[vertexCount + 11] = z3;
if (facingOut) {
normals[vertexCount + 0] = x0;
normals[vertexCount + 1] = y0;
normals[vertexCount + 2] = z0;
normals[vertexCount + 3] = x1;
normals[vertexCount + 4] = y1;
normals[vertexCount + 5] = z1;
normals[vertexCount + 6] = x2;
normals[vertexCount + 7] = y2;
normals[vertexCount + 8] = z2;
normals[vertexCount + 9] = x3;
normals[vertexCount + 10] = y3;
normals[vertexCount + 11] = z3;
} else {
normals[vertexCount + 0] = -x0;
normals[vertexCount + 1] = -y0;
normals[vertexCount + 2] = -z0;
normals[vertexCount + 3] = -x1;
normals[vertexCount + 4] = -y1;
normals[vertexCount + 5] = -z1;
normals[vertexCount + 6] = -x2;
normals[vertexCount + 7] = -y2;
normals[vertexCount + 8] = -z2;
normals[vertexCount + 9] = -x3;
normals[vertexCount + 10] = -y3;
normals[vertexCount + 11] = -z3;
}
texCoords[texCoordCount + 0] = s0;
texCoords[texCoordCount + 1] = t0;
texCoords[texCoordCount + 2] = s1;
texCoords[texCoordCount + 3] = t0;
texCoords[texCoordCount + 4] = s0;
texCoords[texCoordCount + 5] = t1;
texCoords[texCoordCount + 6] = s1;
texCoords[texCoordCount + 7] = t1;
// one quad looking from outside toward center
//
// #formatter:off
//
// s1 --> s0
//
// t0 1-----0
// | | |
// v | |
// t1 3-----2
//
// #formatter:on
//
// Note that tex_coord t increase from top to bottom because the
// texture image is loaded upside down.
if (facingOut) {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 1);
indices[indexCount + 2] = (char) (triangleCount + 2);
indices[indexCount + 3] = (char) (triangleCount + 2);
indices[indexCount + 4] = (char) (triangleCount + 1);
indices[indexCount + 5] = (char) (triangleCount + 3);
} else {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 2);
indices[indexCount + 2] = (char) (triangleCount + 1);
indices[indexCount + 3] = (char) (triangleCount + 2);
indices[indexCount + 4] = (char) (triangleCount + 3);
indices[indexCount + 5] = (char) (triangleCount + 1);
}
vertexCount += 12;
texCoordCount += 8;
indexCount += 6;
triangleCount += 4;
}
}
}
I've tried to port these 2 functions from Lua to Java (bspline2D and bspline3D), but they doen't work. The output is completely different, and I can't figure out why.
I've already tried messing with the array numbers, the subdivision value, the for loops, and everything that came up my head.
The test provided should give numbers that are totally different one another, but they're not.
These are the Java results:
bspline2D
0.0, 0.0, 0.0
-1.0, 0.0, 0.0
-1.0, 0.0, 0.0
-1.0, 0.0, 0.0
-1.0, 0.0, 0.0
0.0, 0.0, 0.0
0.0, 0.0, 0.0
0.0, 0.0, 0.0
0.0, 0.0, 0.0
bspline3D
-2.0, 0.0, 0.0
-1.0, 0.0, 0.0
-1.0, 0.0, 0.0
0.0, 0.0, 0.0
0.0, 0.0, 0.0
...which are clearly wrong.
These are the outputs from the Lua version, the correct ones.
bspline2D
0, 0
-0.75, 0
-0.5, 0
-0.25, 0
0, 0
0.24739583, 0.0026041667
0.47916666, 0.020833334
0.6796875, 0.0703125
0.8333333, 0.16666667
0.9270833, 0.31770834
0.9583333, 0.5
0.9270833, 0.6822917
0.8333333, 0.8333333
0.6796875, 0.9296875
0.47916666, 0.9791667
0.24739583, 0.9973958
0, 1
bspline3D
0, 0, 0
0.24739583, 0.0026041667, 0
0.47916666, 0.020833334, 0
0.6796875, 0.0703125, 0
0.8333333, 0.16666667, 0
0.9270833, 0.31770834, 0
0.9583333, 0.5, 0
0.9270833, 0.6822917, 0
0.8333333, 0.8333333, 0
0.6796875, 0.9296875, 0
0.47916666, 0.9791667, 0
0.24739583, 0.9973958, 0
0, 1, 0
-0.25, 1, 0
-0.5, 1, 0
-0.75, 1, 0
-1, 1, 0
-1.25, 1, 0
-1.5, 1, 0
-1.75, 1, 0
-2, 1, 0
I've been scratching my head for days on this, and I have just no ideas, so I thought maybe someone here at StackOverflow would.
Here's the Java version that needs to be fixed:
public static List<Vector3> bspline2D(Vector3[] pointz, int subdivision) {
ArrayList spline = new ArrayList();
int pointsNumber = pointz.length;
Vector3[] points = new Vector3[pointsNumber + 4];
System.arraycopy(pointz, 0, points, 2, pointsNumber);
points[1] = points[2].add(points[2].minus(points[3]));
points[0] = points[1].add(points[1].minus(points[2]));
points[pointsNumber + 2] = points[pointsNumber - 3].add(points[pointsNumber - 3].minus(points[pointsNumber - 4]));
points[pointsNumber + 3] = points[pointsNumber].add(points[pointsNumber].minus(points[pointsNumber - 3]));
spline.add(pointz[0]);
for(int i = 1; i < pointsNumber - 1; ++i) {
double[] a = new double[4];
double[] b = new double[4];
a[0] = (-points[i - 1].x + 3.0D * points[i].x - 3.0D * points[i + 1].x + points[i + 2].x) / 6.0D;
a[1] = (3.0D * points[i - 1].x - 6.0D * points[i].x + 3.0D * points[i + 1].x) / 6.0D;
a[2] = (-3.0D * points[i - 1].x + 3.0D * points[i + 1].x) / 6.0D;
a[3] = (points[i - 1].x + 4.0D * points[i].x + points[i + 1].x) / 6.0D;
b[0] = (-points[i - 1].y + 3.0D * points[i].y - 3.0D * points[i + 1].y + points[i + 2].y) / 6.0D;
b[1] = (3.0D * points[i - 1].y - 6.0D * points[i].y + 3.0D * points[i + 1].y) / 6.0D;
b[2] = (-3.0D * points[i - 1].y + 3.0D * points[i + 1].y) / 6.0D;
b[3] = (points[i - 1].y + 4.0D * points[i].y + points[i + 1].y) / 6.0D;
for(int j = 0; j < subdivision; ++j) {
double t = j / subdivision;
spline.add(new Vector3((a[2] + t * (a[1] + t * a[0])) * t + a[3], (b[2] + t * (b[1] + t * b[0])) * t + b[3]));
}
}
return spline;
}
public static List<Vector3> bspline3D(Vector3[] pointz, int subdivision) {
ArrayList spline = new ArrayList();
int pointsNumber = pointz.length;
Vector3[] points = new Vector3[pointsNumber + 4];
System.arraycopy(pointz, 0, points, 2, pointsNumber);
points[1] = points[2].add(points[2].minus(points[3]));
points[0] = points[1].add(points[1].minus(points[2]));
points[pointsNumber + 2] = pointz[pointsNumber - 1].add(pointz[pointsNumber - 1].minus(pointz[pointsNumber - 2]));
points[pointsNumber + 3] = points[pointsNumber].add(points[pointsNumber].minus(pointz[pointsNumber - 1]));
spline.add(points[0]);
for(int i = 1; i < pointsNumber - 1; ++i) {
double[] a = new double[4];
double[] b = new double[4];
double[] c = new double[4];
a[0] = (-points[i - 1].x + 3.0D * points[i].x - 3.0D * points[i + 1].x + points[i + 2].x) / 6.0D;
a[1] = (3.0D * points[i - 1].x - 6.0D * points[i].x + 3.0D * points[i + 1].x) / 6.0D;
a[2] = (-3.0D * points[i - 1].x + 3.0D * points[i + 1].x) / 6.0D;
a[3] = (points[i - 1].x + 4.0D * points[i].x + points[i + 1].x) / 6.0D;
b[0] = (-points[i - 1].y + 3.0D * points[i].y - 3.0D * points[i + 1].y + points[i + 2].y) / 6.0D;
b[1] = (3.0D * points[i - 1].y - 6.0D * points[i].y + 3.0D * points[i + 1].y) / 6.0D;
b[2] = (-3.0D * points[i - 1].y + 3.0D * points[i + 1].y) / 6.0D;
b[3] = (points[i - 1].y + 4.0D * points[i].y + points[i + 1].y) / 6.0D;
c[0] = (-points[i - 1].z + 3.0D * points[i].z - 3.0D * points[i + 1].z + points[i + 2].z) / 6.0D;
c[1] = (3.0D * points[i - 1].z - 6.0D * points[i].z + 3.0D * points[i + 1].z) / 6.0D;
c[2] = (-3.0D * points[i - 1].z + 3.0D * points[i + 1].z) / 6.0D;
c[3] = (points[i - 1].z + 4.0D * points[i].z + points[i + 1].z) / 6.0D;
for(int j = 1; j < subdivision; ++j) {
double t = j / subdivision;
Vector3 sp = new Vector3();
sp.x = (a[2] + t * (a[1] + t * a[0])) * t + a[3];
sp.y = (b[2] + t * (b[1] + t * b[0])) * t + b[3];
sp.z = (c[2] + t * (c[1] + t * c[0])) * t + c[3];
spline.add(sp);
}
}
return spline;
}
public static void main(String[] args) {
Vector3[] v = new Vector3[] {new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0)};
System.out.println("\nbspline2D");
for(Vector3 v3 : bspline2D(v, 4)) {
System.out.println(v3.x + ", " + v3.y + ", " + v3.z);
}
System.out.println("\nbspline3D");
for(Vector3 v3 : bspline3D(v, 4)) {
System.out.println(v3.x + ", " + v3.y + ", " + v3.z);
}
}
And here's the Lua version:
function M.bspline2D(points, subdivision)
local spline = {}
local wbvector2 = dofile('wbvector3.lua')
local pointsNumber = #points
-- extend the points array
points[0] = wbvector2.add(points[1], wbvector2.minus(points[1], points[2]))
points[-1] = wbvector2.add(points[0], wbvector2.minus(points[0], points[1]))
points[pointsNumber + 1] = wbvector2.add(points[pointsNumber], wbvector2.minus(points[pointsNumber], points[pointsNumber - 1]))
points[pointsNumber + 2] = wbvector2.add(points[pointsNumber + 1], wbvector2.minus(points[pointsNumber + 1], points[pointsNumber]))
-- Interpolation
local index = 1
spline[index] = points[1] -- first point
for i = 0, pointsNumber - 1 do
local a = {} -- compute the third order coefficients for x
local b = {} -- compute the third order coefficients for y
a[1] = (-points[i-1].x + 3 * points[i].x - 3 * points[i+1].x + points[i+2].x) / 6.0;
a[2] = (3 * points[i-1].x - 6 * points[i].x + 3 * points[i+1].x) / 6.0;
a[3] = (-3 * points[i-1].x + 3 * points[i+1].x) / 6.0;
a[4] = (points[i-1].x + 4 * points[i].x + points[i+1].x) / 6.0;
b[1] = (-points[i-1].y + 3 * points[i].y - 3 * points[i+1].y + points[i+2].y) / 6.0;
b[2] = (3 * points[i-1].y - 6 * points[i].y + 3 * points[i+1].y) / 6.0;
b[3] = (-3 * points[i-1].y + 3 * points[i+1].y) / 6.0;
b[4] = (points[i-1].y + 4 * points[i].y + points[i+1].y) / 6.0;
for j = 1, subdivision do
index = index + 1
spline[index] = {}
local t = j / subdivision
spline[index].x = (a[3] + t * (a[2] + t * a[1])) * t + a[4]
spline[index].y = (b[3] + t * (b[2] + t * b[1])) * t + b[4]
end
end
return spline
end
function M.bspline3D(points, subdivision)
local spline = {}
local wbcore = require('wbcore')
local wbvector3 = require('wbvector3')
local pointsNumber = wbcore.tablelength(points)
-- extend the points array
points[0] = wbvector3.add(points[1], wbvector3.minus(points[1], points[2]))
points[-1] = wbvector3.add(points[0], wbvector3.minus(points[0], points[1]))
points[pointsNumber + 1] = wbvector3.add(points[pointsNumber], wbvector3.minus(points[pointsNumber], points[pointsNumber - 1]))
points[pointsNumber + 2] = wbvector3.add(points[pointsNumber + 1], wbvector3.minus(points[pointsNumber + 1], points[pointsNumber]))
-- Interpolation
local index = 1
spline[index] = points[1] -- first point
for i = 1, pointsNumber - 1 do
local a = {} -- compute the third order coefficients for x
local b = {} -- compute the third order coefficients for y
local c = {} -- compute the third order coefficients for z
a[1] = (-points[i-1].x + 3 * points[i].x - 3 * points[i+1].x + points[i+2].x) / 6.0;
a[2] = (3 * points[i-1].x - 6 * points[i].x + 3 * points[i+1].x) / 6.0;
a[3] = (-3 * points[i-1].x + 3 * points[i+1].x) / 6.0;
a[4] = (points[i-1].x + 4 * points[i].x + points[i+1].x) / 6.0;
b[1] = (-points[i-1].y + 3 * points[i].y - 3 * points[i+1].y + points[i+2].y) / 6.0;
b[2] = (3 * points[i-1].y - 6 * points[i].y + 3 * points[i+1].y) / 6.0;
b[3] = (-3 * points[i-1].y + 3 * points[i+1].y) / 6.0;
b[4] = (points[i-1].y + 4 * points[i].y + points[i+1].y) / 6.0;
c[1] = (-points[i-1].z + 3 * points[i].z - 3 * points[i+1].z + points[i+2].z) / 6.0;
c[2] = (3 * points[i-1].z - 6 * points[i].z + 3 * points[i+1].z) / 6.0;
c[3] = (-3 * points[i-1].z + 3 * points[i+1].z) / 6.0;
c[4] = (points[i-1].z + 4 * points[i].z + points[i+1].z) / 6.0;
for j = 1, subdivision do
index = index + 1
spline[index] = {}
local t = j / subdivision
spline[index].x = (a[3] + t * (a[2] + t * a[1])) * t + a[4]
spline[index].y = (b[3] + t * (b[2] + t * b[1])) * t + b[4]
spline[index].z = (c[3] + t * (c[2] + t * c[1])) * t + c[4]
end
end
return spline
end
local v = {{x = 0, y = 0, z = 0}, {x = 1, y = 0, z = 0}, {x = 1, y = 1, z = 0}, {x = 0, y = 1, z = 0}}
print()
print("bspline2D");
for _, v3 in ipairs(M.bspline2D(v, 4)) do
print(v3.x .. ", " .. v3.y .. ", " .. v3.z)
end
print()
print("bspline3D");
for _, v3 in ipairs(M.bspline3D(v, 4)) do
print(v3.x .. ", " .. v3.y .. ", " .. v3.z)
end
My friend and I was having a lot of trouble trying to implement the perceptron algorithm, but then I found this tutorial, it goes through a java implementation and then has some example code. I substituted my own data structures for there ones in the tutorial, and it works! :)
HOWEVER
I made this substitution in the most simplistic possible way, manually enumerating my data structures. This works as a proof of concept, for my experimental "toy" data, but most certainly is not able to tackle the real data I want to consider. It's far too rigid.
Perhaps someone more proficient in abstract thinking and loops would be able to show me how I can improve this code.
The important data structure to consider is Map<File, int[] >, it looks like this:
/data/test/sports/t.s_1.txt, [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
/data/test/politics/t.p_0.txt, [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
/data/test/atheism/t.a_0.txt, [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
/data/test/science/t.s_0.txt, [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
The code that needs to be generalized proceeds this sentence, you can also find the full programme on this github page.
Thank you for your consideration & happy Japanese/Chinese new year!
public static void perceptron( Set<String> GLOBO_DICT, Map<File, int[] > training_perceptron_input, Map<File, int[] > test_perceptron_input)
{
//number of features, number of x, y, z
int size_of_globo_dict = GLOBO_DICT.size();
//number of instances
int NUM_INSTANCES = training_perceptron_input.size();
//three variables (features) they enumerate by
//features, xyz, i also do that
double[] a00 = new double [NUM_INSTANCES];
double[] a01 = new double [NUM_INSTANCES];
double[] a02 = new double [NUM_INSTANCES];
double[] a03 = new double [NUM_INSTANCES];
double[] a04 = new double [NUM_INSTANCES];
double[] a05 = new double [NUM_INSTANCES];
double[] a06 = new double [NUM_INSTANCES];
double[] a07 = new double [NUM_INSTANCES];
double[] a08 = new double [NUM_INSTANCES];
double[] a09 = new double [NUM_INSTANCES];
double[] a10 = new double [NUM_INSTANCES];
double[] a11 = new double [NUM_INSTANCES];
double[] a12 = new double [NUM_INSTANCES];
double[] a13 = new double [NUM_INSTANCES];
double[] a14 = new double [NUM_INSTANCES];
double[] a15 = new double [NUM_INSTANCES];
double[] a16 = new double [NUM_INSTANCES];
double[] a17 = new double [NUM_INSTANCES];
double[] a18 = new double [NUM_INSTANCES];
double[] a19 = new double [NUM_INSTANCES];
double[] a20 = new double [NUM_INSTANCES];
double[] a21 = new double [NUM_INSTANCES];
double[] a22 = new double [NUM_INSTANCES];
double[] a23 = new double [NUM_INSTANCES];
double[] a24 = new double [NUM_INSTANCES];
double[] a25 = new double [NUM_INSTANCES];
double[] a26 = new double [NUM_INSTANCES];
double[] a27 = new double [NUM_INSTANCES];
double[] a28 = new double [NUM_INSTANCES];
double[] a29 = new double [NUM_INSTANCES];
double[] a30 = new double [NUM_INSTANCES];
//2d array for features
int x = 0;
int[][] feature_matrix = new int[ training_perceptron_input.size() ][ GLOBO_DICT.size() ];
String[][] output_label = new String[ training_perceptron_input.size() ][ 2 ];
for(Entry<File, int[]> entry : training_perceptron_input.entrySet())
{
int[] container =entry.getValue();
for(int j = 0; j < 4; j++)
{
feature_matrix[x][j] = container[j];
output_label[x][1] = entry.getKey().toString();
}
x++;
}
int[] outputs = new int [NUM_INSTANCES];
for(int g = 0; g < NUM_INSTANCES; g++)
{
a00[g] = feature_matrix[g][ 0];
a01[g] = feature_matrix[g][ 1];
a02[g] = feature_matrix[g][ 2];
a03[g] = feature_matrix[g][ 3];
a04[g] = feature_matrix[g][ 4];
a05[g] = feature_matrix[g][ 5];
a06[g] = feature_matrix[g][ 6];
a07[g] = feature_matrix[g][ 7];
a08[g] = feature_matrix[g][ 8];
a09[g] = feature_matrix[g][ 9];
a10[g] = feature_matrix[g][10];
a11[g] = feature_matrix[g][11];
a12[g] = feature_matrix[g][12];
a13[g] = feature_matrix[g][13];
a14[g] = feature_matrix[g][14];
a15[g] = feature_matrix[g][15];
a16[g] = feature_matrix[g][16];
a17[g] = feature_matrix[g][17];
a18[g] = feature_matrix[g][18];
a19[g] = feature_matrix[g][19];
a20[g] = feature_matrix[g][20];
a21[g] = feature_matrix[g][21];
a22[g] = feature_matrix[g][22];
a23[g] = feature_matrix[g][23];
a24[g] = feature_matrix[g][24];
a25[g] = feature_matrix[g][25];
a26[g] = feature_matrix[g][26];
a27[g] = feature_matrix[g][27];
a28[g] = feature_matrix[g][28];
a29[g] = feature_matrix[g][29];
a30[g] = feature_matrix[g][30];
if(output_label[g][1].equals( "/home/yamada/Workbench/SUTD/ISTD_50.570/assignments/practice_data/data/train/atheism/a_0.txt" ) )
{
outputs[g] = 1;
}
else
{
outputs[g] = 0;
}
}
double[] weights = new double[ GLOBO_DICT.size() + 1];// 32 for input variables and one for bias
double localError, globalError;
int i, p, iteration, output;
weights[ 0] = randomNumber(0,1);// w1
weights[ 1] = randomNumber(0,1);// w2
weights[ 2] = randomNumber(0,1);// w3
weights[ 3] = randomNumber(0,1);// w4
weights[ 4] = randomNumber(0,1);// w5
weights[ 5] = randomNumber(0,1);// w6
weights[ 6] = randomNumber(0,1);// w7
weights[ 7] = randomNumber(0,1);// w8
weights[ 8] = randomNumber(0,1);// w9
weights[ 9] = randomNumber(0,1);// w10
weights[10] = randomNumber(0,1);// w11
weights[11] = randomNumber(0,1);// w12
weights[12] = randomNumber(0,1);// w13
weights[13] = randomNumber(0,1);// w14
weights[14] = randomNumber(0,1);// w15
weights[15] = randomNumber(0,1);// w16
weights[16] = randomNumber(0,1);// w17
weights[17] = randomNumber(0,1);// w18
weights[18] = randomNumber(0,1);// w19
weights[19] = randomNumber(0,1);// w20
weights[20] = randomNumber(0,1);// w21
weights[21] = randomNumber(0,1);// w22
weights[22] = randomNumber(0,1);// w23
weights[23] = randomNumber(0,1);// w24
weights[24] = randomNumber(0,1);// w25
weights[25] = randomNumber(0,1);// w26
weights[26] = randomNumber(0,1);// w27
weights[27] = randomNumber(0,1);// w28
weights[28] = randomNumber(0,1);// w29
weights[29] = randomNumber(0,1);// w30
weights[30] = randomNumber(0,1);// w31
weights[31] = randomNumber(0,1);// this is the bias
iteration = 0;
do {
iteration++;
globalError = 0;
//loop through all instances (complete one epoch)
for (p = 0; p < NUM_INSTANCES; p++)
{
// calculate predicted class
output = calculateOutput(theta,
weights,
a00[p],
a01[p],
a02[p],
a03[p],
a04[p],
a05[p],
a06[p],
a07[p],
a08[p],
a09[p],
a10[p],
a11[p],
a12[p],
a13[p],
a14[p],
a15[p],
a16[p],
a17[p],
a18[p],
a19[p],
a20[p],
a21[p],
a22[p],
a23[p],
a24[p],
a25[p],
a26[p],
a27[p],
a28[p],
a29[p],
a30[p]);
// difference between predicted and actual class values
localError = outputs[p] - output;
//update weights and bias
weights[ 0] += LEARNING_RATE * localError * a00[p];
weights[ 1] += LEARNING_RATE * localError * a01[p];
weights[ 2] += LEARNING_RATE * localError * a02[p];
weights[ 3] += LEARNING_RATE * localError * a03[p];
weights[ 4] += LEARNING_RATE * localError * a04[p];
weights[ 5] += LEARNING_RATE * localError * a05[p];
weights[ 6] += LEARNING_RATE * localError * a06[p];
weights[ 7] += LEARNING_RATE * localError * a07[p];
weights[ 8] += LEARNING_RATE * localError * a08[p];
weights[ 9] += LEARNING_RATE * localError * a09[p];
weights[10] += LEARNING_RATE * localError * a10[p];
weights[11] += LEARNING_RATE * localError * a11[p];
weights[12] += LEARNING_RATE * localError * a12[p];
weights[13] += LEARNING_RATE * localError * a13[p];
weights[14] += LEARNING_RATE * localError * a14[p];
weights[15] += LEARNING_RATE * localError * a15[p];
weights[16] += LEARNING_RATE * localError * a16[p];
weights[17] += LEARNING_RATE * localError * a17[p];
weights[18] += LEARNING_RATE * localError * a18[p];
weights[19] += LEARNING_RATE * localError * a19[p];
weights[20] += LEARNING_RATE * localError * a20[p];
weights[21] += LEARNING_RATE * localError * a21[p];
weights[22] += LEARNING_RATE * localError * a22[p];
weights[23] += LEARNING_RATE * localError * a23[p];
weights[24] += LEARNING_RATE * localError * a24[p];
weights[25] += LEARNING_RATE * localError * a25[p];
weights[26] += LEARNING_RATE * localError * a26[p];
weights[27] += LEARNING_RATE * localError * a27[p];
weights[28] += LEARNING_RATE * localError * a28[p];
weights[29] += LEARNING_RATE * localError * a29[p];
weights[30] += LEARNING_RATE * localError * a30[p];
weights[31] += LEARNING_RATE * localError;
//summation of squared error (error value for all instances)
globalError += (localError*localError);
}
/* Root Mean Squared Error */
System.out.println("Iteration "+iteration+" : RMSE = "+Math.sqrt(globalError/NUM_INSTANCES));
}
while (globalError != 0 && iteration <= MAX_ITER);
System.out.println("\n=======\nDecision boundary equation:");
System.out.println(
" a00 *" +
weights[ 0] +
" a01 *" +
weights[ 1] +
" a02 *" +
weights[ 2] +
" a03 *" +
weights[ 3] +
" a04 *" +
weights[ 4] +
" a05 *" +
weights[ 5] +
" a06 *" +
weights[ 6] +
" a07 *" +
weights[ 7] +
" a08 *" +
weights[ 8] +
" a09 *" +
weights[ 9] +
" a10 *" +
weights[10] +
" a11 *" +
weights[11] +
" a12 *" +
weights[12] +
" a13 *" +
weights[13] +
" a14 *" +
weights[14] +
" a15 *" +
weights[15] +
" a16 *" +
weights[16] +
" a17 *" +
weights[17] +
" a18 *" +
weights[18] +
" a19 *" +
weights[19] +
" a20 *" +
weights[20] +
" a21 *" +
weights[21] +
" a22 *" +
weights[22] +
" a23 *" +
weights[23] +
" a24 *" +
weights[24] +
" a25 *" +
weights[25] +
" a26 *" +
weights[26] +
" a27 *" +
weights[27] +
" a28 *" +
weights[28] +
" a29 *" +
weights[29] +
" a30 *" +
weights[30] +
" bias: " +
weights[31]);
//2d array for features
int x_TEST = 0;
int[][] feature_matrix_TEST = new int[ test_perceptron_input.size() ][ GLOBO_DICT.size() ];
for(Entry<File, int[]> entry : test_perceptron_input.entrySet())
{
int[] container =entry.getValue();
for(int jj = 0; jj < 4; jj++)
{
feature_matrix_TEST[x_TEST][jj] = container[jj];
}
x_TEST++;
}
//three variables (features) they enumerate by
//features, xyz, i also do that
double[] z00 = new double [NUM_INSTANCES];
double[] z01 = new double [NUM_INSTANCES];
double[] z02 = new double [NUM_INSTANCES];
double[] z03 = new double [NUM_INSTANCES];
double[] z04 = new double [NUM_INSTANCES];
double[] z05 = new double [NUM_INSTANCES];
double[] z06 = new double [NUM_INSTANCES];
double[] z07 = new double [NUM_INSTANCES];
double[] z08 = new double [NUM_INSTANCES];
double[] z09 = new double [NUM_INSTANCES];
double[] z10 = new double [NUM_INSTANCES];
double[] z11 = new double [NUM_INSTANCES];
double[] z12 = new double [NUM_INSTANCES];
double[] z13 = new double [NUM_INSTANCES];
double[] z14 = new double [NUM_INSTANCES];
double[] z15 = new double [NUM_INSTANCES];
double[] z16 = new double [NUM_INSTANCES];
double[] z17 = new double [NUM_INSTANCES];
double[] z18 = new double [NUM_INSTANCES];
double[] z19 = new double [NUM_INSTANCES];
double[] z20 = new double [NUM_INSTANCES];
double[] z21 = new double [NUM_INSTANCES];
double[] z22 = new double [NUM_INSTANCES];
double[] z23 = new double [NUM_INSTANCES];
double[] z24 = new double [NUM_INSTANCES];
double[] z25 = new double [NUM_INSTANCES];
double[] z26 = new double [NUM_INSTANCES];
double[] z27 = new double [NUM_INSTANCES];
double[] z28 = new double [NUM_INSTANCES];
double[] z29 = new double [NUM_INSTANCES];
double[] z30 = new double [NUM_INSTANCES];
for(int g = 0; g < NUM_INSTANCES; g++)
{
z00[g] = feature_matrix_TEST[g][ 0];
z01[g] = feature_matrix_TEST[g][ 1];
z02[g] = feature_matrix_TEST[g][ 2];
z03[g] = feature_matrix_TEST[g][ 3];
z04[g] = feature_matrix_TEST[g][ 4];
z05[g] = feature_matrix_TEST[g][ 5];
z06[g] = feature_matrix_TEST[g][ 6];
z07[g] = feature_matrix_TEST[g][ 7];
z08[g] = feature_matrix_TEST[g][ 8];
z09[g] = feature_matrix_TEST[g][ 9];
z10[g] = feature_matrix_TEST[g][10];
z11[g] = feature_matrix_TEST[g][11];
z12[g] = feature_matrix_TEST[g][12];
z13[g] = feature_matrix_TEST[g][13];
z14[g] = feature_matrix_TEST[g][14];
z15[g] = feature_matrix_TEST[g][15];
z16[g] = feature_matrix_TEST[g][16];
z17[g] = feature_matrix_TEST[g][17];
z18[g] = feature_matrix_TEST[g][18];
z19[g] = feature_matrix_TEST[g][19];
z20[g] = feature_matrix_TEST[g][20];
z21[g] = feature_matrix_TEST[g][21];
z22[g] = feature_matrix_TEST[g][22];
z23[g] = feature_matrix_TEST[g][23];
z24[g] = feature_matrix_TEST[g][24];
z25[g] = feature_matrix_TEST[g][25];
z26[g] = feature_matrix_TEST[g][26];
z27[g] = feature_matrix_TEST[g][27];
z28[g] = feature_matrix_TEST[g][28];
z29[g] = feature_matrix_TEST[g][29];
z30[g] = feature_matrix_TEST[g][30];
}
int output_TEST;
// calculate predicted class TEST
output_TEST = calculateOutput(theta,
weights,
z00[2],
z01[2],
z02[2],
z03[2],
z04[2],
z05[2],
z06[2],
z07[2],
z08[2],
z09[2],
z10[2],
z11[2],
z12[2],
z13[2],
z14[2],
z15[2],
z16[2],
z17[2],
z18[2],
z19[2],
z20[2],
z21[2],
z22[2],
z23[2],
z24[2],
z25[2],
z26[2],
z27[2],
z28[2],
z29[2],
z30[2]);
System.out.println("\n=======\nTEST Point:");
System.out.println(
"z00[0]:" + z00[0] +
"z01[0]:" + z01[0] +
"z02[0]:" + z02[0] +
"z03[0]:" + z03[0] +
"z04[0]:" + z04[0] +
"z05[0]:" + z05[0] +
"z06[0]:" + z06[0] +
"z07[0]:" + z07[0] +
"z08[0]:" + z08[0] +
"z09[0]:" + z09[0] +
"z10[0]:" + z10[0] +
"z11[0]:" + z11[0] +
"z12[0]:" + z12[0] +
"z13[0]:" + z13[0] +
"z14[0]:" + z14[0] +
"z15[0]:" + z15[0] +
"z16[0]:" + z16[0] +
"z17[0]:" + z17[0] +
"z18[0]:" + z18[0] +
"z19[0]:" + z19[0] +
"z20[0]:" + z20[0] +
"z21[0]:" + z21[0] +
"z22[0]:" + z22[0] +
"z23[0]:" + z23[0] +
"z24[0]:" + z24[0] +
"z25[0]:" + z25[0] +
"z26[0]:" + z26[0] +
"z27[0]:" + z27[0] +
"z28[0]:" + z28[0] +
"z29[0]:" + z29[0] +
"z30[0]:" + z30[0]
);
System.out.println("class = "+output_TEST);
}
//end main
/**
* returns a random double value within a given range
* #param min the minimum value of the required range (int)
* #param max the maximum value of the required range (int)
* #return a random double value between min and max
*/
public static double randomNumber(int min , int max) {
DecimalFormat df = new DecimalFormat("#.####");
double d = min + Math.random() * (max - min);
String s = df.format(d);
double x = Double.parseDouble(s);
return x;
}
/**
* returns either 1 or 0 using a threshold function
* theta is 0range
* #param theta an integer value for the threshold
* #param weights[] the array of weights
* #param x the x input value
* #param y the y input value
* #param z the z input value
* #return 1 or 0
*/
static int calculateOutput(int theta,
double weights[],
double a00,
double a01,
double a02,
double a03,
double a04,
double a05,
double a06,
double a07,
double a08,
double a09,
double a10,
double a11,
double a12,
double a13,
double a14,
double a15,
double a16,
double a17,
double a18,
double a19,
double a20,
double a21,
double a22,
double a23,
double a24,
double a25,
double a26,
double a27,
double a28,
double a29,
double a30)
{
double sum = a00 *
weights[ 0] +
a01 *
weights[ 1] +
a02 *
weights[ 2] +
a03 *
weights[ 3] +
a04 *
weights[ 4] +
a05 *
weights[ 5] +
a06 *
weights[ 6] +
a07 *
weights[ 7] +
a08 *
weights[ 8] +
a09 *
weights[ 9] +
a10 *
weights[10] +
a11 *
weights[11] +
a12 *
weights[12] +
a13 *
weights[13] +
a14 *
weights[14] +
a15 *
weights[15] +
a16 *
weights[16] +
a17 *
weights[17] +
a18 *
weights[18] +
a19 *
weights[19] +
a20 *
weights[20] +
a21 *
weights[21] +
a22 *
weights[22] +
a23 *
weights[23] +
a24 *
weights[24] +
a25 *
weights[25] +
a26 *
weights[26] +
a27 *
weights[27] +
a28 *
weights[28] +
a29 *
weights[29] +
a30 *
weights[30] +
weights[31];
return (sum >= theta) ? 1 : 0;
}
}
Here is a more generalized version your Perceptron class, simplified to reduce code duplication. Though untested, this is mechanically equivalent to the code you presented in your question.
class Perceptron {
static final int MAX_ITER = 100;
static final double LEARNING_RATE = 0.1;
static final int THETA = 0;
static final String FILEPATH =
"/home/yamada/Workbench/SUTD/ISTD_50.570/assignments/practice_data/data/train/atheism/a_0.txt";
public static void perceptron(Set<String> globoDict,
Map<File, int[]> trainingPerceptronInput,
Map<File, int[]> testPerceptronInput)
{
final int globoDictSize = globoDict.size(); // number of features (x, y, z)
// weights total 32 (31 for input variables and one for bias)
double[] weights = new double[globoDictSize + 1];
for (int i = 0; i < weights.length; i++) {
weights[i] = Math.floor(Math.random() * 10000) / 10000;
}
int inputSize = trainingPerceptronInput.size();
int[] outputs = new int[inputSize];
final double[][] a =
initializeOutput(trainingPerceptronInput, globoDictSize, outputs);
double globalError;
int iteration = 0;
do {
iteration++;
globalError = 0;
// loop through all instances (complete one epoch)
for (int p = 0; p < inputSize; p++) {
// calculate predicted class
int output = calculateOutput(THETA, weights, a, p);
// difference between predicted and actual class values
double localError = outputs[p] - output;
int i;
for (i = 0; i < a.length; i++) {
weights[i] += LEARNING_RATE * localError * a[i][p];
}
weights[i] += LEARNING_RATE * localError;
// summation of squared error (error value for all instances)
globalError += localError * localError;
}
/* Root Mean Squared Error */
System.out.println("Iteration "
+ iteration + " : RMSE = " + Math.sqrt(globalError
/ inputSize));
} while (globalError != 0 && iteration <= MAX_ITER);
System.out.println("\n=======\nDecision boundary equation:");
int i;
for (i = 0; i < a.length; i++) {
System.out.print(" a");
if (i < 10) System.out.print(0);
System.out.print(i + " *" + weights[i]);
}
System.out.println(" bias: " + weights[i]);
inputSize = testPerceptronInput.size();
outputs = new int[inputSize];
double[][] z = initializeOutput(testPerceptronInput, globoDictSize, outputs);
// calculate predicted class TEST
int output = calculateOutput(THETA, weights, z, 2);
System.out.println("\n=======\nTEST Point:");
for (i = 0; i < z.length; i++) {
System.out.print(" z");
if (i < 10) System.out.print(0);
System.out.print(i + "[0]" + z[i][0]);
}
System.out.println();
System.out.println("class = " + output);
}
static double[][] initializeOutput(
Map<File, int[]> perceptronInput, int size, int[] outputs)
{
final int inputSize = perceptronInput.size();
final double[][] a = new double[size][inputSize];
// 2d array for features
int[][] feature_matrix = new int[inputSize][size];
String[] output_label = new String[inputSize];
int x = 0;
for (Entry<File, int[]> entry : perceptronInput.entrySet()) {
int[] container = entry.getValue();
for (int j = 0; j < container.length; j++) {
feature_matrix[x][j] = container[j];
output_label[x] = String.valueOf(entry.getKey());
}
x++;
}
for (x = 0; x < inputSize; x++) {
for (int i = 0; i < a.length; i++) {
a[i][x] = feature_matrix[x][i];
}
outputs[x] = output_label[x].equals(FILEPATH) ? 1 : 0;
}
return a;
}
/**
* returns either 1 or 0 using a threshold function
* theta is 0range
* #param theta an integer value for the threshold
* #param weights the array of weights
* #param a the array of inputs
* #return 1 or 0
*/
static int calculateOutput(int theta, double[] weights, double[][] a, int index)
{
double sum = 0;
int i;
for (i = 0; i < a.length; i++) {
sum += weights[i] * a[i][index];
}
sum += weights[i];
return (sum >= theta) ? 1 : 0;
}
}
I hope this is instructional for you.
I have an algorithm in JavaScript which I want to transform into Java.
Here is the JavaScript code:
function downScaleCanvas(cv, scale) {
if (!(scale < 1) || !(scale > 0)) throw ('scale must be a positive number <1 ');
var sqScale = scale * scale; // square scale = area of source pixel within target
var sw = cv.width; // source image width
var sh = cv.height; // source image height
var tw = Math.ceil(sw * scale); // target image width
var th = Math.ceil(sh * scale); // target image height
var sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
var tx = 0, ty = 0, yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
var tX = 0, tY = 0; // rounded tx, ty
var w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
// weight is weight of current source point within target.
// next weight is weight of current source point within next target's point.
var crossX = false; // does scaled px cross its current px right border ?
var crossY = false; // does scaled px cross its current px bottom border ?
var sBuffer = cv.getContext('2d').getImageData(0, 0, sw, sh).data; // source buffer 8 bit rgba
var tBuffer = new Float32Array(4 * sw * sh); // target buffer Float32 rgb
var sR = 0, sG = 0, sB = 0; // source's current point r,g,b
// untested !
var sA = 0; //source alpha
for (sy = 0; sy < sh; sy++) {
ty = sy * scale; // y src position within target
tY = 0 | ty; // rounded : target pixel's y
yIndex = 4 * tY * tw; // line index within target array
crossY = (tY != (0 | ty + scale));
if (crossY) { // if pixel is crossing botton target pixel
wy = (tY + 1 - ty); // weight of point within target pixel
nwy = (ty + scale - tY - 1); // ... within y+1 target pixel
}
for (sx = 0; sx < sw; sx++, sIndex += 4) {
tx = sx * scale; // x src position within target
tX = 0 | tx; // rounded : target pixel's x
tIndex = yIndex + tX * 4; // target pixel index within target array
crossX = (tX != (0 | tx + scale));
if (crossX) { // if pixel is crossing target pixel's right
wx = (tX + 1 - tx); // weight of point within target pixel
nwx = (tx + scale - tX - 1); // ... within x+1 target pixel
}
sR = sBuffer[sIndex ]; // retrieving r,g,b for curr src px.
sG = sBuffer[sIndex + 1];
sB = sBuffer[sIndex + 2];
sA = sBuffer[sIndex + 3];
if (!crossX && !crossY) { // pixel does not cross
// just add components weighted by squared scale.
tBuffer[tIndex ] += sR * sqScale;
tBuffer[tIndex + 1] += sG * sqScale;
tBuffer[tIndex + 2] += sB * sqScale;
tBuffer[tIndex + 3] += sA * sqScale;
} else if (crossX && !crossY) { // cross on X only
w = wx * scale;
// add weighted component for current px
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
tBuffer[tIndex + 3] += sA * w;
// add weighted component for next (tX+1) px
nw = nwx * scale;
tBuffer[tIndex + 4] += sR * nw; // not 3
tBuffer[tIndex + 5] += sG * nw; // not 4
tBuffer[tIndex + 6] += sB * nw; // not 5
tBuffer[tIndex + 7] += sA * nw; // not 6
} else if (crossY && !crossX) { // cross on Y only
w = wy * scale;
// add weighted component for current px
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
tBuffer[tIndex + 3] += sA * w;
// add weighted component for next (tY+1) px
nw = nwy * scale;
tBuffer[tIndex + 4 * tw ] += sR * nw; // *4, not 3
tBuffer[tIndex + 4 * tw + 1] += sG * nw; // *4, not 3
tBuffer[tIndex + 4 * tw + 2] += sB * nw; // *4, not 3
tBuffer[tIndex + 4 * tw + 3] += sA * nw; // *4, not 3
} else { // crosses both x and y : four target points involved
// add weighted component for current px
w = wx * wy;
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
tBuffer[tIndex + 3] += sA * w;
// for tX + 1; tY px
nw = nwx * wy;
tBuffer[tIndex + 4] += sR * nw; // same for x
tBuffer[tIndex + 5] += sG * nw;
tBuffer[tIndex + 6] += sB * nw;
tBuffer[tIndex + 7] += sA * nw;
// for tX ; tY + 1 px
nw = wx * nwy;
tBuffer[tIndex + 4 * tw ] += sR * nw; // same for mul
tBuffer[tIndex + 4 * tw + 1] += sG * nw;
tBuffer[tIndex + 4 * tw + 2] += sB * nw;
tBuffer[tIndex + 4 * tw + 3] += sA * nw;
// for tX + 1 ; tY +1 px
nw = nwx * nwy;
tBuffer[tIndex + 4 * tw + 4] += sR * nw; // same for both x and y
tBuffer[tIndex + 4 * tw + 5] += sG * nw;
tBuffer[tIndex + 4 * tw + 6] += sB * nw;
tBuffer[tIndex + 4 * tw + 7] += sA * nw;
}
} // end for sx
} // end for sy
// create result canvas
var resCV = document.createElement('canvas');
resCV.width = tw;
resCV.height = th;
var resCtx = resCV.getContext('2d');
var imgRes = resCtx.getImageData(0, 0, tw, th);
var tByteBuffer = imgRes.data;
// convert float32 array into a UInt8Clamped Array
var pxIndex = 0; //
for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 4, tIndex += 4, pxIndex++) {
tByteBuffer[tIndex] = Math.ceil(tBuffer[sIndex]);
tByteBuffer[tIndex + 1] = Math.ceil(tBuffer[sIndex + 1]);
tByteBuffer[tIndex + 2] = Math.ceil(tBuffer[sIndex + 2]);
tByteBuffer[tIndex + 3] = Math.ceil(tBuffer[sIndex + 3]);
}
// writing result to canvas.
resCtx.putImageData(imgRes, 0, 0);
return resCV;
}
Here is what I did so far in Java:
public static final CanvasElement scaleCanvas(CanvasElement cv, double scale) {
if (!(scale < 1) || !(scale > 0)) {
// throw new Exception("scale must be a positive number <1 ");
}
double sqScale = scale * scale; // square scale = area of source pixel within target
int sw = cv.getWidth(); // source image width
int sh = cv.getHeight(); // source image height
double tw = Math.ceil(sw * scale); // target image width
double th = Math.ceil(sh * scale); // target image height
int sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
double tx = 0, ty = 0;
int yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
double tX = 0, tY = 0; // rounded tx, ty
double w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
// weight is weight of current source point within target.
// next weight is weight of current source point within next target's point.
boolean crossX = false; // does scaled px cross its current px right border ?
boolean crossY = false; // does scaled px cross its current px bottom border ?
CanvasPixelArray sBuffer = cv.getContext2d().getImageData(0, 0, sw, sh).getData(); // source buffer 8 bit rgba
Float32Array tBuffer = TypedArrays.createFloat32Array(4 * sw * sh);
double sR = 0, sG = 0, sB = 0; // source's current point r,g,b
// untested !
double sA = 0; //source alpha
for (sy = 0; sy < sh; sy++) {
GWT.log("sy: "+sy+" sh: "+sh);
ty = sy * scale; // y src position within target
tY = (long)ty; // rounded : target pixel's y // ?????
yIndex = (int)Math.floor(4 * tY * tw); // line index within target array
crossY = (tY != ( (long)(ty + scale) )); // ?????
if (crossY) { // if pixel is crossing botton target pixel
wy = (tY + 1 - ty); // weight of point within target pixel
nwy = (ty + scale - tY - 1); // ... within y+1 target pixel
}
for (sx = 0; sx < sw; sx++, sIndex += 4) {
tx = sx * scale; // x src position within target
tX = (long)tx; // rounded : target pixel's x // ?????
tIndex = (int)Math.floor(yIndex + tX * 4); // target pixel index within target array // ?????
crossX = (tX != ((int)Math.floor(tx + scale)));
if (crossX) { // if pixel is crossing target pixel's right
wx = (tX + 1 - tx); // weight of point within target pixel
nwx = (tx + scale - tX - 1); // ... within x+1 target pixel
}
sR = sBuffer.get(sIndex); // retrieving r,g,b for curr src px.
sG = sBuffer.get(sIndex + 1);
sB = sBuffer.get(sIndex + 2);
sA = sBuffer.get(sIndex + 3);
if (!crossX && !crossY) { // pixel does not cross
// just add components weighted by squared scale.
tBuffer.set(tIndex , (float)(tBuffer.get(tIndex) + sR * sqScale));
tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * sqScale));
tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * sqScale));
tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * sqScale));
} else if (crossX && !crossY) { // cross on X only
w = wx * scale;
// add weighted component for current px
tBuffer.set(tIndex , (float)(tBuffer.get(tIndex) + sR * w));
tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));
// add weighted component for next (tX+1) px
nw = nwx * scale;
tBuffer.set(tIndex + 4, (float)(tBuffer.get(tIndex + 4) + sR * nw)); // not 3
tBuffer.set(tIndex + 5, (float)(tBuffer.get(tIndex + 5) + sG * nw)); // not 4
tBuffer.set(tIndex + 6, (float)(tBuffer.get(tIndex + 6) + sB * nw)); // not 5
tBuffer.set(tIndex + 7, (float)(tBuffer.get(tIndex + 7) + sA * nw)); // not 6
} else if (crossY && !crossX) { // cross on Y only
w = wy * scale;
// add weighted component for current px
tBuffer.set(tIndex , (float)(tBuffer.get(tIndex) + sR * w));
tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));
// add weighted component for next (tY+1) px
nw = nwy * scale;
tBuffer.set((int)Math.floor(tIndex + 4 * tw) , (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw)) + sR * nw)); // *4, not 3
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 1), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 1)) + sG * nw)); // *4, not 3
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 2), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 2)) + sB * nw)); // *4, not 3
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 3), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 3)) + sA * nw)); // *4, not 3
} else { // crosses both x and y : four target points involved
// add weighted component for current px
w = wx * wy;
tBuffer.set(tIndex , (float)(tBuffer.get(tIndex) + sR * w));
tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));
// for tX + 1; tY px
nw = nwx * wy;
tBuffer.set(tIndex + 4, (float)(tBuffer.get(tIndex + 4) + sR * nw)); // same for x
tBuffer.set(tIndex + 5, (float)(tBuffer.get(tIndex + 5) + sG * nw));
tBuffer.set(tIndex + 6, (float)(tBuffer.get(tIndex + 6) + sB * nw));
tBuffer.set(tIndex + 7, (float)(tBuffer.get(tIndex + 7) + sA * nw));
// for tX ; tY + 1 px
nw = wx * nwy;
tBuffer.set((int)Math.floor(tIndex + 4 * tw) , (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw)) + sR * nw)); // same for mul
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 1), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 1)) + sG * nw));
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 2), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 2)) + sB * nw));
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 3), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 3)) + sA * nw));
// for tX + 1 ; tY +1 px
nw = nwx * nwy;
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 4), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 4)) + sR * nw)); // same for both x and y
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 5), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 5)) + sG * nw));
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 6), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 6)) + sB * nw));
tBuffer.set((int)Math.floor(tIndex + 4 * tw + 7), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 7)) + sA * nw));
}
} // end for sx
} // end for sy
// create result canvas
Canvas resCV = Canvas.createIfSupported();
resCV.getCanvasElement().setWidth((int)Math.floor(tw));
resCV.getCanvasElement().setHeight((int)Math.floor(th));
Context2d resCtx = resCV.getContext2d();
ImageData imgRes = resCtx.getImageData(0, 0, tw, th);
CanvasPixelArray tByteBuffer = imgRes.getData();
// convert float32 array into a UInt8Clamped Array
int pxIndex = 0;
for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 4, tIndex += 4, pxIndex++) {
tByteBuffer.set(tIndex, (int)Math.ceil(tBuffer.get(sIndex)));
tByteBuffer.set(tIndex + 1, (int)Math.ceil(tBuffer.get(sIndex + 1)));
tByteBuffer.set(tIndex + 2, (int)Math.ceil(tBuffer.get(sIndex + 2)));
tByteBuffer.set(tIndex + 3, (int)Math.ceil(tBuffer.get(sIndex + 3)));
}
// writing result to canvas.
resCtx.putImageData(imgRes, 0, 0);
return resCV.getCanvasElement();
}
I am not sure how to write an expression like thit tY = 0 | ty in Java. I am also not sure if canvas.width(value); transforms into canvas.setCoordinateSpaceWidth(value);
Edit:
Is (0 | ty + scale) in JS is ((long)ty + scale) in Java?
What is the translation of
var sBuffer = cv.getContext('2d').getImageData(0, 0, sw, sh).data
is it CanvasPixelArray
CanvasPixelArray sBuffer = cv.getContext2d().getImageData(0, 0, sw, sh).getData()
Can someone help me with the translation?
Edit: It turns out that my Java code is very slow. I cannot run it in dev mode. In prod mode it is event slower than the JavaScript code. Why is this code slower?
0 | tY is rounding a JS Number (equivalent to a Java double) to an integer value; equivalent to Math.floor(tY) or (long) tY (int is too small for all integer values that can be expressed in a double, but might be OK in this specific case).
canvas.width maps to Canvas#getCoordinateSpaceWidth, as you can see in GWT sources.
Finally, new Float32Array(n) maps to TypedArrays.createFloat32Array.