I’m making a mod for Minecraft 1.12. In it I have a tile entity that holds a list of block positions. I want to render a box in the world at each position specified in this list, a bit like how structure blocks display air blocks.
I managed to render the boxes by copying relevent code in TileEntityStructureRenderer but whenever the block associated to the tile entity exits the screen, all boxes stop rendering. I saw on some forums and in TileEntityStructureRenderer’s code that I should override isGlobalRenderer() method and make it return true. So I did, but the problem still persists and I have absolutely no clue as why. I looked up Forge’s docs but there isn’t any information on this that I could find.
Did I miss something? Maybe I’m registering the renderer the wrong way?
Here’s how I register my tile entity and its renderer in my mod’s class (I removed irrelevent code):
#Mod(modid = NaissanceE.MODID, name = NaissanceE.NAME, version = NaissanceE.VERSION)
public class NaissanceE {
public static final String MODID = "naissancee";
public static final String NAME = "NaissanceE";
public static final String VERSION = "1.0";
#Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
if (event.getSide() == Side.CLIENT) {
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityLightOrbController.class, new TileEntityLightOrbControllerRenderer());
}
}
#Mod.EventBusSubscriber
static class EventsHandler {
#SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
GameRegistry.registerTileEntity(TileEntityLightOrbController.class, new ResourceLocation(MODID, "light_orb_controller"));
}
}
and the code for the renderer (imports removed for clarity):
#SideOnly(Side.CLIENT)
public class TileEntityLightOrbControllerRenderer extends TileEntitySpecialRenderer<TileEntityLightOrbController> {
#Override
public void render(TileEntityLightOrbController te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) {
EntityPlayer player = Minecraft.getMinecraft().player;
if ((player.canUseCommandBlock() || player.isSpectator())
&& (player.getHeldItemMainhand().getItem() == ModItems.LIGHT_ORB_TWEAKER
|| player.getHeldItemOffhand().getItem() == ModItems.LIGHT_ORB_TWEAKER)) {
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferbuilder = tessellator.getBuffer();
GlStateManager.disableFog();
GlStateManager.disableLighting();
GlStateManager.disableTexture2D();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
this.setLightmapDisabled(true);
List<PathCheckpoint> checkpoints = te.getCheckpoints();
for (int i = 0, size = checkpoints.size(); i < size; i++) {
PathCheckpoint checkpoint = checkpoints.get(i);
this.renderCheckpoint(te, x, y, z, checkpoint, tessellator, bufferbuilder);
}
this.setLightmapDisabled(false);
GlStateManager.glLineWidth(1F);
GlStateManager.enableLighting();
GlStateManager.enableTexture2D();
GlStateManager.enableDepth();
GlStateManager.depthMask(true);
GlStateManager.enableFog();
}
}
private void renderCheckpoint(TileEntityLightOrbController te, double x, double y, double z, PathCheckpoint checkpoint, Tessellator tessellator, BufferBuilder bufferBuilder) {
GlStateManager.glLineWidth(3f);
bufferBuilder.begin(3, DefaultVertexFormats.POSITION_COLOR);
BlockPos tePos = te.getPos();
BlockPos checkpointPos = checkpoint.getPos();
final double size = 0.25;
double start = 0.5 - size;
double end = 0.5 + size;
double x1 = checkpointPos.getX() - tePos.getX() + start + x;
double y1 = checkpointPos.getY() - tePos.getY() + start + y;
double z1 = checkpointPos.getZ() - tePos.getZ() + start + z;
double x2 = checkpointPos.getX() - tePos.getX() + end + x;
double y2 = checkpointPos.getY() - tePos.getY() + end + y;
double z2 = checkpointPos.getZ() - tePos.getZ() + end + z;
int r = 0, g = 0, b = 0;
if (checkpoint.isStop()) {
r = 1;
} else {
g = 1;
}
RenderGlobal.drawBoundingBox(bufferBuilder, x1, y1, z1, x2, y2, z2, r, g, b, 1);
tessellator.draw();
}
// FIXME ignored
#Override
public boolean isGlobalRenderer(TileEntityLightOrbController te) {
return true;
}
}
The full code is available here.
So, I just found out that for some reason it seems to not work when I run the game through Intellij but works fine when launching the game through the official laucher…
Related
I have a simple class 2Dpoints with two fields, x and y. I want to write a code so that I could command one point to moves slowly to another point, like so that it moves on the vector line of their distances. But I don't know how?
I've first thought that it should contain a for loop so that it would know, it should move till it reaches the other point
something like for(int d=0 ; d<distance ; d++) but I don't know how should I then command it so that it would move on the line?
import java.lang.Math.*;
public class Punkt {
private int x;
private int y;
public Punkt(int x, int y) {
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int distance) {
x = x + distance;
}
public void setY(int distance) {
y = y + distance;
}
public void moveAbout(int dx, int dy) {
x = x + dx;
y = y + dy;
}
/// method for calculating the distance to another point
public double giveDistance(Punkt otherPoint) {
return Math.sqrt(
(otherPoint.getY() - y) *
(otherPoint.getY() - y) +
(otherPoint.getX() - x) *
(otherPoint.getX() - x));
}
}
I've commented the major lines:
import static java.lang.Math.*;
/**
* Immutable structure. Functional way
*/
class Point {
public final double x;
public final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
/**
* Here you are. This is what you want to implement.
* from.moveTo(0.0, to) => from
* from.moveTo(1.0, to) => to
*
* #param by - from 0.0 to 1.0 (from 0% to 100%)
* #param target - move toward target by delta
*/
public Point moveTo(double by, Point target) {
Point delta = target.sub(this);
return add(delta.dot(by));
}
public Point add(Point point) {
return new Point(x + point.x, y + point.y);
}
public Point sub(Point point) {
return new Point(x - point.x, y - point.y);
}
public Point dot(double v) {
return new Point(v * x, v * y);
}
public double dist(Point point) {
return sub(point).len();
}
public double len() {
return sqrt(x * x + y * y);
}
public String toString() {
return x + ":" + y;
}
}
class Main {
public static void main(String[] args) {
Point source = new Point(2, 3);
Point target = new Point(-4, 9);
// You can utilize the cycle or implement kind of timer to animate something
for (int t = 0; t <= 100; t++) {
System.out.println(source.moveTo(0.01 * t, target));
}
}
}
https://replit.com/join/sucvdhpqoa-redneckz
#AlexanderAlexandrov I've change the type of my variables to double accordingly, now in one of my classes I have a method givePoints, which uses Scanner for asking a user how many points he wants and what are the coordinates then it saves them into an array of points with first element being always(0,0).
Another method takes an array of points as parameter and sort them in order of their distances to point(0,0).
These methods work perfectly. The problem is with method hitThepoints.
Here I want to first create the array of points, sort them, and then command my robot to hit all the points. robot is an object of class Robot extends circle, with position of type Point, that at first is at point(0,0)
public void hitThePoints(){
Point[] poi=sortPoints (givePoints()); //Creates a sorted array of points
Point short=new Point(poi[1].getX(),poi[1].getY());
System.out.println(" the nearest point is :");
System.out.println("("+short.getX()+ ","+short.getY()+")");
for(int i=1; i<poi.length;i++){
Point source=robot.position;
Point target=new Point(poi[i].getX(), poi[i].getY());
while(source.getX()!=target.getX() &&
source.getY()!=target.getY()){
robot.bewegeUm((source.moveTo(0.01,target)).getX(),
(source.moveTo(0.01,target)).getY());
if(source.getX()!=target.getX() &&
source.getY()!=target.getY()){break;}
System.out.println(source.getX() +","+ source.getY());
}
}
}
I created a simple Application which is meant to display, in 2D, dots which are hypothetically on a 3D plane (these points are of type Vector, written below). The class uses the XYZ Coordinates of the Camera and the XYZ Coordinates of the Vector, and uses that information to quickly translate the Vector XYZ Coordinates into XY Coordinates.
The only class needed in this question is the Vector class given below. All other classes used are omitted because they essentially fire mouse movements and redraw the frame.
--My concern is that, as the camera moves, the Vector points jump around as if the formula that I'm using is totally unreliable. Are these formulas (found under Perspective Projection) completely incorrect? The use of those formulas can be found within my set2D method. Am I doing something completely wrong, skipping steps, or perhaps have I translated the formula into code incorrectly?
Thanks!
import java.awt.Color;
import java.awt.Graphics;
public class Vector
{
private int cX, cY, cZ; //Camera Coordinates
private int aX, aY, aZ; //Object Coordinates
private double bX, bY; //3D to 2D Plane Coordinates
public Vector(int aX, int aY, int aZ, int cX, int cY, int cZ)
{
this.aX = aX;
this.aY = aY;
this.aZ = aZ;
this.cX = cX;
this.cY = cY;
this.cY = cZ;
set2D();
}
//SETS
public void setCameraX(int cX)
{
this.cX = cX;
set2D();
}
public void setCameraY(int cY)
{
this.cY = cY;
set2D();
}
public void setCameraZ(int cZ)
{
this.cZ = cZ;
set2D();
}
public void setCameraXYZ(int cX, int cY, int cZ)
{
setCameraX(cX);
setCameraY(cY);
setCameraZ(cZ);
}
public void setObjX(int x)
{
this.aX = x;
}
public void setObjY(int y)
{
this.aY = y;
}
public void setObjZ(int z)
{
this.aZ = z;
}
public void setObjXYZ(int x, int y, int z)
{
this.aX = x;
this.aY = y;
this.aZ = z;
}
public void set2D()
{
//---
//the viewer's position relative to the display surface which goes through point C representing the camera.
double eX = aX - cX;
double eY = aY - cY;
double eZ = aZ - cZ;
//----
double cosX = Math.cos(eX);
double cosY = Math.cos(eY);
double cosZ = Math.cos(eZ);
double sinX = Math.sin(eX);
double sinY = Math.sin(eY);
double sinZ = Math.sin(eZ);
//---
//The position of point A with respect to a coordinate system defined by the camera, with origin in C and rotated by Theta with respect to the initial coordinate system.
double dX = ((cosY*sinZ*eY) + (cosY*cosZ*eX)) - (sinY * eZ);
double dY = ((sinX*cosY*eZ) + (sinX*sinY*sinZ*eY) + (sinX*sinY*cosZ*eX)) + ((cosX*cosZ*eY) - (cosX*sinZ*eX));
double dZ = ((cosX*cosY*eZ) + (cosX*sinY*sinZ*eY) + (cosX*sinY*cosZ*eX)) - ((-sinX*cosZ*eY) - (-sinX*sinZ*eX));
//---
//---
//The 2D projection coordinates of the 3D object
bX = (int)(((eZ / dZ) * dX) - eX);
bY = (int)(((eZ / dZ) * dY) - eY);
//---
System.out.println(bX + " " + bY);
}
//GETS
public int getCameraX()
{
return cX;
}
public int getCameraY()
{
return cY;
}
public int getCameraZ()
{
return cZ;
}
public int getObjX()
{
return aX;
}
public int getObjY()
{
return aY;
}
public int getObjZ()
{
return aY;
}
public int get2DX()
{
return (int)bX;
}
public int get2DY()
{
return (int)bY;
}
//DRAW
public void draw(Graphics g)
{
g.setColor(Color.red);
g.fillOval((int)bX, (int)bY, 3, 3);
}
//TO STRING
public String toString()
{
return (aX + " " + aY + " " + aZ);
}
}
The following line of your code does not match with the formula you are using:
double dZ = ((cosX*cosY*eZ) + (cosX*sinY*sinZ*eY) + (cosX*sinY*cosZ*eX)) - ((-sinX*cosZ*eY) - (-sinX*sinZ*eX));
Notice the part - ((-sinX*cosZ*eY) - (-sinX*sinZ*eX))
This should be - ((sinX*cosZ*eY) - (sinX*sinZ*eX))
Since if you take sinX and multiply it, the -ve sign stays outside. If however you multiple -sinX then the sign outside the brackets should become +ve.
here is my code:
public class Rectangles
{
private final double x;
private final double y;
private final double width;
private final double height;
public Rectangles(double x0, double y0, double w, double h)
{
x = x0;
y = y0;
width = w;
height = h;
}
public double area()
{
return width * height;
}
public double perimeter()
{
return 2*width + 2*height;
}
public boolean intersects(Rectangles b)
{
boolean leftof = ((b.x + b.width)<(x-width));
boolean rightof = ((b.x-b.width)>(x+width));
boolean above = ((b.y-b.height)>(y+height));
boolean below = ((b.y+b.height)<(y-height));
if (leftof==false && rightof==false && above==false && below==false)
return false;
else return true;
}
public void show()
{
StdDraw.setYscale((0),(y+height));
StdDraw.setXscale((0), (x+width));
StdDraw.setPenColor();
StdDraw.rectangle(x,y,.5*width,.5*height);
}
public static void main(String[] args)
{
Rectangles a = new Rectangles(Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Double.parseDouble(args[2]),
Double.parseDouble(args[3]));
Rectangles b = new Rectangles(0,0,1,1);
System.out.println(a.area());
System.out.println(a.perimeter());
System.out.println(a.intersects(b));
a.show();
b.show();
}
}
I am new to this. This is from a lab assignment based on creating data types. Everything is going well except that System.out.println(a.intersects(b)) is returning true for rectangles that definitely should not intersect. Worse still, the drawing created by show() is showing that they intersect when they definitely should not. For example, (and tell me if I'm completely wrong) %java Rectangles 5 5 3 6 should definitely not return true, right? because a rectangle centered at 5,5 whose width is three would definitely not intersect with a rectangle centered at 0,0 whose width is one.
help is appreciated. I would post a pic of the image displayed, but it says I have to have more reputation to post images. oh well. It was intersecting rectangles.
based on some comments, I edited my code and it now looks like this:
public class Rectangles
{
private final double x;
private final double y;
private final double width;
private final double height;
public Rectangles(double x0, double y0, double w, double h)
{
x = x0;
y = y0;
width = w;
height = h;
}
public double area()
{
return width * height;
}
public double perimeter()
{
return 2*width + 2*height;
}
public boolean intersects(Rectangles b)
{
boolean intersects = ((b.width / 2) + (width / 2) < Math.abs(b.x - x) &&
(b.height / 2) + (height / 2) < Math.abs(b.y - y));
if (intersects==false)
return false;
else return true;
}
public void show()
{
StdDraw.setYscale((0),(y+height));
StdDraw.setXscale((0), (x+width));
StdDraw.setPenColor();
StdDraw.rectangle(x,y,.5*width,.5*height);
}
public static void main(String[] args)
{
Rectangles a = new Rectangles(Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Double.parseDouble(args[2]),
Double.parseDouble(args[3]));
Rectangles b = new Rectangles(1.0,1.0,1.0,1.0);
System.out.println(a.area());
System.out.println(a.perimeter());
System.out.println(b.intersects(a));
a.show();
b.show();
}
}
I am still getting funky answers for intersects, and for some reason my drawings always have intersecting rectangles. I don't know what I'm doing wrong. After changing code I tried %java Rectangles 5 5 3 6 and it said they intersect and also drew an image of intersecting rectangles. What is going on?
I fixed it.
public class Rectangles
{
private final double x;
private final double y;
private final double width;
private final double height;
public Rectangles(double x0, double y0, double w, double h)
{
x = x0;
y = y0;
width = w;
height = h;
}
public double area()
{
return width * height;
}
public double perimeter()
{
return 2*width + 2*height;
}
public boolean intersects(Rectangles b)
{
boolean leftof = ((b.x + (0.5*b.width))<(x-(0.5*width)));
boolean rightof = ((b.x-(0.5*b.width))>(x+(0.5*width)));
boolean above = ((b.y-(0.5*b.height))>(y+(0.5*height)));
boolean below = ((b.y+(0.5*b.height))<(y-(0.5*height)));
if (leftof==true || rightof==true || above==true || below==true)
return false;
else return true;
}
public void show()
{
double j = Math.max((x+(0.5*height)), (y+(0.5*height)));
StdDraw.setYscale((0),j+1);
StdDraw.setXscale((0),j+1);
StdDraw.setPenColor();
StdDraw.rectangle(x,y,.5*width,.5*height);
}
public static void main(String[] args)
{
Rectangles a = new Rectangles(Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Double.parseDouble(args[2]),
Double.parseDouble(args[3]));
Rectangles b = new Rectangles(2,2,2,2);
System.out.println(a.area());
System.out.println(a.perimeter());
System.out.println(a.intersects(b));
a.show();
}
}
There is an error in formula for intersection, try this one
((x < b.x && (x + width) > b.x) || (x > b.x && x < (b.x + b.width))) &&
((y < b.y && (y + height) > b.y) || (y > b.y && y < (b.y + b.height)))
If we think geometrically,
(b.width / 2) + (width / 2) < abs(b.x - x) &&
(b.height / 2) + (height / 2) < abs(b.y - y)
should be enough and easier to understand.
I am in pursuit of a lightning fast java method to check if a point is inside a triangle.
I found the following c++ code in a paper from Kasper Fauerby:
typedef unsigned int uint32;
#define in(a) ((uint32&) a)
bool checkPointInTriangle(const VECTOR& point, const VECTOR& pa,const VECTOR& pb, const VECTOR& pc) {
VECTOR e10=pb-pa;
VECTOR e20=pc-pa;
float a = e10.dot(e10);
float b = e10.dot(e20);
float c = e20.dot(e20);
float ac_bb=(a*c)-(b*b);
VECTOR vp(point.x-pa.x, point.y-pa.y, point.z-pa.z);
float d = vp.dot(e10);
float e = vp.dot(e20);
float x = (d*c)-(e*b);
float y = (e*a)-(d*b);
float z = x+y-ac_bb;
return (( in(z)& ~(in(x)|in(y)) ) & 0x80000000);
}
I was wondering if this code snippet could be converted to java, and if so, if it would outperform my Java code:
public class Util {
public static boolean checkPointInTriangle(Vector p1, Vector p2, Vector p3, Vector point) {
float angles = 0;
Vector v1 = Vector.min(point, p1); v1.normalize();
Vector v2 = Vector.min(point, p2); v2.normalize();
Vector v3 = Vector.min(point, p3); v3.normalize();
angles += Math.acos(Vector.dot(v1, v2));
angles += Math.acos(Vector.dot(v2, v3));
angles += Math.acos(Vector.dot(v3, v1));
return (Math.abs(angles - 2*Math.PI) <= 0.005);
}
public static void main(String [] args) {
Vector p1 = new Vector(4.5f, 0, 0);
Vector p2 = new Vector(0, -9f, 0);
Vector p3 = new Vector(0, 0, 4.5f);
Vector point = new Vector(2, -4, 0.5f);
System.out.println(checkPointInTriangle(p1, p2, p3, point));
}
}
and the Vector class:
public class Vector {
public float x, y, z;
public Vector(float x, float y, float z) {
this.x = x; this.y = y; this.z = z;
}
public float length() {
return (float) Math.sqrt(x*x + y*y + z*z);
}
public void normalize() {
float l = length(); x /= l; y /= l; z /= l;
}
public static float dot(Vector one, Vector two) {
return one.x*two.x + one.y*two.y + one.z*two.z;
}
public static Vector min(Vector one, Vector two) {
return new Vector(one.x-two.x, one.y-two.y, one.z-two.z);
}
}
or is there an even faster method for Java?
Thanks in advance!
The code you've found, if correct, should be quite a bit faster than what you've got. The return statement
return (( in(z)& ~(in(x)|in(y)) ) & 0x80000000);
is just a tricky way of checking the sign bit of the floating point numbers; if I'm not completely wrong it's equivalent to:
return z < 0 && x >= 0 && y >= 0;
The text of the paper should confirm this. The rest I would guess you can convert yourself.
How to Rotate Circle with Text on TouchEvent or on TrackBallMoveEvent.
How do I create this kind of circle?
I had created a circle and rotated it also, but it always starts from 0 degrees.
Is there any other option to create this kind of circle?
Each circle have different text and each of the circles can move independently.
So, this is definitely not complete, but I think it's most of what you need.
Limitations/Assumptions
I have so far only implemented touch handling, as I think that's more difficult. If I get time later, I'll come back and add trackball handling.
I did not give the spinning discs any momentum. After the user's finger leaves the disc, it stops spinning.
I'm not sure the focus transitions between discs are 100% right. You'll have to do some testing. They're mostly right, at least.
When you mentioned Canvas in the title, I assumed that didn't mean you required this to utilize the J2ME Canvas. Writing BlackBerry apps with the RIM UI libraries is pretty much all I've done.
Solution
Essentially, I created a Field subclass to represent each disc. You create the field by passing in an array of labels, to be spaced around the perimeter, a radius, and a color. Hardcoded in each DiscField is an edge inset for the text, which kind of assumes a certain size difference between discs. You should probably make that more dynamic.
public class DiscField extends Field {
/** Used to map Manager's TouchEvents into our coordinate system */
private int _offset = 0;
private int _radius;
private int _fillColor;
private double _currentRotation = 0.0;
private double _lastTouchAngle = 0.0;
private boolean _rotating = false;
private String[] _labels;
/** Text inset from outer disc edge */
private static final int INSET = 30;
private DiscField() {
}
public DiscField(String[] labels, int radius, int fillColor) {
super(Field.FOCUSABLE);
_labels = labels;
_radius = radius;
_fillColor = fillColor;
}
protected void layout(int width, int height) {
setExtent(Math.min(width, getPreferredWidth()), Math.min(height, getPreferredHeight()));
}
private void drawFilledCircle(Graphics g, int x, int y, int r) {
// http://stackoverflow.com/a/1186851/119114
g.fillEllipse(x, y, x + r, y, x, y + r, 0, 360);
}
private void drawCircle(Graphics g, int x, int y, int r) {
g.drawEllipse(x, y, x + r, y, x, y + r, 0, 360);
}
protected void paint(Graphics graphics) {
int oldColor = graphics.getColor();
graphics.setColor(_fillColor);
drawFilledCircle(graphics, _radius, _radius, _radius);
graphics.setColor(Color.WHITE);
drawCircle(graphics, _radius, _radius, _radius);
// plot the text around the circle, inset by some 'padding' value
int textColor = (_fillColor == Color.WHITE) ? Color.BLACK : Color.WHITE;
graphics.setColor(textColor);
// equally space the labels around the disc
double interval = (2.0 * Math.PI / _labels.length);
for (int i = 0; i < _labels.length; i++) {
// account for font size when plotting text
int fontOffsetX = getFont().getAdvance(_labels[i]) / 2;
int fontOffsetY = getFont().getHeight() / 2;
int x = _radius + (int) ((_radius - INSET) * Math.cos(i * interval - _currentRotation)) - fontOffsetX;
int y = _radius - (int) ((_radius - INSET) * Math.sin(i * interval - _currentRotation)) - fontOffsetY;
graphics.drawText(_labels[i], x, y);
}
graphics.setColor(oldColor);
}
protected void drawFocus(Graphics graphics, boolean on) {
if (on) {
int oldColor = graphics.getColor();
int oldAlpha = graphics.getGlobalAlpha();
// just draw a white shine to indicate focus
graphics.setColor(Color.WHITE);
graphics.setGlobalAlpha(80);
drawFilledCircle(graphics, _radius, _radius, _radius);
// reset graphics context
graphics.setColor(oldColor);
graphics.setGlobalAlpha(oldAlpha);
}
}
protected void onUnfocus() {
super.onUnfocus();
_rotating = false;
}
protected boolean touchEvent(TouchEvent event) {
switch (event.getEvent()) {
case TouchEvent.MOVE: {
setFocus();
// Get the touch location, within this Field
int x = event.getX(1) - _offset - _radius;
int y = event.getY(1) - _offset - _radius;
if (x * x + y * y <= _radius * _radius) {
double angle = MathUtilities.atan2(y, x);
if (_rotating) {
// _lastTouchAngle only valid if _rotating
_currentRotation += angle - _lastTouchAngle;
// force a redraw (paint) with the new rotation angle
invalidate();
} else {
_rotating = true;
}
_lastTouchAngle = angle;
return true;
}
}
case TouchEvent.UNCLICK:
case TouchEvent.UP: {
_rotating = false;
return true;
}
case TouchEvent.DOWN: {
setFocus();
int x = event.getX(1) - _offset - _radius;
int y = event.getY(1) - _offset - _radius;
if (x * x + y * y <= _radius * _radius) {
_lastTouchAngle = MathUtilities.atan2(y, x);
_rotating = true;
return true;
}
}
default:
break;
}
return super.touchEvent(event);
}
protected boolean trackwheelRoll(int arg0, int arg1, int arg2) {
return super.trackwheelRoll(arg0, arg1, arg2);
// TODO!
}
public int getPreferredHeight() {
return getPreferredWidth();
}
public int getPreferredWidth() {
return 2 * _radius;
}
public String[] getLabels() {
return _labels;
}
public void setLabels(String[] labels) {
this._labels = labels;
}
public int getRadius() {
return _radius;
}
public void setRadius(int radius) {
this._radius = radius;
}
public double getCurrentAngle() {
return _currentRotation;
}
public void setCurrentAngle(double angle) {
this._currentRotation = angle;
}
public int getOffset() {
return _offset;
}
public void setOffset(int offset) {
this._offset = offset;
}
}
Containing all the DiscField objects is the DiscManager. It aligns the child DiscFields in sublayout(), and handles proper delegation of touch events ... since the fields overlap, and a touch within a DiscFields extent that does not also fall within its radius (i.e. the corners) should be handled by a larger disc.
/**
* A DiscManager is a container for DiscFields and manages proper delegation
* of touch event handling.
*/
private class DiscManager extends Manager {
private int _maxRadius = 0;
public DiscManager(long style){
super(style);
DiscField outerDisc = new DiscField(new String[] { "1", "2", "3", "4", "5", "6" },
180, Color.BLUE);
_maxRadius = outerDisc.getRadius();
DiscField middleDisc = new DiscField(new String[] { "1", "2", "3", "4", "5" },
120, Color.GRAY);
middleDisc.setOffset(_maxRadius - middleDisc.getRadius());
DiscField innerDisc = new DiscField(new String[] { "1", "2", "3", "4" },
60, Color.RED);
innerDisc.setOffset(_maxRadius - innerDisc.getRadius());
// order matters here:
add(outerDisc);
add(middleDisc);
add(innerDisc);
}
protected void sublayout(int width, int height) {
setExtent(2 * _maxRadius, 2 * _maxRadius);
// each disc needs to have the same x,y center to be concentric
for (int i = 0; i < getFieldCount(); i++) {
if (getField(i) instanceof DiscField) {
DiscField disc = (DiscField) getField(i);
int xCenter = _maxRadius - disc.getRadius();
int yCenter = _maxRadius - disc.getRadius();
setPositionChild(disc, xCenter, yCenter);
layoutChild(disc, 2 * _maxRadius, 2 * _maxRadius);
}
}
}
protected boolean touchEvent(TouchEvent event) {
int eventCode = event.getEvent();
// Get the touch location, within this Manager
int x = event.getX(1);
int y = event.getY(1);
if ((x >= 0) && (y >= 0) && (x < getWidth()) && (y < getHeight())) {
int field = getFieldAtLocation(x, y);
if (field >= 0) {
DiscField df = null;
for (int i = 0; i < getFieldCount(); i++) {
if (getField(field) instanceof DiscField) {
int r = ((DiscField)getField(field)).getRadius();
// (_maxRadius, _maxRadius) is the center of all discs
if ((x - _maxRadius) * (x - _maxRadius) + (y - _maxRadius) * (y - _maxRadius) <= r * r) {
df = (DiscField)getField(field);
} else {
// touch was not within this disc's radius, so the one slightly bigger
// should be passed this touch event
break;
}
}
}
// Let event propagate to child field
return (df != null) ? df.touchEvent(event) : super.touchEvent(event);
} else {
if (eventCode == TouchEvent.DOWN) {
setFocus();
}
// Consume the event
return true;
}
}
// Event wasn't for us, let superclass handle in default manner
return super.touchEvent(event);
}
}
Finally, a screen to use them:
public class DiscScreen extends MainScreen {
public DiscScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
add(new DiscManager(Field.USE_ALL_WIDTH));
}
}
Results