How to draw a spherical shell? - c#

I'm trying to arrange points in a spherical shell in C#. I have code to arrange a bunch of points (I'm doing finite element analysis) in a spherical pattern with radius of double earthRadius. I can't figure out how to do the same for a spherical shell like the type pictured here. Ideas?
for (double x = -earthRadius; x < earthRadius; x += pointIncrement) //taken from http://stackoverflow.com/questions/8671385/sphere-drawing-in-java and slightly altered to make more efficient
{
for (double y = -earthRadius; y < earthRadius; y += pointIncrement)
{
for (double z = -earthRadius; z < earthRadius; z += pointIncrement)
{
if ((x * x) + (y * y) + (z * z) <= earthRadius * earthRadius)
{
earth.AddPoint(new Vector(x, y, z), 0);
totalPoints++;
}
}
}
}

I figured this out using just Cartesian coordinates. I elected not to use spherical coordinates because it was a suboptimal solution: it made the code less elegant and using Math.Sin() and Math.Cos() would have slowed it down. Here's the eventual solution I got:
for (double x = -earthRadius; x < earthRadius; x += pointIncrement) //taken from http://stackoverflow.com/questions/8671385/sphere-drawing-in-java and slightly altered to make more efficient
{
for (double y = -earthRadius; y < earthRadius; y += pointIncrement)
{
for (double z = -earthRadius; z < earthRadius; z += pointIncrement)
{
if ((x * x) + (y * y) + (z * z) <= earthRadius * earthRadius)
{
if ((x * x) + (y * y) + (z * z) >= (earthRadius - shellThickness) * (earthRadius - shellThickness))
{
earth.AddPoint(new Vector(x, y, z), 0); // need to figure out how to calculate masspoint
totalPoints++;
}
}
}
}
}
Notice that I simply added another if statement. I (foolishly) didn't use the && operator because I thought it reduced readability.

Related

Integer points inside a circle - problem with recursion

