Why is a Java function so much slower than a JSNI function? - java
I compared the performance of two functions doing downscaling on a canvas image. It turns out that the Java function is so much slower than the JSNI function. That is strange because I would assume that the GWT compiler optimizes the code that it become at least as fast as the JSNI code.
Here is the Java function:
public static final CanvasElement scaleCanvas(CanvasElement cv, double scale) {
if (!(scale < 1) || !(scale > 0)) {
GWT.log("scale must be a positive number <1");
// throw new Exception("scale must be a positive number <1 ");
}
GWT.log("scaleCanvas start");
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();
}
Here is the equivalent JSNI function:
public static final native CanvasElement resizeCanvas(CanvasElement cv, double 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;
}-*/;
Why is the Java function so much slower than the JSNI function?
Did you GWT-compiled and run the compiled version of your application in the "java scenario" ?
Because, in case you didn't and just ran from Eclipse Run->Web Application. GWT will convert the java code in javascript at runtime which is a lot slower than the compiled version.
Related
How to make the ends of Cylinder transparent in JavaFX
I'm currently trying to make the ends of a Cylinder completely transparent whilst keeping the sides a Material. I'm unsure how to achieve this. This thread mentions it but all the links are broken I think I need to use a clipping plane? Although I don't know where to start with that. Here's what I'm currently using to just simply set a translucent material! Cylinder line = new Cylinder(getRadius()/4, height); lineMaterial = new PhongMaterial(); lineMaterial.setDiffuseColor(new Color(1,1,1,0.5)); lineMaterial.diffuseMapProperty(); line.setMaterial(lineMaterial);
One possible way to get transparency is by using a png as the diffuse image, with some transparent pixels. While this works, if you apply over a built-in JavaFX Cylinder, you won't get the expected result, because the Cylinder applies the same image to both the vertical tube and the two cap faces. So this won't work for your case. There could be an option to remove the caps from the cylinder mesh and get just a tube, but unfortunately it doesn't export its triangle mesh. So far, the best option is to create directly the mesh of a tube, and for that we can reuse the mesh of the Cylinder by checking the open source code at the (new) OpenJDK/JFX GitHub repository. The following method creates a TriangleMesh of a Tube of height h, radius r, with div divisions (producing 2 * div triangles). It is the exact same code as the one in Cylinder, but without the caps points, texture coordinate and faces arrays. private static TriangleMesh createMesh(int div, float h, float r) { final int nPoints = div * 2; final int tcCount = (div + 1) * 2; final int faceCount = div * 2; float textureDelta = 1.f / 256; float dA = 1.f / div; h *= .5f; float points[] = new float[nPoints * 3]; float tPoints[] = new float[tcCount * 2]; int faces[] = new int[faceCount * 6]; int smoothing[] = new int[faceCount]; int pPos = 0, tPos = 0; for (int i = 0; i < div; ++i) { double a = dA * i * 2 * Math.PI; points[pPos + 0] = (float) (Math.sin(a) * r); points[pPos + 1] = h; points[pPos + 2] = (float) (Math.cos(a) * r); tPoints[tPos + 0] = 1 - dA * i; tPoints[tPos + 1] = 1 - textureDelta; pPos += 3; tPos += 2; } // top edge tPoints[tPos + 0] = 0; tPoints[tPos + 1] = 1 - textureDelta; tPos += 2; for (int i = 0; i < div; ++i) { double a = dA * i * 2 * Math.PI; points[pPos + 0] = (float) (Math.sin(a) * r); points[pPos + 1] = -h; points[pPos + 2] = (float) (Math.cos(a) * r); tPoints[tPos + 0] = 1 - dA * i; tPoints[tPos + 1] = textureDelta; pPos += 3; tPos += 2; } // bottom edge tPoints[tPos + 0] = 0; tPoints[tPos + 1] = textureDelta; tPos += 2; int fIndex = 0; // build body faces for (int p0 = 0; p0 < div; ++p0) { int p1 = p0 + 1; int p2 = p0 + div; int p3 = p1 + div; // add p0, p1, p2 faces[fIndex+0] = p0; faces[fIndex+1] = p0; faces[fIndex+2] = p2; faces[fIndex+3] = p2 + 1; faces[fIndex+4] = p1 == div ? 0 : p1; faces[fIndex+5] = p1; fIndex += 6; // add p3, p2, p1 faces[fIndex+0] = p3 % div == 0 ? p3 - div : p3; faces[fIndex+1] = p3 + 1; faces[fIndex+2] = p1 == div ? 0 : p1; faces[fIndex+3] = p1; faces[fIndex+4] = p2; faces[fIndex+5] = p2 + 1; fIndex += 6; } for (int i = 0; i < div * 2; ++i) { smoothing[i] = 1; } TriangleMesh m = new TriangleMesh(); m.getPoints().setAll(points); m.getTexCoords().setAll(tPoints); m.getFaces().setAll(faces); m.getFaceSmoothingGroups().setAll(smoothing); return m; } Now you will need to create a MeshView so you can add it to your scene: private static MeshView createTube(int div, float h, float r) { MeshView meshView = new MeshView(createMesh(div, h, r)); // meshView.setDrawMode(DrawMode.LINE); meshView.setCullFace(CullFace.NONE); PhongMaterial material = new PhongMaterial(Color.RED); meshView.setMaterial(material); return meshView; } Create and add one to your scene: MeshView tube = createTube(64, 5f, 1.6f); Scene scene = new Scene(new Group(tube), 600, 600, true, SceneAntialiasing.BALANCED); And you will get your tube: You can also apply a diffuse image as texture: material.setDiffuseMap(new Image(getClass.getResourceAsStream("440px-JavaFX_Logo.png")));
Resizing array in Java throws OutOfMemoryError
I have written a method to resize an array in Java: public HUD resize(float factor){ int[] array = new int[(int)Math.pow(pixels.length * factor, 2)];//Create new array for(int y = 0; y < height; ++y){ for(int x = 0; x < width; ++x){ array[(int)((x * 2) + (y * 2) * width * factor)] = pixels[x + y * width]; array[(int)((x * 2 + 1) + (y * 2) * width * factor)] = pixels[x + y * width]; array[(int)((x * 2) + (y * 2 + 1) * width * factor)] = pixels[x + y * width]; array[(int)((x * 2 + 1) + (y * 2 + 1) * width * factor)] = pixels[x + y * width]; } } pixels = array; width = (int)(width * factor); height = (int)(height * factor); return this; } This method is only called once and an OutOfMemoryError is given out whenever its called
Increasing the heap size of your application may prevent the OutOfMemoryError. This post on SO explains how to do this.
How can I add this float[] vertexData to a Vector3f list?
How I can add vertex data (float[]) to a Vector3f list? It gives me an error if I try. float[] vertexData = new float[ allindices2.length * vertexDataSize / 3]; for (int i = 0; i < vertexData.length / vertexDataSize; i++){ vertexData[i * vertexDataSize + 0] = Float.parseFloat(allindices2 [Integer.parseInt(allindices2 [i * source.size() + 0]) * 3 + 0]); vertexData[i * vertexDataSize + 1] = Float.parseFloat(allpositions2[Integer.parseInt(allindices2[i * source.size() + 0]) * 3 + 1]); vertexData[i * vertexDataSize + 2] = Float.parseFloat(allpositions2[Integer.parseInt(allindices2[i * source.size() + 0]) * 3 + 2]); vertices.add(vertexData); }
If you don't need the vertexData array for any other reason, you should not create it at all. Instead, you can directly create the required Vector3f instances. for (int i = 0; i < vertexData.length / vertexDataSize; i++){ float x = Float.parseFloat(allindices2 [Integer.parseInt(allindices2[i * source.size() + 0]) * 3 + 0]); float y = Float.parseFloat(allpositions2[Integer.parseInt(allindices2[i * source.size() + 0]) * 3 + 1]); float z = Float.parseFloat(allpositions2[Integer.parseInt(allindices2[i * source.size() + 0]) * 3 + 2]); vertices.add(new Vector3f(x,y,z)); } Nevertheless, all these parse... calls and the general structure look highly dubious. Unless you obtain this data directly from a file or so, you should consider a different data model. Additionally: Are you sure that the first allindices2 should not be allpositions2? There's no need to do work twice. You can pull out the computation of the index. Most likely, the code could also be written as for (int i = 0; i < vertexData.length / vertexDataSize; i++){ int index = Integer.parseInt(allindices2[i * source.size()]); float x = Float.parseFloat(allpositions2[index * 3 + 0]); float y = Float.parseFloat(allpositions2[index * 3 + 1]); float z = Float.parseFloat(allpositions2[index * 3 + 2]); vertices.add(new Vector3f(x,y,z)); }
How to draw the middle half of a sphere (in code)
I'm trying to create the middle half of a sphere. Basically to create a sphere, stack numbers and slice numbers are given, and there are two variables phi (for slices) and theta (for stacks) responsible for how much to progress. And the process is divided into creating bottom cap, body, and top cap (as seen below). To achieve middle half (theta of middle 50% as below), we need to omit the caps, and somehow modify the body. I was playing around with stack numbers (1/4*stackNumbers to 3/4*stackNumbers) but didn't give the result I wanted. How should I modify the sphere generation to achieve the middle half (pi/4 <theta <pi*3/4)? My overall problem is how can I split the sphere into 3 different parts upper 25%, middle 50%, and bottom 25%? (25% in terms of angle, i.e. theta) Here is the popular code for generating a sphere programmatically: 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; } } }
The code here is using spherical coordinates to calculate the sphere. theta is the variable that represents the up/down coordinate that you're interested in, and theta goes from 0 to PI. You want to go from PI/4 to 3PI/4. stackNumbers simply represent the number of divisions in the sphere, since you can see that it is used as a denominator for stack, which is the wrong variable to change. So you can make the following changes to the code instead. From: double theta0 = stackPercentage0 * Math.PI; double theta1 = stackPercentage1 * Math.PI; to: double startTheta = Math.PI / 4; double endTheta = 3 * Math.PI / 4; double theta0 = stackPercentage0 * (endTheta - startTheta) + startTheta; double theta1 = stackPercentage1 * (endTheta - startTheta) + startTheta; And since you aren't using the caps you need to change the start and end stack numbers to reflect that: for (int stack = 1; stack < stackNumber - 1; stack++) { // old for (int stack = 0; stack < stackNumber; stack++) { // new Also, you because you have more body faces now, you need to update the appropriate container for them. Replace (stackNumber - 2) with (stackNumber - 1).
TouchImageView tap location
I'm using TouchImageView from https://github.com/MikeOrtiz/TouchImageView and I'm trying to use highlights. For example, when the user taps in a certain point of the image (doesn't matter if the image is zoomed in or not), I would trigger an event - show the description of the highlight in a webview. It works but only when all the image fits in the view, when I zoom in it all bugs. If (scaledRight < 0) it seems to work reasonably well, but when it falls on the else it doesn't work. Any help will be really appreciated, and if it works I can send it to the creator :) Thanks in advance. public boolean onTouch(View v, MotionEvent event) { mScaleDetector.onTouchEvent(event); matrix.getValues(m); float x = m[Matrix.MTRANS_X]; float y = m[Matrix.MTRANS_Y]; PointF curr = new PointF(event.getX(), event.getY()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: last.set(event.getX(), event.getY()); if(!isFullScreen){ //System.out.println("Right is " + right); //System.out.println("Bottom is " + bottom); //System.out.println("Start x is " + start.x); //System.out.println("Start y is " + start.y); //float scaledRight = 2 * (right); float scaledRight = 2 * (right/saveScale); float scaledBottom = 2 * (bottom/saveScale); //float scaledBottom = 2 * (bottom); float tapPositionX = 0; float tapPositionY = 0; double xMaxBound = 0; double xMinBound = 0; double yMaxBound = 0; double yMinBound = 0; int radius = 0; System.out.println("Right is " + right); System.out.println("Bottom is " + bottom); /* if(theDeltaX < 0) { theDeltaX = -theDeltaX; } theDeltaX = theDeltaX / saveScale; if(theDeltaY < 0) { theDeltaY = -theDeltaY; } theDeltaY = theDeltaY / saveScale; */ for(int i = 0; i < XMLParser.hlPicIDArray.size(); i++) { if(imageID.equalsIgnoreCase(XMLParser.hlPicIDArray.get(i))) { radius = Integer.parseInt(XMLParser.hlRadiusArray.get(i)); xMaxBound = Integer.parseInt(XMLParser.hlOrXArray.get(i)); xMinBound = xMaxBound; yMaxBound = Integer.parseInt(XMLParser.hlOrYArray.get(i)); yMinBound = yMaxBound; xMaxBound = xMaxBound + (radius * Math.cos(0)); xMinBound = xMinBound + (radius * Math.cos(3.14)); yMaxBound = yMaxBound + (radius * Math.sin(1.57)); yMinBound = yMinBound + (radius * Math.sin(4.71)); if(scaledRight <= 0) { //tapPositionX = (bmWidth * 2) - (scaledRight) + (80/saveScale); tapPositionX = bmWidth - (scaledRight) + (80/saveScale); tapPositionX = (curr.x/width) * (tapPositionX); tapPositionX += (right);// - 80; xMinBound = xMinBound - (0.12 * xMinBound); //Compensation for imprecision //xMaxBound = xMaxBound - (0.08 * (maxScale - saveScale) * xMinBound); //Highlight point is added to right to compensate the white margins } else { //? //tapPositionX = (start.x/width) * right * 2; //(+ offset) tapPositionX = (start.x/width) * right; //(+ offset) //tapPositionX = tapPositionX + ((theDeltaX/width) * bmWidth * 2); tapPositionX = tapPositionX + (last.x/width) * (bmWidth - right); // * 2 //tapPositionX -= theDeltaX; } if(scaledBottom <= 0) { //tapPositionY = (bmHeight * 2) - (scaledBottom) + (80/saveScale); tapPositionY = bmHeight - (scaledBottom) + (80/saveScale); tapPositionY = (last.y/height) * tapPositionY; tapPositionY += (bottom/saveScale);// - 80; yMinBound = yMinBound - (0.12 * yMinBound); //Compensation for imprecision //yMaxBound = yMaxBound - (0.08 * (maxScale - saveScale) * yMinBound); } else { //tapPositionY = (start.y/height) * bottom * 2; tapPositionY = (start.y/height) * bottom; tapPositionY = tapPositionY + (last.y/height) * (bmHeight - bottom); //tapPositionY -= theDeltaY; } System.out.println("X tapped is " + tapPositionX); System.out.println("Y tapped is " + tapPositionY); System.out.println("Delta X is " + theDeltaX); System.out.println("Delta Y is " + theDeltaY); if((tapPositionX <= xMaxBound) && (tapPositionX >= xMinBound) && (tapPositionY <= yMaxBound) && (tapPositionY >= yMinBound)) { System.out.println("HL Area tapped!");