Seeing as Valentine's Day is fast approaching, I decided to create a heart. So I found this heart from mathematica.se:
I played around in Mathematica (solved for z, switching some variables around) to get this equation for the z-value of the heart, given the x and y values (click for full-size):
I faithfully ported this equation to Java, dealing with a couple out-of-bounds cases:
import static java.lang.Math.cbrt;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
...
public static double heart(double xi, double yi) {
double x = xi;
double y = -yi;
double temp = 5739562800L * pow(y, 3) + 109051693200L * pow(x, 2) * pow(y, 3)
- 5739562800L * pow(y, 5);
double temp1 = -244019119519584000L * pow(y, 9) + pow(temp, 2);
//
if (temp1 < 0) {
return -1; // this is one possible out of bounds location
// this spot is the location of the problem
}
//
double temp2 = sqrt(temp1);
double temp3 = cbrt(temp + temp2);
if (temp3 != 0) {
double part1 = (36 * cbrt(2) * pow(y, 3)) / temp3;
double part2 = 1 / (10935 * cbrt(2)) * temp3;
double looseparts = 4.0 / 9 - 4.0 / 9 * pow(x, 2) - 4.0 / 9 * pow(y, 2);
double sqrt_body = looseparts + part1 + part2;
if (sqrt_body >= 0) {
return sqrt(sqrt_body);
} else {
return -1; // this works; returns -1 if we are outside the heart
}
} else {
// through trial and error, I discovered that this should
// be an ellipse (or that it is close enough)
return Math.sqrt(Math.pow(2.0 / 3, 2) * (1 - Math.pow(x, 2)));
}
}
The only problem is that when temp1 < 0, I cannot simply return -1, like I do:
if (temp1 < 0) {
return -1; // this is one possible out of bounds location
// this spot is the location of the problem
}
That's not the behavior of the heart at that point. As it is, when I try to make my image:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import static java.lang.Math.cbrt;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
public class Heart {
public static double scale(int x, int range, double l, double r) {
double width = r - l;
return (double) x / (range - 1) * width + l;
}
public static void main(String[] args) throws IOException {
BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);
// this is actually larger than the max heart value
final double max_heart = 0.679;
double max = 0.0;
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
double xv = scale(x, img.getWidth(), -1.2, 1.2);
double yv = scale(y, img.getHeight(), -1.3, 1);
double heart = heart(xv, yv); //this isn't an accident
// yes I don't check for the return of -1, but still
// the -1 values return a nice shade of pink: 0xFFADAD
// None of the other values should be negative, as I did
// step through from -1000 to 1000 in python, and there
// were no negatives that were not -1
int r = 0xFF;
int gb = (int) (0xFF * (max_heart - heart));
int rgb = (r << 16) | (gb << 8) | gb;
img.setRGB(x, y, rgb);
}
}
ImageIO.write(img, "png", new File("location"));
}
// heart function clipped; it belongs here
}
I get this:
Look at that dip at the top! I tried changing that problematic -1 to a .5, resulting in this:
Now the heart has horns. But it becomes clear where that problematic if's condition is met.
How can I fix this problem? I don't want a hole in my heart at the top, and I don't want a horned heart. If I could clip the horns to the shape of a heart, and color the rest appropriately, that would be perfectly fine. Ideally, the two sides of the heart would come together as a point (hearts have a little point at the join), but if they curve together like shown in the horns, that would be fine too. How can I achieve this?
The problem is simple. If we look at that horseshoe region, we get imaginary numbers. For part of it, it should belong to our heart. In that region, if we were to evaluate our function (by math, not by programming), the imaginary parts of the function cancel. So it should look like this (generated in Mathematica):
Basically, the function for that part is almost identical; we just have to do arithmetic with complex numbers instead of real numbers. Here's a function that does exactly that:
private static double topOfHeart(double x, double y, double temp, double temp1) {
//complex arithmetic; each double[] is a single number
double[] temp3 = cbrt_complex(temp, sqrt(-temp1));
double[] part1 = polar_reciprocal(temp3);
part1[0] *= 36 * cbrt(2) * pow(y, 3);
double[] part2 = temp3;
part2[0] /= (10935 * cbrt(2));
toRect(part1, part2);
double looseparts = 4.0 / 9 - 4.0 / 9 * pow(x, 2) - 4.0 / 9 * pow(y, 2);
double real_part = looseparts + part1[0] + part2[0];
double imag_part = part1[1] + part2[1];
double[] result = sqrt_complex(real_part, imag_part);
toRect(result);
// theoretically, result[1] == 0 should work, but floating point says otherwise
if (Math.abs(result[1]) < 1e-5) {
return result[0];
}
return -1;
}
/**
* returns a specific cuberoot of this complex number, in polar form
*/
public static double[] cbrt_complex(double a, double b) {
double r = Math.hypot(a, b);
double theta = Math.atan2(b, a);
double cbrt_r = cbrt(r);
double cbrt_theta = 1.0 / 3 * (2 * PI * Math.floor((PI - theta) / (2 * PI)) + theta);
return new double[]{cbrt_r, cbrt_theta};
}
/**
* returns a specific squareroot of this complex number, in polar form
*/
public static double[] sqrt_complex(double a, double b) {
double r = Math.hypot(a, b);
double theta = Math.atan2(b, a);
double sqrt_r = Math.sqrt(r);
double sqrt_theta = 1.0 / 2 * (2 * PI * Math.floor((PI - theta) / (2 * PI)) + theta);
return new double[]{sqrt_r, sqrt_theta};
}
public static double[] polar_reciprocal(double[] polar) {
return new double[]{1 / polar[0], -polar[1]};
}
public static void toRect(double[]... polars) {
for (double[] polar: polars) {
double a = Math.cos(polar[1]) * polar[0];
double b = Math.sin(polar[1]) * polar[0];
polar[0] = a;
polar[1] = b;
}
}
To join this with your program, simply change your function to reflect this:
if (temp1 < 0) {
return topOfHeart(x, y, temp, temp1);
}
And running it, we get the desired result:
It should be pretty clear that this new function implements exactly the same formula. But how does each part work?
double[] temp3 = cbrt_complex(temp, sqrt(-temp1));
cbrt_complex takes a complex number in the form of a + b i. That's why the second argument is simply sqrt(-temp1) (notice that temp1 < 0, so I use - instead of Math.abs; Math.abs is probably a better idea). cbrt_complex returns the cube root of the complex number, in polar form: r eiθ. We can see from wolframalpha that with positive r and θ, we can write an n-th root of a complex numbers as follows:
And that's exactly how the code for the cbrt_complex and sqrt_complex work. Note that both take a complex number in rectangular coordinates (a + b i) and return a complex number in polar coordinates (r eiθ)
double[] part1 = polar_reciprocal(temp3);
It is easier to take the reciprocal of a polar complex number than a rectangular complex number. If we have r eiθ, its reciprocal (this follows standard power rules, luckily) is simply 1/r e-iθ. This is actually why we are staying in polar form; polar form makes multiplication-type operations easier, and addition type operations harder, while rectangular form does the opposite.
Notice that if we have a polar complex number r eiθ and we want to multiply by a real number d, the answer is as simple as d r eiθ.
The toRect function does exactly what it seems like it does: it converts polar coordinate complex numbers to rectangular coordinate complex numbers.
You may have noticed that the if statement doesn't check that there is no imaginary part, but only if the imaginary part is really small. This is because we are using floating point numbers, so checking result[1] == 0 will likely fail.
And there you are! Notice that we could actually implement the entire heart function with this complex number arithmetic, but it's probably faster to avoid this.
Related
I am trying to calculate sine of an angle without using the Math.sin(). I got stuck in it's equation as I keep getting the wrong results
note I have a method that changes the angle from degrees to radians
public static double sin(double x, int precision) {
//this method is simply the sine function
double answer = 1, power = 1;
int n = 2,factorial = 1;
while (n<=precision) {
power = (power * x * x *-1) +1 ;
factorial = (factorial * (n +1))* (n-1);
answer = answer + ((power/factorial ));
n = n + 2;
}
return answer;
}
It looks like you're attempting to calculate the sine of angle given in radians using the Maclaurin series, a special case of Taylor series.
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
Your initial answer is 1 when it should be x. Your initial power is 1 when it should be x also.
double answer = x, power = x;
For some reason you're adding one to the power part of the result when you shouldn't be.
power = (power * x * x * -1);
You'll also need to fix your factorial calculation. Multiply by n + 1 and n, not n + 1 and n - 1.
factorial = (factorial * (n + 1)) * (n);
With these fixes, testing:
for (double angle = 0; angle <= Math.PI; angle += Math.PI / 4)
{
System.out.println("sin(" + angle + ") = " + sin(angle, 10));
}
The results are pretty good considering the limitations of precision for floating point arithmetic.
sin(0.0) = 0.0
sin(0.7853981633974483) = 0.7071067811796194
sin(1.5707963267948966) = 0.999999943741051
sin(2.356194490192345) = 0.7070959900908971
sin(3.141592653589793) = -4.4516023820965686E-4
Note that this will get more inaccurate as the values of x get larger, not just because of the inaccuracy to represent pi, but also because of the floating point calculations for adding and subtracting large values.
I'm attempting to implement the Adaptive function plotting algorithm using the psuedocode from these two examples (both examples the same really)
https://www.andr.mu/logs/acquiring-samples-to-plot-a-math-function-adaptive/
http://yacas.readthedocs.io/en/latest/book_of_algorithms/basic.html
The problems I've encountered (from my probably incorrect implementation) are:
(1) Duplicated coordinates are being created. I know this is because of the splitting, where both ends are kept for each spilt, so the end of one interval is the start of the other interval - same x value evaluated twice. But is there a way to setup the algorithm to avoid deplicated coordinates. I could avoid adding the start or end coordinate as a dirty fix (see comment in code below) but then I'd be missing one them for the whole interval.
(2) Some parts of the plot are missing coordinates for what is essentially a symmetrical function, which is strange. The algorithm should work the same way for both sides, yet it doesn't. This starts to happen when depth >= 6, it does an extra split to the right side of the function and nothing for the left side around the origin. The rest of the coordinates away from the origin seem to match up. The right side seems to get more splits than the left side overall.
Problem (2)
My Implementation of algorithm
static List<Double[]> linePoints;
public static void main(String[] args) {
linePoints = new ArrayList<>();
double startX = -50;
double endX = 50;
sampling(startX, endX, depth, tolerance);
/* Print all points to be plotted - x,y coordinates */
for (Double[] point : linePoints) {
System.out.println(point[0]+","+point[1]);
}
}
/* math function */
public static double f(double x){
return x*Math.sin(x);
}
static int depth = 6; /* 8 */
static double tolerance = 0.005; /* just a guess */
/* Adaptive sampling algorithm */
/* mostly followed along 2st website and used 1st website variable names */
public static void sampling(double xa, double xc, int depth, double tolerance){
/* step (1) of 2nd website - determine mid-intervals */
double xb = (xa+xc)/2; /* (xc-xa)/2; tried these from 1st website - didn't work out */
double xab = (xa+xb)/2; /* (xb-xa)/2; */
double xbc = (xb+xc)/2; /* (xc-xb)/2; */
/* evaluate the above points using math function - store in array */
double[] points = new double[5];
points[0] = f(xa); points[1] = f(xab); points[2] = f(xb); points[3] = f(xbc); points[4] = f(xc);
/* step (2) of 2nd website */
if (depth <= 0){
linePoints.add(new Double[]{xa, points[0]}); /* either I comment out this line for dirty fix */
linePoints.add(new Double[]{xab, points[1]});
linePoints.add(new Double[]{xb, points[2]});
linePoints.add(new Double[]{xbc, points[3]});
linePoints.add(new Double[]{xc, points[4]}); /* or comment out this line */
} else {
/* step (3) of 2nd website */
int counter = 0;
for (int i = 1; i < points.length-1; i++){
/* Check if prev, current, next values are infinite or NaN */
if ( (Double.isInfinite(points[i-1]) || Double.isNaN(points[i-1])) ||
(Double.isInfinite(points[i]) || Double.isNaN(points[i])) ||
(Double.isInfinite(points[i+1]) || Double.isNaN(points[i+1]))){
counter++;
continue;
}
/* Determine the fluctuations - if current is < or > both it's left/right neighbours */
boolean middleLarger = (points[i] > points[i-1]) && (points[i] > points[i+1]);
boolean middleSmaller = (points[i] < points[i-1]) && (points[i] < points[i+1]);
if (middleLarger || middleSmaller){
counter++;
}
}
if (counter <= 2){ /* at most 2 */
/* Newton-Cotes quadratures - check if smooth enough */
double f1 = (3d/8d)*points[0]+(19d/24d)*points[1]-(5d/24d)*points[2]+(1d/24d)*points[3]; /* add 'd' to end of number, otherwise get 0 always */
double f2 = (5d/12d)*points[2]+(2d/3d)*points[3]-(1d/12d)*points[4];
if (Math.abs(f1-f2) < tolerance * f2){
linePoints.add(new Double[]{xa, points[0]});
linePoints.add(new Double[]{xab, points[1]});
linePoints.add(new Double[]{xb, points[2]});
linePoints.add(new Double[]{xbc, points[3]});
linePoints.add(new Double[]{xc, points[4]});
} else {
/* not smooth enough - needs more refinement */
depth--;
tolerance *= 2;
sampling(xa, xb, depth, tolerance);
sampling(xb, xc, depth, tolerance);
}
} else {
/* else (count > 2), that means further splittings are needed to produce more accurate samples */
depth--;
tolerance *= 2;
sampling(xa, xb, depth, tolerance);
sampling(xb, xc, depth, tolerance);
}
}
}
FIX - Modifications to my code
Looking at Gene's example and multiplying the tolerance by 0.5 instead of 2 seemed to fix problem (2)
Genes example is a better and cleaner implementation of this algorithm and handles the duplicated coordinates
I think you've implemented faithfully, but the algorithm is broken. Asymmetric quadrature can easily give asymmetric results. I get the same kinds of weirdness.
But you can eliminate duplicate points by maintaining them in a sorted set and using the invariant that the endpoints have already been inserted when the recursive analyzer runs.
Here's a simplification using modern Java features more fully:
import static java.lang.Double.compare;
import static java.lang.Double.isFinite;
import static java.lang.Math.PI;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.DoubleUnaryOperator;
import static java.util.stream.Collectors.toList;
import java.util.stream.DoubleStream;
public class AdaptivePlot {
private final DoubleUnaryOperator f;
private final double a;
private final double c;
private final SortedSet<Point> plot = new TreeSet<>((s, t) -> compare(s.x, t.x));
public AdaptivePlot(DoubleUnaryOperator f, double a, double c) {
this.f = f;
this.a = a;
this.c = c;
}
public static class Point {
final double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
public AdaptivePlot computePlot(int depth, double eps) {
plot.clear();
Point pa = pointAt(a);
Point pc = pointAt(c);
plot.add(pa);
plot.add(pc);
computePlot(pa, pc, depth, eps);
return this;
}
public List<Point> getPlot() {
return plot.stream().collect(toList());
}
private Point pointAt(double x) {
return new Point(x, f.applyAsDouble(x));
}
private void computePlot(Point pa, Point pc, int depth, double eps) {
Point pb = pointAt(0.5 * (pa.x + pc.x));
Point pa1 = pointAt(0.5 * (pa.x + pb.x));
Point pb1 = pointAt(0.5 * (pb.x + pc.x));
plot.add(pb);
if (depth > 0 &&
(oscillates(pa.y, pa1.y, pb.y, pb1.y, pc.y)
|| unsmooth(pa.y, pa1.y, pb.y, pb1.y, pc.y, eps))) {
computePlot(pa, pb, depth - 1, 2 * eps);
computePlot(pb, pc, depth - 1, 2 * eps);
}
plot.add(pa1);
plot.add(pb1);
}
private static boolean oscillates(
double ya, double ya1, double yb, double yb1, double yc) {
return isOscillation(ya, ya1, yb)
&& isOscillation(ya1, yb, yb1)
&& isOscillation(yb, yb1, yc);
}
private static boolean isOscillation(double ya, double yb, double yc) {
return !isFinite(ya) || !isFinite(yb) || !isFinite(yc)
|| (yb > ya && yb > yc) || (yb < ya && yb < yc);
}
private static boolean unsmooth(
double ya, double ya1, double yb, double yb1,double yc, double eps) {
double y0 = DoubleStream.of(ya, ya1, yb, yb1, yc).min().getAsDouble();
double [] yg = DoubleStream.of(ya, ya1, yb, yb1, yc).map(y -> y - y0).toArray();
double q4 = quadrature(yg[0], yg[1], yg[2], yg[3]);
double q3 = quadrature(yg[2], yg[3], yg[4]);
return Math.abs(q4 - q3) > eps * q3;
}
private static double quadrature(double y0, double y1, double y2, double y3) {
return 3d/8d * y0 + 19d/24d * y1 - 5d/24d * y2 + 1d/24d * y3;
}
private static double quadrature(double y0, double y1, double y2) {
return 5d/12d * y0 + 2d/3d * y1 - 1d/12d * y2;
}
public static void main(String [] args) {
List<Point> plot = new AdaptivePlot(x -> x * Math.sin(x), -2d * PI, 2d * PI)
.computePlot(6, 0.005).getPlot();
for (Point p : plot) {
System.out.println(p.x + "\t" + p.y);
}
}
}
I personally find this method horrible.
You can get nice results with a simple rule: subdivide the interval as long as the distance of the middle point to the chord exceeds a small threshold.
Caution: to avoid scaling effects, you should express all geometric quantities in mm (with appropriate scale factors on both axes).
Subdivide left, middle, right:
if DistancePointLine (middle, f(middle)), (left, f(left)), (right, f(right)) < Tolerance:
DrawLine (left, f(left), (right, f(right))
else
Subdivide left, (left + middle) / 2, middle
Subdivide middle, (middle + right) / 2, right
The method can fail in symmetric situations, around inflection points. To cope, you can force one extra subdivision even if the tolerance has been met.
My algorithm checks the relative position between two lines at this point I'm sure the lines are concurrents and want to return the point of intersection. I'm using this formula to not have linear systems:
My problem is when the input lines are as follows:
r: X= (8,1,9) + λ(2,-1,3) s: X (3,-4,4) + µ(1,-2,2) I hope the output is (-2, 6, -6) but is (7.6, 1.2, 8.4). Does anyone have any idea why this occurs?
My method
public Point3D intersectingLines(Line lineOne, Line lineTwo) {
double x = lineOne.getPoint().getX() - lineTwo.getPoint().getX();
double y = lineOne.getPoint().getY() - lineTwo.getPoint().getY();
double z = lineOne.getPoint().getZ() - lineTwo.getPoint().getZ();
Vector3D pointsDifference = new Vector3D(x, y, z);
Vector3D second = pointsDifference.crossProduct(lineTwo.getVector());
Vector3D first = lineOne.getVector().crossProduct(lineTwo.getVector());
double lambda = first.getNorm() / second.getNorm();
double xIntersectionOne = lineOne.getPoint().getX() + (lambda * lineOne.getVector().getX());
double yIntersectionOne = lineOne.getPoint().getY() + (lambda * lineOne.getVector().getY());
double zIntersectionOne = lineOne.getPoint().getZ() + (lambda * lineOne.getVector().getZ());
double xInLineTwo = (xIntersectionOne - lineTwo.getPoint().getX()) / lineTwo.getVector().getX();
double yInLineTwo = (yIntersectionOne - lineTwo.getPoint().getY()) / lineTwo.getVector().getY();
double zInLineTwo = (zIntersectionOne - lineTwo.getPoint().getZ()) / lineTwo.getVector().getZ();
//Here I check if the point is even correct or lambda must be negative to obtain the correct point
if (xInLineTwo == yInLineTwo && xInLineTwo == zInLineTwo) {
return new Point3D(xIntersectionOne, yIntersectionOne, zIntersectionOne);
} else {
xIntersectionOne = lineOne.getPoint().getX() + (-1 * lambda * lineOne.getVector().getX());
yIntersectionOne = lineOne.getPoint().getY() + (-1 * lambda * lineOne.getVector().getY());
zIntersectionOne = lineOne.getPoint().getZ() + (-1 * lambda * lineOne.getVector().getZ());
return new Point3D(xIntersectionOne, yIntersectionOne, zIntersectionOne);
}
}
The main problem is in the equation the other tests had no difference but that particular equation yes. The equation is in "reverse" order so got lambda = |0,2| and not lambda = |5|.
And another problem was noted in the comment's David Wallace where the condition checks equality of doubles type.
See more
I am working on the problem of dividing an ellipse into equal sized segments. This question has been asked but the answers suggested numerical integration so that I what I'm attempting. This code short-circuits the sectors so the integration itself should never cover more than 90 degrees. The integration itself is being done by totaling the area of intermediate triangles. Below is the code I have tried, but it is sweeping more than 90 degrees in some cases.
public class EllipseModel {
protected double r_x;
protected double r_y;
private double a,a2;
private double b,b2;
boolean flip;
double area;
double sector_area;
double radstep;
double rot;
int xp,yp;
double deviation;
public EllipseModel(double r_x, double r_y, double deviation)
{
this.r_x = r_x;
this.r_y = r_y;
this.deviation = deviation;
if (r_x < r_y) {
flip = true;
a = r_y;
b = r_x;
xp = 1;
yp = 0;
rot = Math.PI/2d;
} else {
flip = false;
xp = 0;
yp = 1;
a = r_x;
b = r_y;
rot = 0d;
}
a2 = a * a;
b2 = b * b;
area = Math.PI * r_x * r_y;
sector_area = area / 4d;
radstep = (2d * deviation) / a;
}
public double getArea() {
return area;
}
public double[] getSweep(double sweep_area)
{
System.out.println(String.format("getSweep(%f) a = %f b = %f deviation = %f",sweep_area,a,b,deviation));
double[] ret = new double[2];
double[] next = new double[2];
double t_base, t_height, swept,x_mid,y_mid;
double t_area;
sweep_area = sweep_area % area;
if (sweep_area < 0d) {
sweep_area = area + sweep_area;
}
if (sweep_area == 0d) {
ret[0] = r_x;
ret[1] = 0d;
return ret;
}
double sector = Math.floor(sweep_area/sector_area);
double theta = Math.PI * sector/2d;
double theta_last = theta;
System.out.println(String.format("- Theta start = %f",Math.toDegrees(theta)));
ret[xp] = a * Math.cos(theta + rot);
ret[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);
next[0] = ret[0];
next[1] = ret[1];
swept = sector * sector_area;
System.out.println(String.format("- Sweeping for %f sector_area=%f",sweep_area-swept,sector_area));
int c = 0;
while(swept < sweep_area) {
c++;
ret[0] = next[0];
ret[1] = next[1];
theta_last = theta;
theta += radstep;
// calculate next point
next[xp] = a * Math.cos(theta + rot);
next[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * // selects +/- sqrt
Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);
// calculate midpoint
x_mid = (ret[xp] + next[xp]) / 2d;
y_mid = (ret[yp] + next[yp]) / 2d;
// calculate triangle metrics
t_base = Math.sqrt( ( (ret[0] - next[0]) * (ret[0] - next[0]) ) + ( (ret[1] - next[1]) * (ret[1] - next[1])));
t_height = Math.sqrt((x_mid * x_mid) + (y_mid * y_mid));
// add triangle area to swept
t_area = 0.5d * t_base * t_height;
swept += t_area;
}
System.out.println(String.format("- Theta end = %f (%d)",Math.toDegrees(theta_last),c));
return ret;
}
}
In the output I see the following case where it sweeps over 116 degrees.
getSweep(40840.704497) a = 325.000000 b = 200.000000 deviation = 0.166667
- Theta start = 0.000000
- Sweeping for 40840.704497 sector_area=51050.880621
- Theta end = 116.354506 (1981)
Is there any way to fix the integration formula to create a function that returns the point on an ellipse that has swept a given area? The application that is using this code divides the total area by the number of segments needed, and then uses this code to determine the angle where each segment starts and ends. Unfortunately it doesn't work as intended.
* edit *
I believe the above integration failed because the base and height formula's aren't correct.
No transformation needed use parametric equations for ellipse ...
x=x0+rx*cos(a)
y=y0+ry*sin(a)
where a = < 0 , 2.0*M_PI >
if you divide ellipse by lines from center to x,y from above equation
and angle a is evenly encreased
then the segments will have the same size
btw. if you apply affine transform you will get the same result (even the same equation)
This code will divide ellipse to evenly sized chunks:
double a,da,x,y,x0=0,y0=0,rx=50,ry=20; // ellipse x0,y0,rx,ry
int i,N=32; // divided to N = segments
da=2.0*M_PI/double(N);
for (a=0.0,i=0;i<N;i++,a+=da)
{
x=x0+(rx*cos(a));
y=y0+(ry*sin(a));
// draw_line(x0,y0,x,y);
}
This is what it looks like for N=5
[edit1]
I do not understood from your comment what exactly you want to achieve now
sorry but my English skills are horrible
ok I assume these two possibilities (if you need something different please specify closer)
0.but first some global or member stuff needed
double x0,y0,rx,ry; // ellipse parameters
// [Edit2] sorry forgot to add these constants but they are I thin straight forward
const double pi=M_PI;
const double pi2=2.0*M_PI;
// [/Edit2]
double atanxy(double x,double y) // atan2 return < 0 , 2.0*M_PI >
{
int sx,sy;
double a;
const double _zero=1.0e-30;
sx=0; if (x<-_zero) sx=-1; if (x>+_zero) sx=+1;
sy=0; if (y<-_zero) sy=-1; if (y>+_zero) sy=+1;
if ((sy==0)&&(sx==0)) return 0;
if ((sx==0)&&(sy> 0)) return 0.5*pi;
if ((sx==0)&&(sy< 0)) return 1.5*pi;
if ((sy==0)&&(sx> 0)) return 0;
if ((sy==0)&&(sx< 0)) return pi;
a=y/x; if (a<0) a=-a;
a=atan(a);
if ((x>0)&&(y>0)) a=a;
if ((x<0)&&(y>0)) a=pi-a;
if ((x<0)&&(y<0)) a=pi+a;
if ((x>0)&&(y<0)) a=pi2-a;
return a;
}
1.is point inside segment ?
bool is_pnt_in_segment(double x,double y,int segment,int segments)
{
double a;
a=atanxy(x-x0,y-y0); // get sweep angle
a/=2.0*M_PI; // convert angle to a = <0,1>
if (a>=1.0) a=0.0; // handle extreme case where a was = 2 Pi
a*=segments; // convert to segment index a = <0,segments)
a-=double(segment );
// return floor(a); // this is how to change this function to return points segment id
// of course header should be slightly different: int get_pnt_segment_id(double x,double y,int segments)
if (a< 0.0) return false; // is lower then segment
if (a>=1.0) return false; // is higher then segment
return true;
}
2.get edge point of segment area
void get_edge_pnt(double &x,double &y,int segment,int segments)
{
double a;
a=2.0*M_PI/double(segments);
a*=double(segment); // this is segments start edge point
//a*=double(segment+1); // this is segments end edge point
x=x0+(rx*cos(a));
y=y0+(ry*sin(a));
}
for booth:
x,y is point
segments number of division segments.
segment is sweep-ed area < 0,segments )
Apply an affine transformation to turn your ellipse into a circle, preferrably the unit circle. Then split that into equal sized segments, before you apply the inverse transform. The transformation will scale all areas (as opposed to lengths) by the same factor, so equal area translates to equal area.
EDIT// I might think that the code Programmr.com uses to check the answer output vs expected output is wrong. Because all of the answers here have almost the same formula, and also the formula on the wiki page about hero's formula is the same as the answers here.
In this exercise, complete the function that "returns a value". When you call this function, it should calculate the area of the triangle using Heron's formula and return it.
Heron's formula:
Area = (s*(s-a)(s-b)(s-c))0.5 where s = (a+b+c)/2
I wrote this, but it seems not correct and I can't figure out what's wrong. The output of this gives wrong values:
public class Challenge
{
public static void main( String[] args )
{
double a;
a = triangleArea(3, 3, 3);
System.out.println("A triangle with sides 3,3,3 has an area of:" + a);
a = triangleArea(3, 4, 5);
System.out.println("A triangle with sides 3,4,5 has an area of:" + a);
a = triangleArea(9, 9, 9); // ! also realize the 9,9,9 is not even the same as the comment bellow. This was generated by the Programmr.com exercise.
System.out.println("A triangle with sides 7,8,9 has an area of:" + a );
}
public static double triangleArea( int a, int b, int c )
{
double s = (a + b + c)/2;
double x = ((s) * (s-a) * (s-b) * (s-c));
double Area = Math.sqrt(x);
return Area;
}
}
Expected Output
3.897114317029974
6.0
35.074028853269766
Your code's output
2.0
6.0
28.844410203711913
Use this ..
Heron's formual
double s = (a + b + c)/2.0d;
double x = (s * (s-a) * (s-b) * (s-c));
double Area= Math.sqrt(x);
return Area;
From the Wikipedia article, you are missing a squared root in your formula. Correct solution may be:
public static double triangleArea( int a, int b, int c )
{
double s = (a + b + c)/2;
double Area = Math.sqrt((s* (s-a) *(s-b) * (s-c)) * 0.5);
return Area;
}
EDIT:
I forgot to remove the *0.5 in the second line. It is wrong.
Herons formula used is incorrect.You do not have to multiply with 0.5. You can find correct one here : http://en.wikipedia.org/wiki/Heron%27s_formula
double s = (a + b + c)/2.0d;
double x = ((s) * (s-a) * (s-B)* (s-c));
return Math.sqrt(x);
double s = (a + b + c)/2;
You are getting loss of precision. Read this thread for details.
For your formula, it should be:
double Area = Math.sqrt(s * (s - a) * (s - b) * (s - c));
Since you didn't understand when I said about precision loss, here how your method should look like-
public static double triangleArea( double a, double b, double c ) {
double s = (a + b + c)/2;
double Area = Math.sqrt(s * (s - a) * (s - b) * (s - c));
return Area;
}
double s = (a+b+c)/2.0d;
return Math.pow((s*(s-a)*(s-b)*(s-c)),0.5);
I had the same problem and searched Google for the same. I ran into your question and I am using the same site FYI. The answer is pretty simple. Instead of
double s = (a+b+c)/2;
You use :
double s = (a+b+c)/2.0;
This solved the problem.
Heron's formula
Area = (s*(s-a)(s-b)(s-c))0.5
where s = (a+b+c)/2
double s = (a+b+c)/2.0;
double area = (s*(s-a)*(s-b)*(s-c));
area = Math.pow(area, 0.5);
return area;