I am trying to write the program which will find points that have integer coords inside a circle. Program should read circle's radius from the user.
Sample correct answers are below picture
I need to write it using iteration and recursion. Iteration works correctly for radius=100:
static int FindPointsByIteration(double minusRadius, double radius)
{
int result = 0;
for (int x = (int)minusRadius; x <= (int)radius; x += 1)
{
for (int y = (int)minusRadius; y <= (int)radius; y += 1)
{
if (((x * x) + (y * y)) < (radius * radius)) result++;
}
}
return result;
}
Max radius without stack overflow is 69 if I open EXE file. In VS it's 62. Is it possible to optimize it?
static int FindPointsByRecursion(double x, double y, double radius, int result)
{
//Console.WriteLine($"x: {x}, y: {y},radius: {radius}, result: {result}");
if (((x * x) + (y * y)) < (radius * radius)) result++;
if (y >= -1 * (int)radius)
{
if (x <= (int)radius)
{
x++;
return FindPointsByRecursion(x, y, radius, result);
}
x = -1 * (int)radius;
y--;
return FindPointsByRecursion(x, y, radius, result);
}
return result;
}
I think you can simplified FindPointsByIteration by only accept one parameter: radius. The problem description states it is a circle (not an oval), it only features one radius. Moreover, radius is a scalar not a vector. It has the same quantity from any directions. Therefore, radius could also represent minusRadius. E.g.
static int FindPointsByIteration(double radius)
{
int points = 0;
for (int x = (int)-radius; x <= radius; x++)
{
for (int y = (int)-radius; y <= radius; y++)
{
if (((x * x) + (y * y)) < (radius * radius))
{
points++;
}
}
}
return points;
}
Above function performs (2R)^2 iteration. FindPointsByIteration(2) takes 16 iterations. FindPointsByIteration(100) takes 40,000 iterations. For iteration, it is fine. For recursion, it may be too much. We need to think of another tactic.
As it is a circle, we can cut it into 4 quadrants. We only count the points in quadrant I and multiple it by 4, the result should be close to the solution. However, there is a catch. We should not multiple the origin and the point lie on the axis e.g.( [0,y] and [x,0]) since they share between quadrants.
Consider a circle of radius 6:
There are 3 main category of its integer coordinate.
Origin (in red).
it is always [0,0]. Any circle should have one
Points on axis (in green).
It depends on the radius. Circle with radius > 1 would have. The number equals to the greatest integer less than the radius times 4.
Points inside quadrant (in blue)
Take quadrant I as example. We count the points by bottom-up, right-to-left approach. Start with [5,1] ([6,1] is known out-of-scope). If [5,1] is inside the scope, then the points for y:1 is 5. It is because any points with [5, 1<y<5] must be inside the scope (e.g. [4,1], [3,1] etc). Then we can move one row up at [5,2] and start the iteration again. Until you meet a point which is outside scope [5,4], then you move 1 column left [4,4] and start the iteration again. This way we can greatly reduce the number of recursion.
Example
static int FindPointsByRecursion(double radius)
{
if (radius < 0) { return 0; }
int points = 0;
// origin
if (radius > 0) { points++; }
// points on axis
int longestEdge = IntegerSmallerThan(radius);
points += longestEdge * 4;
// points contained in quadrant
points += FindPointsInQuadrant(1, longestEdge, radius) * 4;
return points;
}
// return the greatest integer just smaller then n
static int IntegerSmallerThan(double n)
{
int i = (int)n;
return (n == i) ? --i : i;
}
static int FindPointsInQuadrant(int x, int y, double radius)
{
// out of bound, return 0
if (x >= radius || y < 1) return 0;
if ((x * x) + (y * y) < (radius * radius))
{
// if inside scope, move 1 row up
return y + FindPointsInQuadrant(x + 1, y, radius);
}
else
{
// if outside scope, move 1 column left
return FindPointsInQuadrant(x, y - 1, radius);
}
}
Main
Console.WriteLine("FindPointsByIteration");
Console.WriteLine(FindPointsByIteration(1));
Console.WriteLine(FindPointsByIteration(2));
Console.WriteLine(FindPointsByIteration(2.1));
Console.WriteLine(FindPointsByIteration(2.5));
Console.WriteLine(FindPointsByIteration(5));
Console.WriteLine(FindPointsByIteration(100));
Console.WriteLine("\nFindPointsByRecursion");
Console.WriteLine(FindPointsByRecursion(1));
Console.WriteLine(FindPointsByRecursion(2));
Console.WriteLine(FindPointsByRecursion(2.1));
Console.WriteLine(FindPointsByRecursion(2.5));
Console.WriteLine(FindPointsByRecursion(5));
Console.WriteLine(FindPointsByRecursion(100));
Output
FindPointsByIteration
1
9
13
21
69
31397
FindPointsByRecursion
1
9
13
21
69
31397
Counting points in one quarter is a very good idea, but I think I found easier solution (of course with your help). Here's code that calls a method for the first time:
points=4* FindPointsByRecursion(radius, 1, (int)radius, points);
points+= ((int)radius - 1) * 4 + 1;
And a counting method here:
static int FindPointsByRecursion(double radius, int x, int y, int points)
{
if (y < 1) return points;
if ((x * x) + (y * y) < (int)(radius * radius))
{
points++;
return FindPointsByRecursion(radius, x + 1, y, points);
}
else
{
x = 1;
return FindPointsByRecursion(radius, x, y - 1, points);
}
return points;
}
It needs only one method.

Calculation island error with perlin noise

I'm developing a small project, which is a grid of 100 x 100 hexagons.
In the script below, I paint my hexagons with the perlin noise, but the format I want to island does not go away.
I'll leave my code and 2 examples as my map stays and how I wish it to stay.
My island
My Island
As i need
Im Need
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}
If you want your image to look more like the second one, the best option is going to be adding a circular gradient which offsets your Perlin Noise.
The easiest way to do this is to measure the distance from the center and combine that with the perlin noise.
Here's some untested code.
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
v -= CircleOffset(x,z)/2; //Change the two to make the island bigger.
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}
float CircleOffset(float x, float y)
{
Vector2 center = new Vector2(terrainWidth/2,terrainHeight/2);
float distance = Mathf.Sqrt((center.x - x)*(center.x - x) + (center.y - y) * (center.y - y));
return distance/terrainWidth;
}
Hope this helps!

