In JavaFX, how to colour the reverse side of a mesh? - javafx

I am creating a 3D plot in JavaFX using the same technique discussed in this answer, with a MeshView and a PhongMaterial to provide the colours. However, only the top side of the material is coloured, and if the user rotates the camera to view from below, it's impossible to determine the shape of the plot because it is all black.
My questions:
Is there any way to set the material of the reverse side of the mesh?
If not, is there a good approach to "faking" it? I would imagine creating a new mesh upside-down in exactly the same position would cause rendering issues; is the best approach to do that but apply a very small offset so that the two meshes are not exactly on top of each other?
Edit: I have included some example code below, which is cut down from my real code but contains enough to illustrate the problem. By default it displays the top of the mesh, which is coloured red in this example. If you change the line that reads new Rotate(-30, Rotate.X_AXIS) so that the angle becomes +30 rather than -30, it will rotate the camera to show the underside of the mesh, which you will see appears black.
package test;
import javafx.application.Application;
import javafx.scene.DepthTest;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TestApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TestPlot tp = new TestPlot();
tp.setPrefSize(600, 400);
Scene scene = new Scene(tp);
stage.setScene(scene);
stage.show();
}
class TestPlot extends Region {
private final PerspectiveCamera camera = new PerspectiveCamera(true);
private double[][] data = new double[500][500];
private final StackPane root = new StackPane();
private final SubScene subscene;
public TestPlot() {
subscene = new SubScene(root, 1, 1, true, SceneAntialiasing.BALANCED);
subscene.setCamera(camera);
getChildren().add(subscene);
widthProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
heightProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
refreshPlot();
}
private void refreshPlot() {
// Set the subscene bounds to match the plot bounds, in case the plot was
// resized
subscene.setHeight(this.getHeight());
subscene.setWidth(this.getWidth());
// Clear any existing stuff
root.getChildren().clear();
root.setStyle("-fx-background-color: rgba(0, 0, 0, 0);");
root.getChildren().add(camera);
camera.getTransforms().clear();
int xDataPoints = data.length;
int zDataPoints = data[0].length;
// Create data mesh
TriangleMesh mesh = new TriangleMesh();
for (int x = 0; x < xDataPoints; x++) {
for (int z = 0; z < zDataPoints; z++) {
// Invert the data as JavaFX meshes are positive-down, whereas we expect
// the plot to be positive-up
mesh.getPoints().addAll(x, (float) (-data[x][z]), z);
}
}
// Create faces from data mesh
for (int x = 0; x < xDataPoints - 1; x++) {
for (int z = 0; z < zDataPoints - 1; z++) {
int tl = x * zDataPoints + z; // top-left
int bl = x * zDataPoints + z + 1; // bottom-left
int tr = (x + 1) * zDataPoints + z; // top-right
int br = (x + 1) * zDataPoints + z + 1; // bottom-right
int offset = (x * (zDataPoints - 1) + z) * 8 / 2; // div 2 because we have u AND v in the list
// working
mesh.getFaces().addAll(bl, offset + 1, tl, offset + 0, tr, offset + 2);
mesh.getFaces().addAll(tr, offset + 2, br, offset + 3, bl, offset + 1);
}
}
// Create data mesh texture map
for (float x = 0; x < xDataPoints - 1; x++) {
for (float z = 0; z < zDataPoints - 1; z++) {
float x0 = x / xDataPoints;
float z0 = z / zDataPoints;
float x1 = (x + 1) / xDataPoints;
float z1 = (z + 1) / zDataPoints;
mesh.getTexCoords().addAll( //
x0, z0, // 0, top-left
x0, z1, // 1, bottom-left
x1, z1, // 2, top-right
x1, z1 // 3, bottom-right
);
}
}
// Create texture material
Image diffuseMap = createTexture(data);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(diffuseMap);
// Create & add mesh view
MeshView meshView = new MeshView(mesh);
meshView.setTranslateZ(-zDataPoints);
meshView.setMaterial(material);
meshView.setCullFace(CullFace.NONE);
meshView.setDrawMode(DrawMode.FILL);
meshView.setDepthTest(DepthTest.ENABLE);
root.getChildren().addAll(meshView);
double biggestAxisSize = xDataPoints;
double z = -(0.5 * biggestAxisSize) / Math.tan(0.5 * Math.toRadians(camera.getFieldOfView()));
camera.getTransforms().addAll(
new Translate(0, 0, -zDataPoints / 3.0),
new Rotate(-30, Rotate.X_AXIS),
new Translate(0, 0.5, z)
);
camera.setFarClip(biggestAxisSize * 200.0);
}
private Image createTexture(double[][] data) {
int width = data.length;
int height = data[0].length;
WritableImage wr = new WritableImage(width, height);
PixelWriter pw = wr.getPixelWriter();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
pw.setColor(x, y, Color.RED);
}
}
return wr;
}
}
}

