Issue
I'm trying to create a 3D carousel in Android (Java) but I can't quite get it to work. This is the kind of carousel I'm aiming for:
I've tried several long and complicated ways of doing this, including drawing paths by hand that use a ton of math, but it's never quite right.
The idea that feels closest is to draw a carousel of images in the x-y plane by calculating their positions on a circle, and then "flipping" that plane so the carousel is now in the x-z direction. That seems to almost work (though the corners don't quite touch), but the squares are lying flat and I need them to stand up on their outermost edge. I can't figure out how to do that final rotation though in a way that doesn't undo the previous rotations by the camera.
Here is what my result looks like visually currently:
Am I going about this complete the wrong way? Am I close but need to just change a couple of things? I'm honestly not sure. Please help me figure out how to do this better!
Here is my drawable code:
public class CarouselCard extends Drawable {
Paint paintBackground = null;
/* CONSTRUCTOR */
public CarouselCard() {
super();
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
if (paintBackground == null) { // Just starting
paintBackground = new Paint();
paintBackground.setStyle(Paint.Style.FILL);
paintBackground.setColor(Color.BLUE);
}
int HALF_CHORD_WIDTH = 150;
float RADIUS = 200;
float INTERIOR_ANGLE = 30;
INTERIOR_ANGLE %= 360;
int numObjects = (int) (360 / INTERIOR_ANGLE);
for (int i = 0; i < numObjects; i++) {
float theta = INTERIOR_ANGLE * i;
double rad = Math.toRadians(theta);
float circleX = (float) (RADIUS * Math.sin(rad));
float circleY = (float) (RADIUS * Math.cos(rad));
canvas.save();
canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);
Camera camera = new Camera();
camera.save();
camera.rotateX(80); // Approximates 90 but keeps it visible since flat
camera.rotateZ(theta);
camera.applyToCanvas(canvas);
camera.restore();
if (i % 4 == 0) paintBackground.setColor(Color.YELLOW);
if (i % 4 == 1) paintBackground.setColor(Color.GREEN);
if (i % 4 == 2) paintBackground.setColor(Color.BLUE);
if (i % 4 == 3) paintBackground.setColor(Color.GRAY);
paintBackground.setAlpha(128);
canvas.drawRect(circleX-HALF_CHORD_WIDTH, circleY-HALF_CHORD_WIDTH,
circleX+HALF_CHORD_WIDTH, circleY+HALF_CHORD_WIDTH, paintBackground);
canvas.restore();
}
}
@Override
public void setAlpha(int alpha) {}
@Override
public void setColorFilter(ColorFilter colorFilter) {}
@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}
Alternatively, I can get everything facing the right way and oriented correctly, but only if they're all co-located at the center of the carousel. I'm not sure how to shift them appropriately while still maintaining the transformations that make this picture possible:
This image was produced by adjusting the above for-loop like so:
for (int i = 0; i < numObjects; i++) {
if (i % 4 == 0) paintBackground.setColor(Color.YELLOW);
if (i % 4 == 1) paintBackground.setColor(Color.GREEN);
if (i % 4 == 2) paintBackground.setColor(Color.BLUE);
if (i % 4 == 3) paintBackground.setColor(Color.GRAY);
paintBackground.setAlpha(128);
float theta = INTERIOR_ANGLE * i;
canvas.save();
canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);
Camera camera = new Camera();
camera.save();
camera.rotateY(theta);
camera.applyToCanvas(canvas);
camera.restore();
canvas.drawRect(
-HALF_CHORD_WIDTH,
-HALF_CHORD_WIDTH,
HALF_CHORD_WIDTH,
HALF_CHORD_WIDTH,
paintBackground);
canvas.restore();
}
Solution
I was able to create the following carousel:
The code for it is:
@Override
public void draw(Canvas canvas) {
if (paintBackground == null) { // Just starting
paintBackground = new Paint();
paintBackground.setStyle(Paint.Style.FILL);
paintBackground.setColor(Color.BLUE);
}
// ######## CRUCIAL - START - Modify constants with care!
// Decreasing chord denominator makes carousel bigger, while increasing it
// makes front side contain more squares (but gets smaller)
float HALF_CHORD_WIDTH = canvas.getWidth()/31f; // Width of each square
float RADIUS = HALF_CHORD_WIDTH; // Radius of carousel
float CAMERA_MULT = HALF_CHORD_WIDTH/10f;
// ######## CRUCIAL - END
float INTERIOR_ANGLE = 30;
INTERIOR_ANGLE %= 360;
int numObjects = (int) (360 / INTERIOR_ANGLE);
for (int i = 0; i < numObjects; i++) {
if (i % 4 == 0) paintBackground.setColor(Color.YELLOW);
if (i % 4 == 1) paintBackground.setColor(Color.GREEN);
if (i % 4 == 2) paintBackground.setColor(Color.BLUE);
if (i % 4 == 3) paintBackground.setColor(Color.GRAY);
paintBackground.setAlpha(128);
float theta = INTERIOR_ANGLE * i;
double rad = Math.toRadians(theta);
float circleX = (float) (RADIUS * Math.sin(rad));
float circleZ = (float) (RADIUS * Math.cos(rad));
canvas.save();
canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);
Camera camera = new Camera();
camera.save();
camera.translate(circleX*CAMERA_MULT, 0, -circleZ*CAMERA_MULT);
camera.rotateY(theta);
camera.applyToCanvas(canvas);
camera.restore();
canvas.drawRect(
-HALF_CHORD_WIDTH,
-HALF_CHORD_WIDTH,
HALF_CHORD_WIDTH,
HALF_CHORD_WIDTH,
paintBackground);
canvas.restore();
}
}
Answered By - Elliptica
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.