Draw a curve of the Cycloid

Good time of day. It is necessary to draw a graph of the cycloids, the radius is specified by the user. Managed to paint only half of the period, I do not understand what it is.
Code'm applying.
My function:
return r * Math.Acos((r - y) / r) - Math.Sqrt(2 * r * y - Math.Pow(y, 2));
And my Main part:
GraphPane pane = zedGraph.GraphPane;
pane.CurveList.Clear();
PointPairList list = new PointPairList();
double r = 20;
double xmax = 50;
for (double y = 0; y < xmax; y+=0.5)
{
list.Add(CountIt(y, r), y);
}
LineItem myCurve = pane.AddCurve("Cycloid", list, Color.Red, SymbolType.None);
zedGraph.AxisChange();
zedGraph.Invalidate();
Apparently it is necessary to consider the situation when y>2r, or that should be several possible x? I do not understand how to get out of the situation.
It is simpler to use parametric equations (with t=0..2*Pi for one period):
x = r * (t - sin(t))
y = r * (1 - cos(t))
If you want to continue using Cartesian equation x(y) - change limit for y to correct value 2 * r and mirror the second part like this:
for (double y = 0; y < 2 * r; y+=0.5)
{
list.Add(CountIt(y, r), y);
}
for (double y = 2 * r; y >= 0; y-=0.5)
{
list.Add(2 * Pi * r - CountIt(y, r), y);
}
If you need to draw few periods, limiting xmax:
p = 0;
while true do
{
for (double y = 0; y < 2 * r; y+=0.5)
{ x = 2 * Pi * r * p + CountIt(y, r);
if (x > xmax)
break;
list.Add(x, y);
}
for (double y = 2 * r; y >= 0; y-=0.5)
{
x = 2 * Pi * r * (p + 1) - CountIt(y, r);
if (x > xmax)
break;
list.Add(x, y);
}
p++;
}

Traverse Pixels in a circle from the center