Modify the cullFace property of your MeshView:
meshView.setCullFace(CullFace.NONE);
Also you need to add ambient light to the scene. The normals of the surface are automatiacally determined and the scalar product used with won't be positive, if the normal is facing away from the light source...
root.getChildren().add(new AmbientLight(Color.WHITE));

Related

JavaFX how do I fix stream()

I'm building Minesweeper following this video: https://www.youtube.com/watch?v=JwcyxuKko_M
But I need a MVC pattern for this and I'm stuck with a NullPointer here in the line with the stream in View:
public class View{
Model model;
Field f; //here's where I'm trying to access the List created in Field class
Stage primarystage;
Text number = new Text();
public View(Model model, Stage primaryStage){
this.model = model;
this.primarystage = primaryStage;
Pane root = new Pane();
root.setPrefSize(model.WIDTH, model.HEIGHT);
//iterate through rows and columns to fill board with random bombs
for (int y = 0; y < model.Y_FIELDS; y++) {
for (int x = 0; x < model.X_FIELDS; x++) {
Field field = new Field(x, y, Math.random() < 0.2);
model.array[x][y] = field;
root.getChildren().add(field);
}
}
for (int y = 0; y < model.Y_FIELDS; y++) {
for (int x = 0; x < model.X_FIELDS; x++) {
Field field = model.array[x][y];
//trying to access the method getSurrounding from class Field with f
long bombs = f.getSurrounding(field).stream().filter(b -> b.isBomb).count(); //number of bombs
if (bombs > 0)
field.bomb.setText(String.valueOf(bombs));
}
}
Scene scene = new Scene(root, model.getWidth(), model.getHeight());
getStage().setScene(scene);
}
Here's the Field class:
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import java.util.ArrayList;
import java.util.List;
public class Field extends StackPane{
//information for each field: x coordinate, y coordinate, is it a bomb or not
int x;
int y;
boolean isBomb; //determines whether or not the field is a bomb
int bombsNum = 0; //counts how many bombs are surrounding the field
Model model;
Rectangle board = new Rectangle(model.FIELD_SIZE - 2, model.FIELD_SIZE - 2);
Text bomb = new Text();
public Field(int x, int y, boolean isBomb){
this.x = x;
this.y = y;
this.isBomb = isBomb;
board.setFill(Color.LAVENDER);
board.setStroke(Color.BLACK);
bomb.setText(isBomb ? "X" : "");
getChildren().addAll(board,bomb);
setTranslateX(x * model.FIELD_SIZE);
setTranslateY(y * model.FIELD_SIZE);
}
public List<Field> getSurrounding(Field field){
//looks at all the fields surrounding the current field
List<Field> surrounding = new ArrayList<>();
int[] coordinates = new int[]{
-1,-1, //top left field
-1, 0, //left field
-1, 1, //bottom left field
0,-1, //top middle field
0, 1, //bottom middle field
1,-1, //top right field
1, 0, //right field
1, 1 //bottom right field
};
for (int i = 0; i < coordinates.length; i++) {
int columnX = coordinates[i]; //considers the x coordinate of a surrounding field
int rowY = coordinates[++i]; //considers the y coordinate of a surrounding field
int newX = field.x + columnX; //sets the x coordinate of a surrounding field
int newY = field.y + rowY; //sets the y coordinate of a surrounding field
if (newX >= 0 && newX < model.X_FIELDS //make sure it's not out of bounds
&& newY >= 0 && newY < model.Y_FIELDS) {
surrounding.add(model.array[newX][newY]);
}
}
return surrounding;
}
}
And the Variables in Model:
public static final int FIELD_SIZE = 40;
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
//sets number of fields in x and y axis
public static final int X_FIELDS = WIDTH / FIELD_SIZE;
public static final int Y_FIELDS = HEIGHT / FIELD_SIZE;
public Field[][] array = new Field[X_FIELDS][Y_FIELDS];
Why isn't it working? I'm trying to pass a list (containing all neighbors of a field) into the stream and filter for the ones containing bombs and then counts those bombs so I can display that number on the current field so the player will know. Why doesn't it recognize the list I passed? Or is it the filter that's confused? Thank you.
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at View.<init>(View.java:46)
at Main.start(Main.java:10)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
I think you have a design problem in getSurrounding(). At the moment you require to pass a Field as an argument but as far as I understand this parameter is non-sense. When you want to grab the surroundings of a Field you don't need to provide another Field ! Just use the current one (this) !
public List<Field> getSurrounding() {
//looks at all the fields surrounding the current field
List<Field> surrounding = new ArrayList<>();
int[] coordinates = new int[]{
-1,-1, //top left field
-1, 0, //left field
-1, 1, //bottom left field
0,-1, //top middle field
0, 1, //bottom middle field
1,-1, //top right field
1, 0, //right field
1, 1 //bottom right field
};
for (int i = 0; i < coordinates.length; i++) {
int columnX = coordinates[i]; //considers the x coordinate of a surrounding field
int rowY = coordinates[++i]; //considers the y coordinate of a surrounding field
int newX = this.x + columnX; //sets the x coordinate of a surrounding field
int newY = this.y + rowY; //sets the y coordinate of a surrounding field
if (newX >= 0 && newX < model.X_FIELDS
&& newY >= 0 && newY < model.Y_FIELDS) {
surrounding.add(model.array[newX][newY]);
}
}
return surrounding;
}
Thefore the line that causes you problem at the moment can be changed to:
long bombs = field.getSurrounding().stream().filter(b -> b.isBomb).count();
It means you can get rid of the view attribute Field f because it is now unused. That was the reason why it was null because it had no purpose, therefore was never initialized.

When zooming with JavaFX, items move weirdly

I have a problem with zooming using javaFX and zooming. The items are wrapped in a group which is then scaled by the ZoomUtil. One node is animated to show the weird effect resulting: The Rectangles should stay within their original place and not constantly move around the frame.
I have no idea what causes this behaviour and tried many different transform settings and such but were not successful.
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
List<Rectangle> rectangles = new ArrayList<>(5);
Color color = new Color(random(), random(), random(), 1);
Rectangle r = new Rectangle(200, 140,color);
r.relocate(-500,-500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(500, -500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(500, 500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(0, 0);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(-500, 500);
rectangles.add(r);
Timeline timeline = new Timeline();
Node circle = r;
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.seconds(10), // set start position at 0
new KeyValue(circle.translateXProperty(), random() * 8000),
new KeyValue(circle.translateYProperty(), random() * 6000)
),
new KeyFrame(new Duration(80000), // set end position at 40s
new KeyValue(circle.translateXProperty(), random() * 8000),
new KeyValue(circle.translateYProperty(), random() * 6000)
)
);
Group group = new Group();
group.setManaged(false);
group.getChildren().addAll(rectangles);
StackPane pane = new StackPane();
pane.setPrefWidth(500);
pane.setPrefHeight(500);
Scene scene = new Scene(pane);
pane.getChildren().add(group);
pane.setOnScroll(event -> ZoomUtil.zoom(group, event));
primaryStage.setScene(scene);
primaryStage.setWidth(500);
primaryStage.setHeight(500);
primaryStage.show();
timeline.play();
}
}
.
public class ZoomUtil {
/** Allow to zoom/scale any node with pivot at scene (x,y) coordinates.
*
* #param node
* #param x
* #param y
*/
public static void zoom(Node node, double factor, double x, double y) {
double oldScale = node.getScaleX();
double scale = oldScale * factor;
if (scale < 0.05) scale = 0.05;
if (scale > 50) scale = 50;
node.setScaleX(scale);
node.setScaleY(scale);
double f = (scale / oldScale) - 1;
/* Move view by translational difference */
Bounds bounds = node.localToScene(node.getBoundsInLocal());
double dx = (x - (bounds.getWidth() / 2 + bounds.getMinX()));
double dy = (y - (bounds.getHeight() / 2 + bounds.getMinY()));
node.setTranslateX(node.getTranslateX() - f * dx);
node.setTranslateY(node.getTranslateY() - f * dy);
}
public static void zoom(Node node, ScrollEvent event) {
zoom(node, Math.pow(1.002, event.getDeltaY()), event.getSceneX(), event.getSceneY());
}
public static void zoom(Node node, ZoomEvent event) {
zoom(node, event.getZoomFactor(), event.getSceneX(), event.getSceneY());
}
}

