I am developing an Augmented reality application with Rajawali lib. My problem is as below.
I want to draw a surface onto the camera view, when i tried the latest ver of rajawali, it didn't work. I spent many days and find out that the latest did not support draw over camera any more. And the rajawali v0.9 work fine. So the following question is apply for v0.9.
When i tried to register SensorEventListener, and in onSensorChanged() I got 3 values which represent 3 dimension of android device, but it was very noise and unstable. I have tried implement low-pass filter but it still noise.
Finally i found this question, but on v0.9, the
getCamera().setOrientation(quaternion)
did not work. I dont know why.
Now i don't know what to do next :(
Here is my codes, it works very well in my project, and I hope it to help you.
// The code snippet of my renderer class
#Override
public void onRender(final long elapsedTime, final double deltaTime) {
mHeadTracker.getLastHeadView(mHeadTransform.getHeadView(), 0);
android.opengl.Matrix.invertM(mHeadTransform.getHeadView(), 0, mHeadTransform.getHeadView(), 0);
Quaternion q = mHeadTransform.getQuaternion(mHeadTransform.getHeadView(), 0);
getCurrentCamera().setOrientation(q);
super.onRender(elapsedTime, deltaTime);
}
// The code snippet of HeadTransform class
private static Quaternion sQuaternion = new Quaternion();
public Quaternion getQuaternion(float[] quaternion, int offset) {
if (offset + 4 > quaternion.length) {
throw new IllegalArgumentException(
"Not enough space to write the result");
}
float[] m = this.mHeadView;
float t = m[0] + m[5] + m[10];
float x;
float y;
float z;
float w;
float s;
if (t >= 0.0F) {
s = (float) Math.sqrt(t + 1.0F);
w = 0.5F * s;
s = 0.5F / s;
x = (m[9] - m[6]) * s;
y = (m[2] - m[8]) * s;
z = (m[4] - m[1]) * s;
} else {
if ((m[0] > m[5]) && (m[0] > m[10])) {
s = (float) Math.sqrt(1.0F + m[0] - m[5] - m[10]);
x = s * 0.5F;
s = 0.5F / s;
y = (m[4] + m[1]) * s;
z = (m[2] + m[8]) * s;
w = (m[9] - m[6]) * s;
} else {
if (m[5] > m[10]) {
s = (float) Math.sqrt(1.0F + m[5] - m[0] - m[10]);
y = s * 0.5F;
s = 0.5F / s;
x = (m[4] + m[1]) * s;
z = (m[9] + m[6]) * s;
w = (m[2] - m[8]) * s;
} else {
s = (float) Math.sqrt(1.0F + m[10] - m[0] - m[5]);
z = s * 0.5F;
s = 0.5F / s;
x = (m[2] + m[8]) * s;
y = (m[9] + m[6]) * s;
w = (m[4] - m[1]) * s;
}
}
}
quaternion[(offset + 0)] = x;
quaternion[(offset + 1)] = y;
quaternion[(offset + 2)] = z;
quaternion[(offset + 3)] = w;
Log.d("facevr", x + "," + y + "," + z + "," + w);
return sQuaternion.setAll(w, x, y, z);
}
Related
I'm trying to draw a pie chart with rounded corners using MpAndroidChart library.
Expected output is something similar to this.
Both ends need to be outer round. There is a method pieChart.setDrawRoundedSlices(true), but the issue is start point of the pie chart getting inner round.
This is the actual output.
// initialise pie chart UI
fun initChart(mChart: PieChart) {
mChart.description.isEnabled = false
mChart.holeRadius = 75f
mChart.transparentCircleRadius = 60f
mChart.setHoleColor(Color.TRANSPARENT)
mChart.legend.isEnabled = false
mChart.isRotationEnabled = false
mChart.setTouchEnabled(false)
mChart.maxAngle = 270f
mChart.rotation = -135f
mChart.animateX(400)
mChart.setDrawRoundedSlices(true)
}
I was faced with the same challenge recently, this is the code of the renderer in case anyone may need it:
public class RoundedSlicesPieChartRenderer extends PieChartRenderer {
public RoundedSlicesPieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
chart.setDrawRoundedSlices(true);
}
#Override
protected void drawDataSet(Canvas c, IPieDataSet dataSet) {
float angle = 0;
float rotationAngle = mChart.getRotationAngle();
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
final RectF circleBox = mChart.getCircleBox();
final int entryCount = dataSet.getEntryCount();
final float[] drawAngles = mChart.getDrawAngles();
final MPPointF center = mChart.getCenterCircleBox();
final float radius = mChart.getRadius();
final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
final float userInnerRadius = drawInnerArc
? radius * (mChart.getHoleRadius() / 100.f)
: 0.f;
final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
final RectF roundedCircleBox = new RectF();
int visibleAngleCount = 0;
for (int j = 0; j < entryCount; j++) {
// draw only if the value is greater than zero
if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) {
visibleAngleCount++;
}
}
final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet);
final Path pathBuffer = new Path();
final RectF mInnerRectBuffer = new RectF();
for (int j = 0; j < entryCount; j++) {
float sliceAngle = drawAngles[j];
float innerRadius = userInnerRadius;
Entry e = dataSet.getEntryForIndex(j);
// draw only if the value is greater than zero
if (!(Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) {
angle += sliceAngle * phaseX;
continue;
}
// Don't draw if it's highlighted, unless the chart uses rounded slices
if (mChart.needsHighlight(j) && !drawInnerArc) {
angle += sliceAngle * phaseX;
continue;
}
final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
mRenderPaint.setColor(dataSet.getColor(j));
final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
0.f :
sliceSpace / (Utils.FDEG2RAD * radius);
final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY;
float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY;
if (sweepAngleOuter < 0.f) {
sweepAngleOuter = 0.f;
}
pathBuffer.reset();
float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
// Android is doing "mod 360"
pathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW);
} else {
if (drawInnerArc) {
float x = center.x + (radius - roundedRadius) * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
float y = center.y + (radius - roundedRadius) * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
pathBuffer.arcTo(roundedCircleBox, startAngleOuter - 180, 180);
}
pathBuffer.arcTo(
circleBox,
startAngleOuter,
sweepAngleOuter
);
}
// API < 21 does not receive floats in addArc, but a RectF
mInnerRectBuffer.set(
center.x - innerRadius,
center.y - innerRadius,
center.x + innerRadius,
center.y + innerRadius);
if (drawInnerArc && (innerRadius > 0.f || accountForSliceSpacing)) {
if (accountForSliceSpacing) {
float minSpacedRadius =
calculateMinimumRadiusForSpacedSlice(
center, radius,
sliceAngle * phaseY,
arcStartPointX, arcStartPointY,
startAngleOuter,
sweepAngleOuter);
if (minSpacedRadius < 0.f)
minSpacedRadius = -minSpacedRadius;
innerRadius = Math.max(innerRadius, minSpacedRadius);
}
final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ?
0.f :
sliceSpace / (Utils.FDEG2RAD * innerRadius);
final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY;
float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY;
if (sweepAngleInner < 0.f) {
sweepAngleInner = 0.f;
}
final float endAngleInner = startAngleInner + sweepAngleInner;
if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
// Android is doing "mod 360"
pathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW);
} else {
float x = center.x + (radius - roundedRadius) * (float) Math.cos(endAngleInner * Utils.FDEG2RAD);
float y = center.y + (radius - roundedRadius) * (float) Math.sin(endAngleInner * Utils.FDEG2RAD);
roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
pathBuffer.arcTo(roundedCircleBox, endAngleInner, 180);
pathBuffer.arcTo(mInnerRectBuffer, endAngleInner, -sweepAngleInner);
}
} else {
if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) {
if (accountForSliceSpacing) {
float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f;
float sliceSpaceOffset =
calculateMinimumRadiusForSpacedSlice(
center,
radius,
sliceAngle * phaseY,
arcStartPointX,
arcStartPointY,
startAngleOuter,
sweepAngleOuter);
float arcEndPointX = center.x +
sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD);
float arcEndPointY = center.y +
sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD);
pathBuffer.lineTo(
arcEndPointX,
arcEndPointY);
} else {
pathBuffer.lineTo(
center.x,
center.y);
}
}
}
pathBuffer.close();
mBitmapCanvas.drawPath(pathBuffer, mRenderPaint);
angle += sliceAngle * phaseX;
}
MPPointF.recycleInstance(center);
}
}
And then you use it like this:
mChart.setRenderer(new RoundedSlicesPieChartRenderer(pieChart, pieChart.getAnimator(), pieChart.getViewPortHandler()));
I am new in android developing, I developed this code based on previous code related to Mr.liwatiz to find orientation from sensor fusion, I added writeCSV file to store data, The application work and the file created but there is no data store! So please what is the problem. my code clear below
public class MainActivity extends Activity implements SensorEventListener, RadioGroup.OnCheckedChangeListener{
private SensorManager mSensorManager = null;
// angular speeds from gyro
private float[] gyro = new float[3];
// rotation matrix from gyro data
private float[] gyroMatrix = new float[9];
// orientation angles from gyro matrix
private float[] gyroOrientation = new float[3];
// magnetic field vector
private float[] magnet = new float[3];
// accelerometer vector
private float[] accel = new float[3];
// orientation angles from accel and magnet
private float[] accMagOrientation = new float[3];
// final orientation angles from sensor fusion
private float[] fusedOrientation = new float[3];
// accelerometer and magnetometer based rotation matrix
private float[] rotationMatrix = new float[9];
public static final float EPSILON = 0.000000001f;
private static final float NS2S = 1.0f / 1000000.0f;
private int timestamp;
private boolean initState = true;
public static final int TIME_CONSTANT = 30;
public static final float FILTER_COEFFICIENT = 0.98f;
private Timer fuseTimer = new Timer();
// The following members are only for displaying the sensor output.
public Handler mHandler;
private RadioGroup mRadioGroup;
private TextView mAzimuthView;
private TextView mPitchView;
private TextView mRollView;
private int radioSelection;
DecimalFormat d = new DecimalFormat("#.##");
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gyroOrientation[0] = 0.0f;
gyroOrientation[1] = 0.0f;
gyroOrientation[2] = 0.0f;
// initialise gyroMatrix with identity matrix
gyroMatrix[0] = 1.0f; gyroMatrix[1] = 0.0f; gyroMatrix[2] = 0.0f;
gyroMatrix[3] = 0.0f; gyroMatrix[4] = 1.0f; gyroMatrix[5] = 0.0f;
gyroMatrix[6] = 0.0f; gyroMatrix[7] = 0.0f; gyroMatrix[8] = 1.0f;
// get sensorManager and initialise sensor listeners
mSensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);
initListeners();
// wait for one second until gyroscope and magnetometer/accelerometer
// data is initialised then scedule the complementary filter task
fuseTimer.scheduleAtFixedRate(new calculateFusedOrientationTask(),
1000, TIME_CONSTANT);
// GUI stuff
mHandler = new Handler();
radioSelection = 0;
d.setRoundingMode(RoundingMode.HALF_UP);
d.setMaximumFractionDigits(3);
d.setMinimumFractionDigits(3);
mRadioGroup = (RadioGroup)findViewById(R.id.radioGroup1);
mAzimuthView = (TextView)findViewById(R.id.textView4);
mPitchView = (TextView)findViewById(R.id.textView5);
mRollView = (TextView)findViewById(R.id.textView6);
mRadioGroup.setOnCheckedChangeListener(this);
}
#Override
public void onStop() {
super.onStop();
// unregister sensor listeners to prevent the activity from draining the device's battery.
mSensorManager.unregisterListener(this);
}
#Override
protected void onPause() {
super.onPause();
// unregister sensor listeners to prevent the activity from draining the device's battery.
mSensorManager.unregisterListener(this);
}
#Override
public void onResume() {
super.onResume();
// restore the sensor listeners when user resumes the application.
initListeners();
}
// This function registers sensor listeners for the accelerometer, magnetometer and gyroscope.
public void initListeners(){
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void writeToCsvGy(String x,String y,String z) throws IOException {
Calendar c = Calendar.getInstance();
File folder = new File(Environment.getExternalStorageDirectory() + "/TollCulator");
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
// Do something on success
String csv = "/storage/sdcard0/project/GyroscopeValue.csv";
FileWriter file_writer = new FileWriter(csv,true);
String s= c.get(Calendar.YEAR)+","+c.get(Calendar.MONTH)+","+c.get(Calendar.DATE)+","+c.get(Calendar.HOUR)+","+c.get(Calendar.MINUTE)+","+c.get(Calendar.SECOND)+","+ c.get(Calendar.MILLISECOND)+","+x + ","+y+","+z+"\n";
file_writer.append(s);
file_writer.close();
}
}
#Override
public void onSensorChanged(SensorEvent event) {
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
// copy new accelerometer data into accel array and calculate orientation
System.arraycopy(event.values, 0, accel, 0, 3);
calculateAccMagOrientation();
break;
case Sensor.TYPE_GYROSCOPE:
// process gyro data
gyroFunction(event);
break;
case Sensor.TYPE_MAGNETIC_FIELD:
// copy new magnetometer data into magnet array
System.arraycopy(event.values, 0, magnet, 0, 3);
break;
}
}
// calculates orientation angles from accelerometer and magnetometer output
public void calculateAccMagOrientation() {
if(SensorManager.getRotationMatrix(rotationMatrix, null, accel, magnet)) {
SensorManager.getOrientation(rotationMatrix, accMagOrientation);
}
}
// This function is borrowed from the Android reference
// at http://developer.android.com/reference/android/hardware/SensorEvent.html#values
// It calculates a rotation vector from the gyroscope angular speed values.
private void getRotationVectorFromGyro(float[] gyroValues,
float[] deltaRotationVector,
float timeFactor)
{
float[] normValues = new float[3];
// Calculate the angular speed of the sample
float omegaMagnitude =
(float)Math.sqrt(gyroValues[0] * gyroValues[0] +
gyroValues[1] * gyroValues[1] +
gyroValues[2] * gyroValues[2]);
// Normalize the rotation vector if it's big enough to get the axis
if(omegaMagnitude > EPSILON) {
normValues[0] = gyroValues[0] / omegaMagnitude;
normValues[1] = gyroValues[1] / omegaMagnitude;
normValues[2] = gyroValues[2] / omegaMagnitude;
}
// Integrate around this axis with the angular speed by the timestep
// in order to get a delta rotation from this sample over the timestep
// We will convert this axis-angle representation of the delta rotation
// into a quaternion before turning it into the rotation matrix.
float thetaOverTwo = omegaMagnitude * timeFactor;
float sinThetaOverTwo = (float)Math.sin(thetaOverTwo);
float cosThetaOverTwo = (float)Math.cos(thetaOverTwo);
deltaRotationVector[0] = sinThetaOverTwo * normValues[0];
deltaRotationVector[1] = sinThetaOverTwo * normValues[1];
deltaRotationVector[2] = sinThetaOverTwo * normValues[2];
deltaRotationVector[3] = cosThetaOverTwo;
}
// This function performs the integration of the gyroscope data.
// It writes the gyroscope based orientation into gyroOrientation.
public void gyroFunction(SensorEvent event) {
// don't start until first accelerometer/magnetometer orientation has been acquired
if (accMagOrientation == null)
return;
// initialisation of the gyroscope based rotation matrix
if(initState) {
float[] initMatrix = new float[9];
initMatrix = getRotationMatrixFromOrientation(accMagOrientation);
float[] test = new float[3];
SensorManager.getOrientation(initMatrix, test);
gyroMatrix = matrixMultiplication(gyroMatrix, initMatrix);
initState = false;
}
// copy the new gyro values into the gyro array
// convert the raw gyro data into a rotation vector
float[] deltaVector = new float[4];
if(timestamp != 0) {
final float dT = (event.timestamp - timestamp) * NS2S;
System.arraycopy(event.values, 0, gyro, 0, 3);
getRotationVectorFromGyro(gyro, deltaVector, dT / 2.0f);
}
// measurement done, save current time for next interval
switch ( timestamp = (int) event.timestamp ) {
}
// convert rotation vector into rotation matrix
float[] deltaMatrix = new float[9];
SensorManager.getRotationMatrixFromVector(deltaMatrix, deltaVector);
// apply the new rotation interval on the gyroscope based rotation matrix
gyroMatrix = matrixMultiplication(gyroMatrix, deltaMatrix);
// get the gyroscope based orientation from the rotation matrix
SensorManager.getOrientation(gyroMatrix, gyroOrientation);
}
private float[] getRotationMatrixFromOrientation(float[] o) {
float[] xM = new float[9];
float[] yM = new float[9];
float[] zM = new float[9];
float sinX = (float)Math.sin(o[1]);
float cosX = (float)Math.cos(o[1]);
float sinY = (float)Math.sin(o[2]);
float cosY = (float)Math.cos(o[2]);
float sinZ = (float)Math.sin(o[0]);
float cosZ = (float)Math.cos(o[0]);
// rotation about x-axis (pitch)
xM[0] = 1.0f; xM[1] = 0.0f; xM[2] = 0.0f;
xM[3] = 0.0f; xM[4] = cosX; xM[5] = sinX;
xM[6] = 0.0f; xM[7] = -sinX; xM[8] = cosX;
// rotation about y-axis (roll)
yM[0] = cosY; yM[1] = 0.0f; yM[2] = sinY;
yM[3] = 0.0f; yM[4] = 1.0f; yM[5] = 0.0f;
yM[6] = -sinY; yM[7] = 0.0f; yM[8] = cosY;
// rotation about z-axis (azimuth)
zM[0] = cosZ; zM[1] = sinZ; zM[2] = 0.0f;
zM[3] = -sinZ; zM[4] = cosZ; zM[5] = 0.0f;
zM[6] = 0.0f; zM[7] = 0.0f; zM[8] = 1.0f;
// rotation order is y, x, z (roll, pitch, azimuth)
float[] resultMatrix = matrixMultiplication(xM, yM);
resultMatrix = matrixMultiplication(zM, resultMatrix);
return resultMatrix;
}
private float[] matrixMultiplication(float[] A, float[] B) {
float[] result = new float[9];
result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];
result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];
result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];
return result;
}
class calculateFusedOrientationTask extends TimerTask {
public void run() {
float oneMinusCoeff = 1.0f - FILTER_COEFFICIENT;
/*
* Fix for 179? <--> -179? transition problem:
* Check whether one of the two orientation angles (gyro or accMag) is negative while the other one is positive.
* If so, add 360? (2 * math.PI) to the negative value, perform the sensor fusion, and remove the 360? from the result
* if it is greater than 180?. This stabilizes the output in positive-to-negative-transition cases.
*/
// azimuth
if (gyroOrientation[0] < -0.5 * Math.PI && accMagOrientation[0] > 0.0) {
fusedOrientation[0] = (float) (FILTER_COEFFICIENT * (gyroOrientation[0] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[0]);
fusedOrientation[0] -= (fusedOrientation[0] > Math.PI) ? 2.0 * Math.PI : 0;
}
else if (accMagOrientation[0] < -0.5 * Math.PI && gyroOrientation[0] > 0.0) {
fusedOrientation[0] = (float) (FILTER_COEFFICIENT * gyroOrientation[0] + oneMinusCoeff * (accMagOrientation[0] + 2.0 * Math.PI));
fusedOrientation[0] -= (fusedOrientation[0] > Math.PI)? 2.0 * Math.PI : 0;
}
else {
fusedOrientation[0] = FILTER_COEFFICIENT * gyroOrientation[0] + oneMinusCoeff * accMagOrientation[0];
}
// pitch
if (gyroOrientation[1] < -0.5 * Math.PI && accMagOrientation[1] > 0.0) {
fusedOrientation[1] = (float) (FILTER_COEFFICIENT * (gyroOrientation[1] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[1]);
fusedOrientation[1] -= (fusedOrientation[1] > Math.PI) ? 2.0 * Math.PI : 0;
}
else if (accMagOrientation[1] < -0.5 * Math.PI && gyroOrientation[1] > 0.0) {
fusedOrientation[1] = (float) (FILTER_COEFFICIENT * gyroOrientation[1] + oneMinusCoeff * (accMagOrientation[1] + 2.0 * Math.PI));
fusedOrientation[1] -= (fusedOrientation[1] > Math.PI)? 2.0 * Math.PI : 0;
}
else {
fusedOrientation[1] = FILTER_COEFFICIENT * gyroOrientation[1] + oneMinusCoeff * accMagOrientation[1];
}
// roll
if (gyroOrientation[2] < -0.5 * Math.PI && accMagOrientation[2] > 0.0) {
fusedOrientation[2] = (float) (FILTER_COEFFICIENT * (gyroOrientation[2] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[2]);
fusedOrientation[2] -= (fusedOrientation[2] > Math.PI) ? 2.0 * Math.PI : 0;
}
else if (accMagOrientation[2] < -0.5 * Math.PI && gyroOrientation[2] > 0.0) {
fusedOrientation[2] = (float) (FILTER_COEFFICIENT * gyroOrientation[2] + oneMinusCoeff * (accMagOrientation[2] + 2.0 * Math.PI));
fusedOrientation[2] -= (fusedOrientation[2] > Math.PI)? 2.0 * Math.PI : 0;
}
else {
fusedOrientation[2] = FILTER_COEFFICIENT * gyroOrientation[2] + oneMinusCoeff * accMagOrientation[2];
}
// overwrite gyro matrix and orientation with fused orientation
// to comensate gyro drift
gyroMatrix = getRotationMatrixFromOrientation(fusedOrientation);
System.arraycopy(fusedOrientation, 0, gyroOrientation, 0, 3);
// update sensor output in GUI
mHandler.post(updateOreintationDisplayTask);
}
}
// **************************** GUI FUNCTIONS *********************************
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch(checkedId) {
case R.id.radio0:
radioSelection = 0;
break;
case R.id.radio1:
radioSelection = 1;
break;
case R.id.radio2:
radioSelection = 2;
break;
}
}
public void updateOreintationDisplay() {
switch(radioSelection) {
case 0:
mAzimuthView.setText(d.format(accMagOrientation[0] * 180/Math.PI) + '?');
mPitchView.setText(d.format(accMagOrientation[1] * 180/Math.PI) + '?');
mRollView.setText(d.format(accMagOrientation[2] * 180/Math.PI) + '?');
try {
writeToCsv((d.format(accMagOrientation[0] * 180/Math.PI) + '?'),(d.format(accMagOrientation[1] * 180/Math.PI)+ '?'),(d.format(accMagOrientation[2] * 180/Math.PI) + '?'));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case 1:
mAzimuthView.setText(d.format(gyroOrientation[0] * 180/Math.PI) + '?');
mPitchView.setText(d.format(gyroOrientation[1] * 180/Math.PI) + '?');
mRollView.setText(d.format(gyroOrientation[2] * 180/Math.PI) + '?');
try {
writeToCsv((d.format(gyroOrientation[0] * 180/Math.PI) + '?'),(d.format(gyroOrientation[1] * 180/Math.PI)+ '?'),(d.format(gyroOrientation[2] * 180/Math.PI) + '?'));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case 2:
mAzimuthView.setText(d.format(fusedOrientation[0] * 180/Math.PI) + '?');
mPitchView.setText(d.format(fusedOrientation[1] * 180/Math.PI) + '?');
mRollView.setText(d.format(fusedOrientation[2] * 180/Math.PI) + '?');
try {
writeToCsv((d.format(fusedOrientation[0] * 180/Math.PI) + '?'),(d.format(fusedOrientation[1] * 180/Math.PI) + '?'),(d.format(fusedOrientation[2] * 180/Math.PI) + '?'));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
private void writeToCsv(String x, String y, String z) throws IOException {
Calendar c = Calendar.getInstance();
// File path = getFilesDir();
File folder = new File(getFilesDir() + "/TollCulator");
boolean success = true;
if (! folder.exists()) {
success = folder.mkdir();
}
if (success) {
// Do something on success
String csv = "data.csv";
FileWriter file_writer = new FileWriter(csv,true);
String s= c.get(Calendar.YEAR)+","+c.get(Calendar.MONTH)+","+c.get(Calendar.DATE)+","+c.get(Calendar.HOUR)+","+c.get(Calendar.MINUTE)+","+c.get(Calendar.SECOND)+","+ c.get(Calendar.MILLISECOND)+","+x + ","+y+","+z+"\n";
file_writer.append(s);
file_writer.close();
}
}
private Runnable updateOreintationDisplayTask = new Runnable() {
public void run() {
updateOreintationDisplay();
}
};
}
Try with below code to create csv file and save data into csv.
Reference : https://sourceforge.net/projects/opencsv/files/opencsv/
For more : Look into this
String csv = (Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyCsvFile.csv"); // Here csv file name is MyCsvFile.csv
//by Hiting button csv will create inside phone storage.
buttonAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
CSVWriter writer = null;
try {
writer = new CSVWriter(new FileWriter(csv));
List<String[]> data = new ArrayList<String[]>();
data.add(new String[]{"Country", "Capital"});
data.add(new String[]{"India", "New Delhi"});
data.add(new String[]{"United States", "Washington D.C"});
data.add(new String[]{"Germany", "Berlin"});
writer.writeAll(data); // data is adding to csv
writer.close();
callRead();
} catch (IOException e) {
e.printStackTrace();
}
}
});
I have array of 2d points and I need to create a Path that passes through all of the points. I think I should use Path.cubicTo() method which creates bezier curve between two points using specified control points. The problem is that I don't know control points of my curve. How do I calculate them?
Maybe there's a better way of doing this? Maybe there's some sort of library that could help me?
After I read this articles it became quite simple.
This is how you do it on android. After you run this code your path p will go through all points from knotsArr array.
Point[] knotsArr = {new Point(0, 0),
new Point(5, 5),
new Point(10, 0),
new Point(15, 5)};
Point[][] controlPoints = BezierSplineUtil.getCurveControlPoints(knotsArr);
Point[] firstCP = controlPoints[0];
Point[] secondCP = controlPoints[1];
Path p = new Path();
p.moveTo(knots.get(0).x, knots.get(0).y);
for (int i = 0; i < firstCP.length; i++) {
p.cubicTo(firstCP[i].x, firstCP[i].y,
secondCP[i].x, secondCP[i].y,
knots.get(i + 1).x, knots.get(i + 1).y);
}
BezierSplineUtil.java
public class BezierSplineUtil {
public static class Point {
public final float x;
public final float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
}
/**
* Get open-ended bezier spline control points.
*
* #param knots bezier spline points.
* #return [2 x knots.length - 1] matrix. First row of the matrix = first
* control points. Second row of the matrix = second control points.
* #throws IllegalArgumentException if less than two knots are passed.
*/
public static Point[][] getCurveControlPoints(Point[] knots) {
if (knots == null || knots.length < 2) {
throw new IllegalArgumentException("At least two knot points are required");
}
final int n = knots.length - 1;
final Point[] firstControlPoints = new Point[n];
final Point[] secondControlPoints = new Point[n];
// Special case: bezier curve should be a straight line
if (n == 1) {
// 3P1 = 2P0 + P3
float x = (2 * knots[0].x + knots[1].x) / 3;
float y = (2 * knots[0].y + knots[1].y) / 3;
firstControlPoints[0] = new Point(x, y);
// P2 = 2P1 - P0
x = 2 * firstControlPoints[0].x - knots[0].x;
y = 2 * firstControlPoints[0].y - knots[0].y;
secondControlPoints[0] = new Point(x, y);
return new Point[][] { firstControlPoints, secondControlPoints };
}
// Calculate first bezier control points
// Right hand side vector
float[] rhs = new float[n];
// Set right hand side X values
for (int i = 1; i < n - 1; i++) {
rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
}
rhs[0] = knots[0].x + 2 * knots[1].x;
rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2f;
// Get first control points X-values
float[] x = getFirstControlPoints(rhs);
// Set right hand side Y values
for (int i = 1; i < n - 1; i++) {
rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
}
rhs[0] = knots[0].y + 2 * knots[1].y;
rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2f;
// Get first control points Y-values
float[] y = getFirstControlPoints(rhs);
for (int i = 0; i < n; i++) {
// First control point
firstControlPoints[i] = new Point(x[i], y[i]);
// Second control point
if (i < n - 1) {
float xx = 2 * knots[i + 1].x - x[i + 1];
float yy = 2 * knots[i + 1].y - y[i + 1];
secondControlPoints[i] = new Point(xx, yy);
} else {
float xx = (knots[n].x + x[n - 1]) / 2;
float yy = (knots[n].y + y[n - 1]) / 2;
secondControlPoints[i] = new Point(xx, yy);
}
}
return new Point[][] { firstControlPoints, secondControlPoints };
}
/**
* Solves a tridiagonal system for one of coordinates (x or y) of first
* bezier control points.
*
* #param rhs right hand side vector.
* #return Solution vector.
*/
private static float[] getFirstControlPoints(float[] rhs) {
int n = rhs.length;
float[] x = new float[n]; // Solution vector
float[] tmp = new float[n]; // Temp workspace
float b = 2.0f;
x[0] = rhs[0] / b;
// Decomposition and forward substitution
for (int i = 1; i < n; i++) {
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0f : 3.5f) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
// Backsubstitution
for (int i = 1; i < n; i++) {
x[n - i - 1] -= tmp[n - i] * x[n - i];
}
return x;
}
}
I'm trying to animate some drawables in Android, I've set a path using PathEvaluator that animates along some curves along a full path.
When I set a duration (e.g. 6 seconds) it splits the duration to the number of curves I've set regardless of their length which causes the animation to be to slow on some segments and too fast on others.
On iOS this can be fixed using
animation.calculationMode = kCAAnimationCubicPaced;
animation.timingFunction = ...;
Which lets iOS to smooth your entire path into mid-points and span the duration according to each segment length.
Is there any way to get the same result in Android?
(besides breaking the path into discrete segments and assigning each segment its own duration manually which is really ugly and unmaintainable).
I don't think that anything can be done with ObjectAnimator because there seems to be no function that can be called to assert the relative duration of a certain fragment of the animation.
I did develop something similar to what you need a while back, but it works slightly differently - it inherits from Animation.
I've modified everything to work with your curving needs, and with the PathPoint class.
Here's an overview:
I supply the list of points to the animation in the constructor.
I calculate the length between all the points using a simple distance calculator. I then sum it all up to get the overall length of the path, and store the segment lengths in a map for future use (this is to improve efficiency during runtime).
When animating, I use the current interpolation time to figure out which 2 points I'm animating between, considering the ratio of time & the ratio of distance traveled.
I calculate the time it should take to animate between these 2 points according to the relative distance between them, compared to the overall distance.
I then interpolate separately between these 2 points using the calculation in the PathAnimator class.
Here's the code:
CurveAnimation.java:
public class CurveAnimation extends Animation
{
private static final float BEZIER_LENGTH_ACCURACY = 0.001f; // Must be divisible by one. Make smaller to improve accuracy, but will increase runtime at start of animation.
private List<PathPoint> mPathPoints;
private float mOverallLength;
private Map<PathPoint, Double> mSegmentLengths = new HashMap<PathPoint, Double>(); // map between the end point and the length of the path to it.
public CurveAnimation(List<PathPoint> pathPoints)
{
mPathPoints = pathPoints;
if (mPathPoints == null || mPathPoints.size() < 2)
{
Log.e("CurveAnimation", "There must be at least 2 points on the path. There will be an exception soon!");
}
calculateOverallLength();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
PathPoint[] startEndPart = getStartEndForTime(interpolatedTime);
PathPoint startPoint = startEndPart[0];
PathPoint endPoint = startEndPart[1];
float startTime = getStartTimeOfPoint(startPoint);
float endTime = getStartTimeOfPoint(endPoint);
float progress = (interpolatedTime - startTime) / (endTime - startTime);
float x, y;
float[] xy;
if (endPoint.mOperation == PathPoint.CURVE)
{
xy = getBezierXY(startPoint, endPoint, progress);
x = xy[0];
y = xy[1];
}
else if (endPoint.mOperation == PathPoint.LINE)
{
x = startPoint.mX + progress * (endPoint.mX - startPoint.mX);
y = startPoint.mY + progress * (endPoint.mY - startPoint.mY);
}
else
{
x = endPoint.mX;
y = endPoint.mY;
}
t.getMatrix().setTranslate(x, y);
super.applyTransformation(interpolatedTime, t);
}
private PathPoint[] getStartEndForTime(float time)
{
double length = 0;
if (time == 1)
{
return new PathPoint[] { mPathPoints.get(mPathPoints.size() - 2), mPathPoints.get(mPathPoints.size() - 1) };
}
PathPoint[] result = new PathPoint[2];
for (int i = 0; i < mPathPoints.size() - 1; i++)
{
length += calculateLengthFromIndex(i);
if (length / mOverallLength >= time)
{
result[0] = mPathPoints.get(i);
result[1] = mPathPoints.get(i + 1);
break;
}
}
return result;
}
private float getStartTimeOfPoint(PathPoint point)
{
float result = 0;
int index = 0;
while (mPathPoints.get(index) != point && index < mPathPoints.size() - 1)
{
result += (calculateLengthFromIndex(index) / mOverallLength);
index++;
}
return result;
}
private void calculateOverallLength()
{
mOverallLength = 0;
mSegmentLengths.clear();
double segmentLength;
for (int i = 0; i < mPathPoints.size() - 1; i++)
{
segmentLength = calculateLengthFromIndex(i);
mSegmentLengths.put(mPathPoints.get(i + 1), segmentLength);
mOverallLength += segmentLength;
}
}
private double calculateLengthFromIndex(int index)
{
PathPoint start = mPathPoints.get(index);
PathPoint end = mPathPoints.get(index + 1);
return calculateLength(start, end);
}
private double calculateLength(PathPoint start, PathPoint end)
{
if (mSegmentLengths.containsKey(end))
{
return mSegmentLengths.get(end);
}
else if (end.mOperation == PathPoint.LINE)
{
return calculateLength(start.mX, end.mX, start.mY, end.mY);
}
else if (end.mOperation == PathPoint.CURVE)
{
return calculateBezeirLength(start, end);
}
else
{
return 0;
}
}
private double calculateLength(float x0, float x1, float y0, float y1)
{
return Math.sqrt(((x0 - x1) * (x0 - x1)) + ((y0 - y1) * (y0 - y1)));
}
private double calculateBezeirLength(PathPoint start, PathPoint end)
{
double result = 0;
float x, y, x0, y0;
float[] xy;
x0 = start.mX;
y0 = start.mY;
for (float progress = BEZIER_LENGTH_ACCURACY; progress <= 1; progress += BEZIER_LENGTH_ACCURACY)
{
xy = getBezierXY(start, end, progress);
x = xy[0];
y = xy[1];
result += calculateLength(x, x0, y, y0);
x0 = x;
y0 = y;
}
return result;
}
private float[] getBezierXY(PathPoint start, PathPoint end, float progress)
{
float[] result = new float[2];
float oneMinusT, x, y;
oneMinusT = 1 - progress;
x = oneMinusT * oneMinusT * oneMinusT * start.mX +
3 * oneMinusT * oneMinusT * progress * end.mControl0X +
3 * oneMinusT * progress * progress * end.mControl1X +
progress * progress * progress * end.mX;
y = oneMinusT * oneMinusT * oneMinusT * start.mY +
3 * oneMinusT * oneMinusT * progress * end.mControl0Y +
3 * oneMinusT * progress * progress * end.mControl1Y +
progress * progress * progress * end.mY;
result[0] = x;
result[1] = y;
return result;
}
}
Here's a sample that shows how to activate the animation:
private void animate()
{
AnimatorPath path = new AnimatorPath();
path.moveTo(0, 0);
path.lineTo(0, 300);
path.curveTo(100, 0, 300, 900, 400, 500);
CurveAnimation animation = new CurveAnimation(path.mPoints);
animation.setDuration(5000);
animation.setInterpolator(new LinearInterpolator());
btn.startAnimation(animation);
}
Now, keep in mind that I'm currently calculating the length of the curve according to an approximation. This will obviously cause some mild inaccuracies in the speed. If you feel it's not accurate enough, feel free to modify the code. Also, if you want to increase the length accuracy of the curve, try decreasing the value of BEZIER_LENGTH_ACCURACY. It must be dividable by 1, so accepted values can be 0.001, 0.000025, etc.
While you might notice some mild fluctuations in speed when using curves, I'm sure it's much better than simply dividing the time equally between all paths.
I hope this helps :)
I tried using Gil's answer, but it didn't fit how I was animating.
Gil wrote an Animation class which is used to animate Views.
I was using ObjectAnimator.ofObject() to animate custom classes using ValueProperties which can't be used with custom Animation.
So this is what I did:
I extend PathEvaluator and override its evaluate method.
I use Gil's logic to calculate path total length, and segmented lengths
Since PathEvaluator.evaluate is called for each PathPoint with t values
0..1, I needed to normalize the interpolated time given to me, so it'll be incremental and won't zero out for each segment.
I ignore the start/end PathPoints given to me so the current position can be
before start or after end along the path depending on the segment's duration.
I pass the current progress calculated to my super
(PathEvaluator) to calc the actual position.
This is the code:
public class NormalizedEvaluator extends PathEvaluator {
private static final float BEZIER_LENGTH_ACCURACY = 0.001f;
private List<PathPoint> mPathPoints;
private float mOverallLength;
private Map<PathPoint, Double> mSegmentLengths = new HashMap<PathPoint, Double>();
public NormalizedEvaluator(List<PathPoint> pathPoints) {
mPathPoints = pathPoints;
if (mPathPoints == null || mPathPoints.size() < 2) {
Log.e("CurveAnimation",
"There must be at least 2 points on the path. There will be an exception soon!");
}
calculateOverallLength();
}
#Override
public PathPoint evaluate(float interpolatedTime, PathPoint ignoredStartPoint,
PathPoint ignoredEndPoint) {
float index = getStartIndexOfPoint(ignoredStartPoint);
float normalizedInterpolatedTime = (interpolatedTime + index) / (mPathPoints.size() - 1);
PathPoint[] startEndPart = getStartEndForTime(normalizedInterpolatedTime);
PathPoint startPoint = startEndPart[0];
PathPoint endPoint = startEndPart[1];
float startTime = getStartTimeOfPoint(startPoint);
float endTime = getStartTimeOfPoint(endPoint);
float progress = (normalizedInterpolatedTime - startTime) / (endTime - startTime);
return super.evaluate(progress, startPoint, endPoint);
}
private PathPoint[] getStartEndForTime(float time) {
double length = 0;
if (time == 1) {
return new PathPoint[] { mPathPoints.get(mPathPoints.size() - 2),
mPathPoints.get(mPathPoints.size() - 1) };
}
PathPoint[] result = new PathPoint[2];
for (int i = 0; i < mPathPoints.size() - 1; i++) {
length += calculateLengthFromIndex(i);
if (length / mOverallLength >= time) {
result[0] = mPathPoints.get(i);
result[1] = mPathPoints.get(i + 1);
break;
}
}
return result;
}
private float getStartIndexOfPoint(PathPoint point) {
for (int ii = 0; ii < mPathPoints.size(); ii++) {
PathPoint current = mPathPoints.get(ii);
if (current == point) {
return ii;
}
}
return -1;
}
private float getStartTimeOfPoint(PathPoint point) {
float result = 0;
int index = 0;
while (mPathPoints.get(index) != point && index < mPathPoints.size() - 1) {
result += (calculateLengthFromIndex(index) / mOverallLength);
index++;
}
return result;
}
private void calculateOverallLength() {
mOverallLength = 0;
mSegmentLengths.clear();
double segmentLength;
for (int i = 0; i < mPathPoints.size() - 1; i++) {
segmentLength = calculateLengthFromIndex(i);
mSegmentLengths.put(mPathPoints.get(i + 1), segmentLength);
mOverallLength += segmentLength;
}
}
private double calculateLengthFromIndex(int index) {
PathPoint start = mPathPoints.get(index);
PathPoint end = mPathPoints.get(index + 1);
return calculateLength(start, end);
}
private double calculateLength(PathPoint start, PathPoint end) {
if (mSegmentLengths.containsKey(end)) {
return mSegmentLengths.get(end);
} else if (end.mOperation == PathPoint.LINE) {
return calculateLength(start.mX, end.mX, start.mY, end.mY);
} else if (end.mOperation == PathPoint.CURVE) {
return calculateBezeirLength(start, end);
} else {
return 0;
}
}
private double calculateLength(float x0, float x1, float y0, float y1) {
return Math.sqrt(((x0 - x1) * (x0 - x1)) + ((y0 - y1) * (y0 - y1)));
}
private double calculateBezeirLength(PathPoint start, PathPoint end) {
double result = 0;
float x, y, x0, y0;
float[] xy;
x0 = start.mX;
y0 = start.mY;
for (float progress = BEZIER_LENGTH_ACCURACY; progress <= 1; progress += BEZIER_LENGTH_ACCURACY) {
xy = getBezierXY(start, end, progress);
x = xy[0];
y = xy[1];
result += calculateLength(x, x0, y, y0);
x0 = x;
y0 = y;
}
return result;
}
private float[] getBezierXY(PathPoint start, PathPoint end, float progress) {
float[] result = new float[2];
float oneMinusT, x, y;
oneMinusT = 1 - progress;
x = oneMinusT * oneMinusT * oneMinusT * start.mX + 3 * oneMinusT * oneMinusT * progress
* end.mControl0X + 3 * oneMinusT * progress * progress * end.mControl1X + progress
* progress * progress * end.mX;
y = oneMinusT * oneMinusT * oneMinusT * start.mY + 3 * oneMinusT * oneMinusT * progress
* end.mControl0Y + 3 * oneMinusT * progress * progress * end.mControl1Y + progress
* progress * progress * end.mY;
result[0] = x;
result[1] = y;
return result;
}
}
This is the usage:
NormalizedEvaluator evaluator = new NormalizedEvaluator((List<PathPoint>) path.getPoints());
ObjectAnimator anim = ObjectAnimator.ofObject(object, "position", evaluator, path.getPoints().toArray());
UPDATE: I just realized that I might have reinvented the wheel, please look at Specifying Keyframes.
It is shocking to see that nothing is available of this kind. Anyways if you don't want to calculate path length at run time then I was able to add functionality of assigning weights to paths. Idea is to assign a weight to your path and run the animation if it feels OK then well and good otherwise just decrease or increase weight assigned to each Path.
Following code is modified code from official Android sample that you pointed in your question:
// Set up the path we're animating along
AnimatorPath path = new AnimatorPath();
path.moveTo(0, 0).setWeight(0);
path.lineTo(0, 300).setWeight(30);// assign arbitrary weight
path.curveTo(100, 0, 300, 900, 400, 500).setWeight(70);// assign arbitrary weight
final PathPoint[] points = path.getPoints().toArray(new PathPoint[] {});
mFirstKeyframe = points[0];
final int numFrames = points.length;
final PathEvaluator pathEvaluator = new PathEvaluator();
final ValueAnimator anim = ValueAnimator.ofInt(0, 1);// dummy values
anim.setDuration(1000);
anim.setInterpolator(new LinearInterpolator());
anim.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
// Special-case optimization for the common case of only two
// keyframes
if (numFrames == 2) {
PathPoint nextPoint = pathEvaluator.evaluate(fraction,
points[0], points[1]);
setButtonLoc(nextPoint);
} else {
PathPoint prevKeyframe = mFirstKeyframe;
for (int i = 1; i < numFrames; ++i) {
PathPoint nextKeyframe = points[i];
if (fraction < nextKeyframe.getFraction()) {
final float prevFraction = prevKeyframe
.getFraction();
float intervalFraction = (fraction - prevFraction)
/ (nextKeyframe.getFraction() - prevFraction);
PathPoint nextPoint = pathEvaluator.evaluate(
intervalFraction, prevKeyframe,
nextKeyframe);
setButtonLoc(nextPoint);
break;
}
prevKeyframe = nextKeyframe;
}
}
}
});
And that's it !!!.
Of course I modified other classes as well but nothing big was added. E.g. in PathPoint I added this:
float mWeight;
float mFraction;
public void setWeight(float weight) {
mWeight = weight;
}
public float getWeight() {
return mWeight;
}
public void setFraction(float fraction) {
mFraction = fraction;
}
public float getFraction() {
return mFraction;
}
In AnimatorPath I modified getPoints() method like this:
public Collection<PathPoint> getPoints() {
// calculate fractions
float totalWeight = 0.0F;
for (PathPoint p : mPoints) {
totalWeight += p.getWeight();
}
float lastWeight = 0F;
for (PathPoint p : mPoints) {
p.setFraction(lastWeight = lastWeight + p.getWeight() / totalWeight);
}
return mPoints;
}
And thats pretty much it. Oh and for better readability I added Builder Pattern in AnimatorPath, so all 3 methods were changed like this:
public PathPoint moveTo(float x, float y) {// same for lineTo and curveTo method
PathPoint p = PathPoint.moveTo(x, y);
mPoints.add(p);
return p;
}
NOTE: To handle Interpolators that can give fraction less then 0 or greater than 1 (e.g. AnticipateOvershootInterpolator) look at com.nineoldandroids.animation.KeyframeSet.getValue(float fraction) method and implement the logic in onAnimationUpdate(ValueAnimator animation).
How can we convert an Int value to an angle.
int speed = remoteService.getSpeed();
I am getting the speed value from a remote service and I want to convert it to an angle.
How can I do this? Any Idea?
public void getGenginePos(int state,float force, double AOD){
double AODrad=(AOD*0.017444);
switch(state){
case BOAT_IDLE:
//System.out.println("Before Vx = " + vx + ", vy = " + vy + ", f = " + force + ", AOD = " + AOD);
vx = (float)(force * Math.cos(AODrad));
px = px + (vx * dt);
vy = (float) (force * Math.sin(AODrad));
//System.out.println("After Vx = " + vx + ", vy = " + vy);
py = py - (vy * dt);
break;
case BOAT_ACCEL:
temp = force *dt;
vx = (float) (force * Math.cos(AODrad) + temp);//(force * dt));
vy = (float) (force * Math.sin(AODrad) + temp);//(force * dt));
px = px + (vx * dt);
py = py - (vy * dt);
break;
case BOAT_DECEL:
temp = force *dt;
vx = (float) (force * Math.cos(AODrad) - temp);//(force * dt));
vy = (float) (force * Math.sin(AODrad) - temp);//(force * dt));
px = px + (vx * dt);
py = py - (vy * dt);
break;
default: break;
}
}
public void setMeterPos(int rpx,int rpy,int epx,int epy){
RefX= rpx;
RefY= rpy;
EndX = epx;
EndY = epy;
screenwidth=BoatRider.screenWidth;
screenheight=BoatRider.screenHeight;
}
public void setArrowEndX(int x){
EndX = x;
}
public void setArrowEndY(int y){
EndY = y;
}
public float getArrowEndX(){
return EndX;
}
public float getArrowEndY(){
return EndY;
}
public void getGMeterArrowPos(double AOD,float radius){
double AODrad=(AOD*0.017444);
vx=(float)(radius*Math.cos(AODrad));
vy=(float)(radius*Math.sin(AODrad));
float height=screenheight-RefY;
EndX = vx+RefX;
EndY=screenheight-(vy+height);
}
public float getBorderEndX(){
return EndX;
}
public float getBorderEndY(){
return EndY;
}
public void getGBoatBorderPos(double AOD,float radius,float boderRefX,float boderRefY){
double AODrad=(AOD*0.017444);
vx=(float)(radius*Math.cos(AODrad));
vy=(float)(radius*Math.sin(AODrad));
float height=screenheight-boderRefY;
EndX = vx+boderRefX;
EndY=screenheight-(vy+height);
}
}
it will be very useful
int speed = 90;
double degrees = speed ;
double angle = degrees * 2 * Math.PI / 360.0;
Or you can use
int speed = 30;
double degrees = speed;
double toDegree = Math.toDegrees(radians);