I need an algorithm like Bresenham's circle algorithm, but with some modifications.
The algorithm must visit all pixels in the radius (so essentially a fill).
The algorithm must start from the center of the circle
It must visit all points that would normally be visited (no holes)
It must visit each point in the circle exactly once
One technique I came up with would first determine all pixel coordinates inside the circle by just going through the rectangle of the circle and checking with Math.Sqrt if it is inside the circle.
Then it would order the pixels by distance and then visit each of them.
That would be exactly what I want, with the exception of being fast.
So my questions is:
Is there a fast way to do this without fetching,ordering and then visiting each pixel?
Just for clarification I do not actually want to draw onto the image, I only want to traverse them in the described order.
First, we can use fact, that circle can be divided in 8 octants. So we just need to fill single octant and use simple +- coordinate change to get full circle. So if we try to fill only one octant, we need to worry only about 2 directions from center : left and left top. Also, clever use of data structures like priority queue (.NET doesn't have it, so you need to find it somewhere else) and hash map can drastically improve performance.
/// <summary>
/// Make sure it is structure.
/// </summary>
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
public int DistanceSqrt()
{
return X * X + Y * Y;
}
}
/// <summary>
/// Points ordered by distance from center that are on "border" of the circle.
/// </summary>
public static PriorityQueue<Point> _pointsToAdd = new PriorityQueue<Point>();
/// <summary>
/// Set of pixels that were already added, so we don't visit single pixel twice. Could be replaced with 2D array of bools.
/// </summary>
public static HashSet<Point> _addedPoints = new HashSet<Point>();
public static List<Point> FillCircle(int radius)
{
List<Point> points = new List<Point>();
_pointsToAdd.Enqueue(new Point { X = 1, Y = 0 }, 1);
_pointsToAdd.Enqueue(new Point { X = 1, Y = 1 }, 2);
points.Add(new Point {X = 0, Y = 0});
while(true)
{
var point = _pointsToAdd.Dequeue();
_addedPoints.Remove(point);
if (point.X >= radius)
break;
points.Add(new Point() { X = -point.X, Y = point.Y });
points.Add(new Point() { X = point.Y, Y = point.X });
points.Add(new Point() { X = -point.Y, Y = -point.X });
points.Add(new Point() { X = point.X, Y = -point.Y });
// if the pixel is on border of octant, then add it only to even half of octants
bool isBorder = point.Y == 0 || point.X == point.Y;
if(!isBorder)
{
points.Add(new Point() {X = point.X, Y = point.Y});
points.Add(new Point() {X = -point.X, Y = -point.Y});
points.Add(new Point() {X = -point.Y, Y = point.X});
points.Add(new Point() {X = point.Y, Y = -point.X});
}
Point pointToLeft = new Point() {X = point.X + 1, Y = point.Y};
Point pointToLeftTop = new Point() {X = point.X + 1, Y = point.Y + 1};
if(_addedPoints.Add(pointToLeft))
{
// if it is first time adding this point
_pointsToAdd.Enqueue(pointToLeft, pointToLeft.DistanceSqrt());
}
if(_addedPoints.Add(pointToLeftTop))
{
// if it is first time adding this point
_pointsToAdd.Enqueue(pointToLeftTop, pointToLeftTop.DistanceSqrt());
}
}
return points;
}
I will leave the expansion to full list on you. Also make sure borders of the octants don't cause doubling of the points.
Ok, I couldn't handle it and did it myself. Also, to make sure it has properties you desire I did simple test :
var points = FillCircle(50);
bool hasDuplicates = points.Count != points.Distinct().Count();
bool isInOrder = points.Zip(points.Skip(1), (p1, p2) => p1.DistanceSqrt() <= p2.DistanceSqrt()).All(x => x);
I found a solution that satisfies my performance needs.
It's very simple, just a offset array.
static Point[] circleOffsets;
static int[] radiusToMaxIndex;
static void InitCircle(int radius)
{
List<Point> results = new List<Point>((radius * 2) * (radius * 2));
for (int y = -radius; y <= radius; y++)
for (int x = -radius; x <= radius; x++)
results.Add(new Point(x, y));
circleOffsets = results.OrderBy(p =>
{
int dx = p.X;
int dy = p.Y;
return dx * dx + dy * dy;
})
.TakeWhile(p =>
{
int dx = p.X;
int dy = p.Y;
var r = dx * dx + dy * dy;
return r < radius * radius;
})
.ToArray();
radiusToMaxIndex = new int[radius];
for (int r = 0; r < radius; r++)
radiusToMaxIndex[r] = FindLastIndexWithinDistance(circleOffsets, r);
}
static int FindLastIndexWithinDistance(Point[] offsets, int maxR)
{
int lastIndex = 0;
for (int i = 0; i < offsets.Length; i++)
{
var p = offsets[i];
int dx = p.X;
int dy = p.Y;
int r = dx * dx + dy * dy;
if (r > maxR * maxR)
{
return lastIndex + 1;
}
lastIndex = i;
}
return 0;
}
With this code you just get the index where to stop from radiusToMaxIndex, then loop through circleOffsets and visit those pixels.
It will cost lot of memory like this, but you can always change the datatype of the offsets from Point to a custom one with Bytes as members.
This solution is extremely fast, fast enough for my needs. It obviously has the drawback of using some memory, but lets be honest, instantiating a System.Windows.Form uses up more memory than this...
You have already mentioned Bresenhams's circle algorithm. That is a good starting point: You could start with the centre pixel and then draw Bresenham circles of increasing size.
The problem is that the Bresenham circle algorithm will miss pixels near the diagonals in a kind of Moiré effect. In another question, I have adopted the Bresenham algorithm for drawing between an inner and outer circle. With that algorithm as base, the strategy of drawing circles in a loop works.
Because the Bresenham algorithm can place pixels only at discrete integer coordinates, the order of visiting pixels will not be strictly in order of increasing distance. But the distance will always be within one pixel of the current circle you are drawing.
An implementation is below. That's in C, but it only uses scalars, so it shouldn't be hard to adapt to C#. The setPixel is what you do to each pixel when iterating.
void xLinePos(int x1, int x2, int y)
{
x1++;
while (x1 <= x2) setPixel(x1++, y);
}
void yLinePos(int x, int y1, int y2)
{
y1++;
while (y1 <= y2) setPixel(x, y1++);
}
void xLineNeg(int x1, int x2, int y)
{
x1--;
while (x1 >= x2) setPixel(x1--, y);
}
void yLineNeg(int x, int y1, int y2)
{
y1--;
while (y1 >= y2) setPixel(x, y1--);
}
void circle2(int xc, int yc, int inner, int outer)
{
int xo = outer;
int xi = inner;
int y = 0;
int erro = 1 - xo;
int erri = 1 - xi;
int patch = 0;
while (xo >= y) {
if (xi < y) {
xi = y;
patch = 1;
}
xLinePos(xc + xi, xc + xo, yc + y);
yLineNeg(xc + y, yc - xi, yc - xo);
xLineNeg(xc - xi, xc - xo, yc - y);
yLinePos(xc - y, yc + xi, yc + xo);
if (y) {
yLinePos(xc + y, yc + xi, yc + xo);
xLinePos(xc + xi, xc + xo, yc - y);
yLineNeg(xc - y, yc - xi, yc - xo);
xLineNeg(xc - xi, xc - xo, yc + y);
}
y++;
if (erro < 0) {
erro += 2 * y + 1;
} else {
xo--;
erro += 2 * (y - xo + 1);
}
if (y > inner) {
xi = y;
} else {
if (erri < 0) {
erri += 2 * y + 1;
} else {
xi--;
erri += 2 * (y - xi + 1);
}
}
}
if (patch) {
y--;
setPixel(xc + y, yc + y);
setPixel(xc + y, yc - y);
setPixel(xc - y, yc - y);
setPixel(xc - y, yc + y);
}
}
/*
* Scan pixels in circle in order of increasing distance
* from centre
*/
void scan(int xc, int yc, int r)
{
int i;
setPixel(xc, yc);
for (i = 0; i < r; i++) {
circle2(xc, yc, i, i + 1);
}
}
This code takes care of not visiting pixels that are in two octants by skipping coincident picels on alterante octants. (Edit: There was still abug in the original code, but it's fixed now by means of the ´patch` variable.)
There's also room for improvement: The inner circle is basically the outer circle of the previous iteration, so there's no point in calculating it twice; you could keep an array of the outer points of the previous circle.
The xLinePos functions are also a bit too complicated. There are never more than two pixels drawn in that function, usually only one.
If the roughness of the search order bothers you, you can run a more exact algorithm once at the beginning of the program, where you calculate a traversing order for all circles up to a reasonable maximum radius. You can then keep that data and use it for iterating all circles with smaller radii.

Loop from x1,y1 to x2,y2, no matter in which order they are

I want to be able to select part of an image via two Points (p1,p2). My problem is that I want to use the same loop regardless in which order they are.
right now I have this:
for (int x = p1.X; x != p2.X; x += Math.Sign(p2.X - p1.X))
{
for (int y = p1.Y; y != p2.Y; y += Math.Sign(p2.Y - p1.Y))
{
MessageBox.Show(String.Format("{0} {1}", x, y));
}
}
With that loop I don't get all of the numbers:
e.g. from 1/1 to 3/3 only gones till 2/2.
I some how need to loop through both loops one more time, but since I don't know which way I'm actually looping (decreasing or increasing) I can't just add/ subtract one from the loop.
any help would be appreciated!
You can just loop from the lowest X to the highest X, and then do the same for Y.
for (int x = Math.Min(p1.X, p2.X); x <= Math.Max(p1.X, p2.X); x++){
for (int y = Math.Min(p1.Y, p2.Y); y <= Math.Max(p1.Y, p2.Y); y++){
MessageBox.Show(String.Format("{0} {1}", x, y));
}
}
This will not walk down from [3,3] to [1,1]. If you actually care about the direction, this approach won't work.
Point p1 = new Point(1, 1);
Point p2 = new Point(3, 3);
int dx = Math.Sign(p2.X - p1.X);
int dy = Math.Sign(p2.Y - p1.Y);
for (int x = p1.X; x != p2.X + dx; x += dx)
{
for (int y = p1.Y; y != p2.Y + dy; y += dy)
{
Console.WriteLine("{0} {1}", x, y);
}
}

Categories