How to calculate the center point of an arc?

I have a CustomPieView which is made of several pie slices. I have to draw something in the middle of every pie section.
//Inside Activity's onCreate Method
public void onCreate(Bundle savedInstanceState){
int size = 400;
int bgColor = 0xffa11b1;
ViewPieChart piechart = (ViewPieChart) findViewById(R.id.pieChartView);
piechart.setCallBack(this);
piechart.setLayoutParams(new LayoutParams(size, size));
piechart.setGeometry(size, size, 2, 2, 2, 2, 2130837504);
piechart.setSkinparams(bgColor);
piechart.setData(piedata, maxCount);
piechart.invalidate();
}
//CustomPieView extends View
public void setGeometry(int width, int height, int gapleft, int gapright, int gaptop, int gapbottom, int overlayid) {
mWidth = width;
mHeight = height;
mGapleft = gapleft;
mGapright = gapright;
mGapBottm = gapbottom;
mGapTop = gaptop;
}
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.DKGRAY);
mBagpaints.setAntiAlias(true);
mBagpaints.setStyle(Paint.Style.FILL);
mBagpaints.setColor(0x88FF0000);
mBagpaints.setStrokeWidth(0.0f);
mLinePaints.setAntiAlias(true);
mLinePaints.setColor(Color.WHITE);
mLinePaints.setStrokeWidth(3.0f);
mLinePaints.setStyle(Paint.Style.STROKE);
sLinePaint.setAntiAlias(true);
sLinePaint.setColor(Color.BLACK);
sLinePaint.setStrokeWidth(3.0f);
sLinePaint.setStyle(Paint.Style.STROKE);
RectF mOvals = new RectF(mGapleft, mGapTop, mWidth - mGapright, mHeight
- mGapBottm);
mStart = START_INC;
PieDetailsItem item;
for (int i = 0; i < mdataArray.size(); i++) {
item = (PieDetailsItem) mdataArray.get(i);
mBagpaints.setColor(item.color);
mSweep = (float) 360* ((float) item.count / (float) mMaxConnection);
canvas.drawArc(mOvals, mStart, mSweep, true, mBagpaints);
canvas.drawArc(mOvals, mStart, mSweep, true, mLinePaints);
// The function below is setting the global vars
cSegX, cSegY to the center of the segment. Not Working!!
calculateMidPointOfPieSegment(mSweep);
canvas.drawPoint(cSegX, cSegY, sLinePaint);
mStart = mStart + mSweep;
}
mState = IS_DRAW;
callBack.onDrawFinished(null);
}
private float calculateRadius(){
float width = mWidth/2;
float height = mHeight/2;
if(width < height){
return width;
}else{
return height;
}
}
private void calculateMidPointOfPieSegment(float sweepAngle){
cSegX = (float)((calculateRadius()/2)*Math.cos(Math.toRadians(sweepAngle/2))+(mWidth/2));
cSegY = (float)((calculateRadius()/2)*Math.sin(Math.toRadians(sweepAngle/2))+(mHeight/2));
}
Just see the dots in the image below, it has to be in the center of every arc.
Screenshot
Ref.
Complete Source
Let me know if this clears it up. This is the basic logic you want to implement.
//rimPoint = (rX, rY)
//centerPoint = (cX,cY)
//theta is the angle of the the midpoint measured anticlockwise from the x axis,
//or the average of the two angles making up the slice(measured anticlockwise from
//the x axis)
private void calculateMidPointOfPieSegment(cX,cY, theta)
{
float rX = cX + calculateRadius()*(cos(theta))
float rY = cY + calculateRadius()*(sin(theta))
cSegX = (rX+cX)/2;
cCegY = (rY+cY)/2;
}
If theta isn't in radians, sub it out for theta*Math.PI/180

JFreechart Polar Chart shape annotation

I am trying to color different region of a polar chart with different colors. e.g coloring the region between the angle 20 and 60 and between the radii 2 and 4.
How can I do this? I was thinking of using a shape annotation and from there drawing an arc, but it seems there is no shape annotation for polar plots.
Any ideas?
Thank you
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTick;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PolarPlot;
import org.jfree.chart.renderer.DefaultPolarItemRenderer;
import org.jfree.chart.renderer.PolarItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.TextAnchor;
public class test2 extends JFrame {
private static final String title = "Archimedes' Spiral";
public test2(String title) {
super(title);
JFreeChart chart = createChart(createDataset());
ChartPanel panel = new ChartPanel(chart);
panel.setPreferredSize(new Dimension(500, 500));
panel.setMouseZoomable(false);
this.add(panel);
}
private static XYDataset createDataset() {
XYSeriesCollection result = new XYSeriesCollection();
XYSeries series = new XYSeries(title);
XYSeries ser = new XYSeries("test");
for (int t = 0; t <= 1 * 360; t++) {
series.add(90 - t, t);
}
for (int t = 0; t <= 1 * 120; t++) {
ser.add(90 - t, 40);
ser.add(90 - t, 120);
}
result.addSeries(series);
result.addSeries(ser);
return result;
}
private static JFreeChart createChart(XYDataset dataset) {
ValueAxis radiusAxis = new NumberAxis();
radiusAxis.setTickLabelsVisible(false);
PolarItemRenderer renderer = new DefaultPolarItemRenderer();
PolarPlot plot = new PolarPlot(dataset, radiusAxis, renderer) {
#Override
protected List refreshAngleTicks() {
List<NumberTick> ticks = new ArrayList<NumberTick>();
int delta = (int) this.getAngleTickUnit().getSize();
for (int t = 0; t < 360; t += delta) {
int tp = (360 + 90 - t) % 360;
NumberTick tick = new NumberTick(
Double.valueOf(t), String.valueOf(tp),
TextAnchor.CENTER, TextAnchor.CENTER, 0.0);
ticks.add(tick);
}
return ticks;
}
};
plot.setBackgroundPaint(new Color(0x00f0f0f0));
plot.setRadiusGridlinePaint(Color.gray);
plot.addCornerTextItem("r(θ) = θ; 0 < θ < 6π");
DefaultPolarItemRenderer ren = new DefaultPolarItemRenderer();
ren.setSeriesFilled(0, true);
ren.setSeriesFilled(1, true);
plot.setRenderer(ren);
JFreeChart chart = new JFreeChart(
title, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
chart.setBackgroundPaint(Color.white);
return chart;
}
public static void main(String[] args) {
test2 demo = new test2(title);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
}
}
The DefaultPolarItemRenderer typically used in a PolarPlot has the method setSeriesFilled(), which controls whether a series is filled. The renderer specifies the AlphaComposite.SRC_OVER mode with a value of 50%, so overlapping fills look especially nice.
Addendum: To create the chart seen below, start with this example and reduce the data set's domain from 6π to 2π in createDataset():
for (int t = 0; t <= 1 * 360; t++) { ...
Then make the series filled in createChart():
...
DefaultPolarItemRenderer renderer = new DefaultPolarItemRenderer();
renderer.setSeriesFilled(0, true);
...

Android - trouble in implementing this user interface

I am trying to implement a UI like this one..
http://www.shrenikvikam.com/wp-content/uploads/2011/04/214e422a43E11S3.png-150x134.png
But i am having some trouble implementing this.. Could someone tell me mistakes in this...
public class Meter extends View{
static final int ORBIT_COLOR = Color.argb(255, 66, 66, 66);
static final double RAD_CIRCLE = 2*Math.PI; // Number radians in a circle
private Paint paint; // Paint object controlling format of screen draws
private ShapeDrawable planet; // Planet symbol
private int planetRadius = 7; // Radius of spherical planet (pixels)
private int sunRadius = 12; // Radius of Sun (pixels)
private float X0 = 0; // X offset from center (pixels)
private float Y0 = 0; // Y offset from center (pixels)
private float X; // Current X position of planet (pixels)
private float Y; // Current Y position of planet (pixels)
private float centerX; // X for center of display (pixels)
private float centerY; // Y for center of display (pixels)
private float R0; // Radius of circular orbit (pixels)
private int nsteps = 120; // Number animation steps around circle
private double theta; // Angle around orbit (radians)
private double dTheta; // Angular increment each step (radians)
private double direction = -1; // Direction: counter-clockwise -1; clockwise +1
private float lastTouchX; // x coordinate of symbol i at last touch
private float lastTouchY; // x coordinate of symbol i at last touch
private int divisions = 120; // Since it requires temperature change from 0 -120
private double oneSegmentLength = (2 * Math.PI * R0)/(double)120;
public Meter(Context context) {
super(context);
// Initialize angle and angle step (in radians)
theta = 30;
//dTheta = RAD_CIRCLE/((double) nsteps); // Angle increment in radians
dTheta = ((360-60)/(double)divisions);
planet = new ShapeDrawable(new OvalShape());
planet.getPaint().setColor(Color.WHITE);
planet.setBounds(0, 0, 2*planetRadius, 2*planetRadius);
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(14);
paint.setStrokeWidth(1);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
// MotionEvent class constant signifying a finger-down event
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
// MotionEvent class constant signifying a finger-drag event
case MotionEvent.ACTION_MOVE: {
float newX = ev.getX();
float newY = ev.getY();
float dx = newX - lastTouchX;
float dy = newY - lastTouchY;
int diff = (int) (Math.abs(ev.getX()) % Math.abs(oneSegmentLength));
if(diff == 0){
if(Math.abs(dx) > Math.abs(dy)) {
if(dx>0) direction = 1;
else direction = -1;
newXY();
} else {
newXY();
}
Log.d("MOVE", "dx ->" + dx + " one seg->" + oneSegmentLength);
invalidate();
}
break;
}
// MotionEvent class constant signifying a finger-up event
case MotionEvent.ACTION_UP: {
Log.d("ACTION MOVE","Value ->");
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(paint, canvas);
canvas.save();
canvas.translate(X + X0, Y + Y0);
planet.draw(canvas);
canvas.restore();
}
// Called by onDraw to draw the background
private void drawBackground(Paint paint, Canvas canvas){
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX + X0, centerY + Y0, sunRadius, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(ORBIT_COLOR);
canvas.drawCircle(centerX + X0, centerY + Y0, R0, paint);
}
//Method to increment angle theta and compute the new X and Y .
private void newXY(){
theta += dTheta;
Log.d("THETA VAL", "->" + theta);
//if(theta > RAD_CIRCLE) theta -= RAD_CIRCLE; // For convenience, keep angle 0-2pi
if(theta > 150)theta = 30;
if(theta > 30 && theta <120){
X = (float)(R0*Math.sin(direction*theta)) + centerX - planetRadius;
Y = centerY - (float)(R0*Math.cos(direction*theta)) - planetRadius;
}
//Log.i("ANIMATOR", "X="+X+" Y="+Y);
}
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh){
// Coordinates for center of screen
centerX = w/2;
centerY = h/2;
// Make orbital radius a fraction of minimum of width and height of display
R0 = (float) (0.90*Math.min(centerX, centerY));
oneSegmentLength = (2 * Math.PI * R0)/(double)120;
// Set the initial position of the planet (translate by planetRadius so center of planet
// is at this position)
X = centerX - planetRadius ;
Y = centerY - R0 - planetRadius;
}
}
I am referring this code to do this implementation...
http://eagle.phys.utk.edu/guidry/android/animatorDemo.html
I am just drawing a circle and trying to implement the same motion between 0 -120 degrees..

Resources