Related
I am doing image manipulation, I have a class that I've inherited that I can't change because it's referenced too many times to completely refactor. Here is the class as it stands.
I have a Bitmap and I am trying to get a collection of all of the LinkedPixels. For example pixel
x, y would be a key and a pixel in the center would be attached to four pixels, (x+1, y), (x, y + 1), (x-1, y), (y-1, x). Pixels on the sides would have three attached Pixels, and Pixels in the corners only two.
imWrap.Arr is an array with all of the Pixels inside of a Bitmap, this is built as the program spools up so this is not built on the fly it is already available.
Since I cannot change the class itself (I could change it to a struct if necessary) I need to find the fastest way to create the LinkedPix, I am aware that there are many better ways to do this but I am locked in to this "LinkedPix" class, there are over 100 references to it
public class LinkedPix
{
public Point P;
public override string ToString()
{
return P.ToString() + " " + Links.Count;
}
public HashSet<LinkedPix> Links;
public SegObject MemberOfObject { get; set; }
public bool IsMemberOfObject { get => MemberOfObject != null; }
private void Init()
{
MemberOfObject = null;
Links = new HashSet<LinkedPix>();
}
public LinkedPix(int x, int y)
{
Init();
P = new Point(x, y);
}
public LinkedPix(Point p)
{
Init();
P = p;
}
public LinkedPix RotateCopy(double theta_radians)
{
//Assumes that 0,0 is the origin
LinkedPix LP2 = new LinkedPix(new Point(
(int)(P.X * Math.Cos(theta_radians) - P.Y * Math.Sin(theta_radians)),
(int)(P.Y * Math.Cos(theta_radians) + P.X * Math.Sin(theta_radians))));
return LP2;
}
public void LinksRemove(LinkedPix ToRemove, bool TwoWay = false)
{
//Remove from each others
if (TwoWay) ToRemove.Links.Remove(this);
Links.Remove(ToRemove);
}
/// <summary>
/// Removes this from each of its links and removes these links
/// </summary>
internal void LinksCrossRemove()
{
foreach (LinkedPix tPix in Links) tPix.LinksRemove(this, false);
Links = new HashSet<LinkedPix>();
}
}
Here are the three functions I used to populate the information about how LinkedPix are related.
Dictionary<Point, LinkedPix> PointDict = new Dictionary<Point, LinkedPix>();
void EstablishLinks(int xi, int yi, LinkedPix Pixi)
{
if (imWrap.Arr[xi, yi] > threshold)
{
Point pi = new Point(xi, yi);
LinkedPix Pix2 = GetOrAdd(pi);
Pixi.Links.Add(Pix2);
Pix2.Links.Add(Pixi);
}
}
LinkedPix Pix; Point p;
int height = imWrap.Height;
for (int y = 0; y < height - 1; y++)
{
if (Slice && y % Slice_ModulusFactor < 1) continue; //This is for Axon Degeneration to break up the axons
for (int x = 0; x < height - 1; x++)
{
if (Slice && x % Slice_ModulusFactor < 1) continue;
if (imWrap.Arr[x, y] > threshold)
{
Pixels++;
if (CreateLinks)
{
p = new Point(x, y);
Pix = GetOrAdd(p);
if (y + 1 < height) EstablishLinks(x, y + 1, Pix);
if (x + 1 < height) EstablishLinks(x + 1, y, Pix);
}
}
}
}
}
public LinkedPix GetOrAdd(Point p)
{
if (PointDict.ContainsKey(p))
{
return PointDict[p];
}
else
{
LinkedPix LP = new LinkedPix(p);
PointDict.Add(p, LP);
return LP;
}
}
}
imWrap.Arr[x, y] is an array that has already been pre-populated with the contents of a bitmap.
things I have tried
Switching to ConcurrentDictionaries and running in Parallel. The inferior Hashing Algorithm of ConcurrentDictionary compared to HashSet means running this in Parallel actually takes more time, at least when I tried it.
switching to a different collection with faster adds such as a Que. This gains me some speed but because of the way LinkedPix is used, I ultimately have to convert to a HashSet anyway which costs time in the end so this is not ultimately a win.
Various types of Asynchronicity, I either gain very little or nothing at all.
I'm definitely stuck and could use some outside of the box thinking. I definitely need to speed this up and any help would be greatly appreciated.
These are some timings
GetOrAdd: 4.190254371881144 ticks
LinksAdd: 1.7282694093881512 ticks
LinksAddTwo: 1.6570632131524925 ticks
PointCreateTime: 0.20380008184398646 ticks
ArrayCheck: 0.3061780882729328 ticks
I am trying to program a Unity Minesweeper game, and I am almost done, but as part of it I have a boolean variable called "isCovered" that works out if the square has been clicked or not.
This variable is used to see if the player has won yet. I have a 2d array that stores all of my squares, and to see if you win yet, it checks the array for covered, non mine squares, and if it finds them, concludes that you have not won yet.
However, when I test it, the code does not seem to find any squares that are covered, even though I can see that there are some. I concluded this because it outputs the "you win" message every time I click a safe square, even when I am not finished.
My variable is declared using this code:
public class Square : MonoBehaviour {
//creates boolean variable
public bool IsCovered() {
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
}
and the code to determine if you win, which is within a different class (that is my array/grid) is:
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
The asset that is used for the default texture is called "Uncleared proposed", rather than "Default" so I don't know if that is an issue.
My entire "Square" code for each individual tile is:
using System.Collections;
using UnityEngine;
public class Square : MonoBehaviour
{
//is this dangerous?
public bool dangerous;
//creates boolean variable
public bool IsCovered()
{
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
//different textures
public Sprite[] emptyTextures;
public Sprite dangerTexture;
// Use this for initialization
void Start()
{
//game randomly decides if square is dangerous
//10% chance for initial testing
//Chance likely to be changed at some point
dangerous = Random.value < 0.1;
//registers square in grid
int x = (int)transform.position.x;
int y = (int)transform.position.y;
Grid.elements[x, y] = this;
}
void OnMouseUpAsButton()
{
// It's dangerous
if (dangerous)
{
//uncover all mines
//calls for procedure "uncoverDanger()" from class "Grid"
Grid.uncoverDanger();
//ToDo: display loss screen
// game over
print("Game Over");
}
// It's not a mine
else
{
// change to correct texture
int x = (int)transform.position.x;
int y = (int)transform.position.y;
loadTexture(Grid.adjacentCount(x, y));
//Flood Fill nearby area
Grid.FFuncover(x, y, new bool[Grid.w, Grid.h]);
// checks if the player has won
if (Grid.gameWon() == true)
//tells the player they have won
print("you win");
}
}
//Loads a different texture
public void loadTexture(int adjacentCount)
{
//checks to see if square is dangerous
if (dangerous)
//Changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = dangerTexture;
//if not dangerous
else
//changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = emptyTextures[adjacentCount];
}
}
and my Grid class code is:
using System.Collections;
using UnityEngine;
public class Grid {
public static int w = 10; // w is the width of the grid (10)
public static int h = 13; // h is the height of the grid (13)
public static Square[,] elements = new Square[w, h];
//Uncover all mines when losing
public static void uncoverDanger() {
foreach (Square elem in elements)
if (elem.dangerous)
elem.loadTexture(0);
}
// Determines if the square is dangerous
public static bool dangerAt(int x, int y)
{
// Makes sure the square is in the range
if (x >= 0 && y >= 0 && x < w && y < h)
// Checks if the square is dangerous
return elements[x, y].dangerous;
return false;
}
// Counts number of dangerous squares adjacent
public static int adjacentCount(int x, int y)
{
int count = 0;
//checks adjacent squares
//potentially adds to counter
if (dangerAt(x, y + 1)) ++count; // top
if (dangerAt(x + 1, y + 1)) ++count; // top-right
if (dangerAt(x + 1, y)) ++count; // right
if (dangerAt(x + 1, y - 1)) ++count; // bottom-right
if (dangerAt(x, y - 1)) ++count; // bottom
if (dangerAt(x - 1, y - 1)) ++count; // bottom-left
if (dangerAt(x - 1, y)) ++count; // left
if (dangerAt(x - 1, y + 1)) ++count; // top-left
//outputs value
return count;
}
// Flood Fill (FF) safe squares
public static void FFuncover(int x, int y, bool[,] visited)
{
// Checks if coordinates in range
if (x >= 0 && y >= 0 && x < w && y < h)
{
// Filled already?
if (visited[x, y])
return;
//uncovers a square
elements[x, y].loadTexture(adjacentCount(x, y));
//checks if adjacencyCount is not 0
if (adjacentCount(x, y) > 0)
//ends algorithm
return;
// set square as visited
visited[x, y] = true;
// repeates with all 4 adjacent squares (not using diagonal)
FFuncover(x - 1, y, visited);
FFuncover(x + 1, y, visited);
FFuncover(x, y - 1, visited);
FFuncover(x, y + 1, visited);
}
}
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
}
I am a real newbie to unity so it might be something stupid, any help would be greatly appreciated.
Turns out I am an idiot, I just had to change the name of the texture in the variable code to "Uncleared proposed", I don't know why it didn't work earlier.
I am currently developing a test program that makes sure that holes on a motherboard aren't too close to each other or that they aren't overlapping.
In order to do this, I am keeping all of the holes' X, Y coordinates and radii in objects called holeInfo's and the objects inside the list holeInfoList.
I am currently using a nested for loop to go through all of the holes and a basic mathematical formula to check the distance between the holes.
Here is the function I use:
public void checkHoleConditions()
{
for (int i = 0; i < holeInfoList.Count - 1; i++)
{
errorType = new List<string>();
for (int j = i + 1; j < holeInfoList.Count; j++)
{
holeMinSpaceFailed = false;
if (failsHoleConditions(holeInfoList[i], holeInfoList[j]))
{
if (holeMinSpaceFailed)
{
errorType.Add("X: " + holeInfoList[j].holeXCoordinate + " Y: " + holeInfoList[j].holeYCoordinate + "R: " + holeInfoList[j].holeDiameter + " too close.");
}
else
{
errorType.Add("X: " + holeInfoList[j].holeXCoordinate + " Y: " + holeInfoList[j].holeYCoordinate + "R: " + holeInfoList[j].holeDiameter + " overlap.");
}
invalidHole = new InvalidHole(holeInfoList[i], errorType);
invalidHoleList.Add(invalidHole);
Console.WriteLine("Hole Error Detected");
}
else
{
Console.WriteLine("Hole Check Successful");
}
}
}
}
public bool failsHoleConditions(HoleInfo holeOne, HoleInfo holeTwo)
{
float holeOneXCoordinate = holeOne.holeXCoordinate;
float holeOneYCoordinate = holeOne.holeYCoordinate;
float holeOneRadius = holeOne.holeDiameter / 2;
float holeTwoXCoordinate = holeTwo.holeXCoordinate;
float holeTwoYCoordinate = holeTwo.holeYCoordinate;
float holeTwoRadius = holeTwo.holeDiameter / 2;
float holeXDifferenceSquared = (float)Math.Pow((holeOneXCoordinate - holeTwoXCoordinate), 2);
float holeYDifferenceSquared = (float)Math.Pow((holeOneYCoordinate - holeTwoYCoordinate), 2);
float distanceBetweenCenters = (float)Math.Sqrt(holeXDifferenceSquared + holeYDifferenceSquared);
float distanceBetweenHoles = distanceBetweenCenters - (holeOneRadius + holeTwoRadius);
if (distanceBetweenHoles <= 0)
{
return true;
}
else if (distanceBetweenHoles <= minimumSpace)
{
holeMinSpaceFailed = true;
return true;
}
return false;
}
Currently, my program does the test in about 2.5 minutes for 254 list objects. Considering that this is a test program, 2.5 minutes for only 254 holes is a long time.
What are some of the algorithms that I can implement to make the test run faster?
An inspiration for this solution is physics engines, and the optimizations there that take care of collision detection.
I don't claim to know entirely how it works, and for better solutions you should try researching ODE or Bullet Dynamics for this.
Basically the solution is to separate objects (holes) into islands, and only compare each object's position to objects in the same island. If you can't come up with a way to properly separate islands, you could just do this:
Suppose we have 125 objects on a square field, 5 by 5. You can divide it into 25 primary square islands, and then 8 intersection islands (long islands, along the edges of the primary islands. The smallest side of these islands should be the minimum distance you want to compute). Islands can overlap. You have to parse the whole list once to make this split. Which means that so far we loop through 125 items total - O(n).
Next, for each island (33 total, O(n^(2/3)), find objects that are closer than they have to be, by using those same nested loops. Total complexity of each island is O((n / n^(2/3))^2) = O(n^(2/3)). Times the number of islands, we get total complexity for this algorithm = O(n^(4/3)), which is smaller than O(n^2) that was originally presented.
Hope this makes sense. I can write a Python demo if you want. It's just that it would be quite a lot of code.
Edit:
Or you could just use a 2D physics engine and plot the objects as circles with a diameter equal to the minimum distance between holes and then let it detect the collisions. Or take the relevant code from there (if license permits), since the entire physics engine is a bit overkill for the task.
https://github.com/VelcroPhysics/VelcroPhysics
Edit 2:
254 list objects
I thought you were parsing 254 different boards. This solution I highlighted would make sense only in huge computational loads.
holeMinSpaceFailed will never be true if failsHoleConditions is true
don't pass around logic like that
x * x is faster then Math.Pow
No need to take Sqrt - just square minimumSpace
Something like this. Runs 1 million 300 milliseconds. Something is wrong for your code to take minutes.
static byte HoleTooClose(HoleInfo x, HoleInfo y, float minDistance)
{
float holeSize = (x.Diameter + y.Diameter) / 2;
float deltaX = y.X - x.X;
float deltaY = y.Y - x.Y;
float distanceSquared = deltaX * deltaX + deltaY * deltaY - holeSize * holeSize;
if (distanceSquared <= 0)
{
return 0;
}
float minDistanceSquared = minDistance * minDistance;
if (distanceSquared <= minDistanceSquared)
{
return 1;
}
return 2;
}
internal struct HoleInfo
{
public float Diameter { get; internal set; }
public float X { get; internal set; }
public float Y { get; internal set; }
public HoleInfo (float x, float y, float diameter)
{
X = x;
Y = y;
Diameter = diameter;
}
}
static bool DistanceTooClose(System.Windows.Point x, System.Windows.Point y, Double minDistance)
{
double deltaX = Math.Abs(y.X - x.X);
double deltaY = Math.Abs(y.Y - x.Y);
double distanceSquared = deltaX * deltaX + deltaY * deltaY;
//double distance = Math.Sqrt(distanceSquared);
Double minDistanceSquared = minDistance * minDistance;
return (distanceSquared <= minDistanceSquared);
}
If you need to test all against all, one optimization can be to put them in a nxn matrix, instead of a list. Then, execute the validation on parallel. The bigger the matrix and the more cores you have the better. The parallel can also be executed on the list, but I'm not 100% sure if .Net will determine to wait for each thread to end, as the algorithm is sequential.
If your method uses euclidean distance, you can try to order the list first, based on this distance. sqr(x)+sqr(y) with respect to 0 can be a score to sort first. I think .Net can handle this sorting easily. Later, just run your algorithm till the first allowed circuit. Then, you know the rest must be accepted, so you will only execute on the first elements of the list
Lets use C# in our example.
public class Sphere
{
public Point Center { get; set; }
public float Radius { get; set; }
public Sphere(IEnumerable<Point> points)
{
Point first = points.First();
Point vecMaxZ = first;
Point vecMinZ = first;
Point vecMaxY = first;
Point vecMinY = first;
Point vecMinX = first;
Point vecMaxX = first;
foreach (Point current in points)
{
if (current.X < vecMinX.X)
{
vecMinX = current;
}
if (current.X > vecMaxX.X)
{
vecMaxX = current;
}
if (current.Y < vecMinY.Y)
{
vecMinY = current;
}
if (current.Y > vecMaxY.Y)
{
vecMaxY = current;
}
if (current.Z < vecMinZ.Z)
{
vecMinZ = current;
}
if (current.Z > vecMaxZ.Z)
{
vecMaxZ = current;
}
}
//the lines bellow assure at least 2 points sit on the surface of the sphere.
//I'm pretty sure the algorithm is solid so far, unless I messed up the if/elses.
//I've been over this, looking at the variables and the if/elses and they all
//seem correct, but our own errors are the hardest to spot,
//so maybe there's something wrong here.
float diameterCandidateX = vecMinX.Distance(vecMaxX);
float diameterCandidateY = vecMinY.Distance(vecMaxY);
float diameterCandidateZ = vecMinZ.Distance(vecMaxZ);
Point c;
float r;
if (diameterCandidateX > diameterCandidateY)
{
if (diameterCandidateX > diameterCandidateZ)
{
c = vecMinX.Midpoint(vecMaxX);
r = diameterCandidateX / 2f;
}
else
{
c = vecMinZ.Midpoint(vecMaxZ);
r = diameterCandidateZ / 2f;
}
}
else if (diameterCandidateY > diameterCandidateZ)
{
c = vecMinY.Midpoint(vecMaxY);
r = diameterCandidateY / 2f;
}
else
{
c = vecMinZ.Midpoint(vecMaxZ);
r = diameterCandidateZ / 2f;
}
//the lines bellow look for points outside the sphere, and if one is found, then:
//1 let dist be the distance from the stray point to the current center
//2 let diff be the equal to dist - radius
//3 radius will then the increased by half of diff.
//4 a vector with the same direction as the stray point but with magnitude equal to diff is found
//5 the current center is moved by half the vector found in the step above.
//
//the stray point will now be included
//and, I would expect, the relationship between the center and other points will be mantained:
//if distance from p to center = r / k,
//then new distance from p to center' = r' / k,
//where k doesn't change from one equation to the other.
//this is where I'm wrong. I cannot figure out how to mantain this relationship.
//clearly, I'm moving the center by the wrong amount, and increasing the radius wrongly too.
//I've been over this problem for so much time, I cannot think outside the box.
//my whole world is the box. The box and I are one.
//maybe someone from outside my world (the box) could tell me where my math is wrong, please.
foreach (Point current in points)
{
float dist = current.Distance(c);
if (dist > r)
{
float diff = dist - r;
r += diff / 2f;
float scaleFactor = diff / current.Length();
Point adjust = current * scaleFactor;
c += adjust / 2f;
}
}
Center = c;
Radius = r;
}
public bool Contains(Point point) => Center.Distance(point) <= Radius;
public override string ToString() => $"Center: {Center}; Radius: {Radius}";
}
public class Point
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
public Point(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public float LengthSquared() => X * X + Y * Y + Z * Z;
public float Length() => (float) Math.Sqrt(X * X + Y * Y + Z * Z);
public float Distance(Point another)
{
return (float) Math.Sqrt(
(X - another.X) * (X - another.X)
+ (Y - another.Y) * (Y - another.Y)
+ (Z - another.Z) * (Z - another.Z));
}
public float DistanceSquared(Point another)
{
return (X - another.X) * (X - another.X)
+ (Y - another.Y) * (Y - another.Y)
+ (Z - another.Z) * (Z - another.Z);
}
public Point Perpendicular()
{
return new Point(-Y, X, Z);
}
public Point Midpoint(Point another)
{
return new Point(
(X + another.X) / 2f,
(Y + another.Y) / 2f,
(Z + another.Z) / 2f);
}
public override string ToString() => $"({X}, {Y}, {Z})";
public static Point operator +(Point p1, Point p2)
{
return new Point(p1.X + p2.X, p1.Y + p2.Y, p1.Z + p2.Z);
}
public static Point operator *(Point p1, float v)
{
return new Point(p1.X * v, p1.Y * v, p1.Z * v);
}
public static Point operator /(Point p1, float v)
{
return new Point(p1.X / v, p1.Y / v, p1.Z / v);
}
}
//Note: this class is here so I can be able to solve the problems suggested by
//Eric Lippert.
public class Line
{
private float coefficient;
private float constant;
public Line(Point p1, Point p2)
{
float deltaY = p2.Y - p1.Y;
float deltaX = p2.X - p1.X;
coefficient = deltaY / deltaX;
constant = coefficient * -p1.X + p1.Y;
}
public Point FromX(float x)
{
return new Point(x, x * coefficient + constant, 0);
}
public Point FromY(float y)
{
return new Point((y - constant) / coefficient, y, 0);
}
public Point Intersection(Line another)
{
float x = (another.constant - constant) / (coefficient - another.coefficient);
float y = FromX(x).Y;
return new Point(x, y, 0);
}
}
Can I safely assume this will run at least just as fast as the fancy algorithms out there that usually consider, for robustness sake, the possibility of the Points having any number of dimensions, from 2 to anything, like 1000 or 10,000 dimensions.
I only need it for 3 dimensions, never more and never less than that. Since I have no academic degree on computer science (or any degree for that matter, I'm a highschool sophomore), I have difficulties in analyzing algorithms for performance and resource consumption. So my question basically is: Is my "smallest enclosing sphere for dumbs" algoritm good in performance and resource consumption when compared with the fancy ones? Is there a point where my algorithm breaks while the professional ones don't, meaning it performs so bad it will cause noticeable loss (like, if I have too many points).
EDIT 1: I editted the code because it made no sense at all (I was hungry, it was 4pm and I haven't eaten all day). This one makes more sense I think, not sure if it's correct though. The original question stands: If this one solves the problem, does it do it well enough to compete with the stardard professional algorithms in case we know in advance that all points have 3 dimensions?
EDIT 2: Now I'm pretty sure the performance is bad, and I lost all hope of implementing a naive algorithm to find the smallest enclosing sphere. I just want to make something that work. Please, check the latest update.
EDIT 3: Doesn't work either. I quit.
EDIT 4: Finally, after, I don't know... some 5 hours. I figured it out. Jesus Christ. This one works. Could someone tell me about the performance issue? Is it really bad compared to the professional algorithms? What lines can I change to make it better? Is there a point where it breaks? Remember, I will always use it for 3D points.
EDIT 5: I learned from Bychenko the previous algorithm still didn't work. I slept on this issue, and this is my new version of the algorithm. I know it doesn't work, and I have a good clue where it is wrong, could anyone please tell why those particular calculations are wrong and how to fix them? I'm inclined to think this has something to do with trigonometry. My assumptions don't hold true for Euclidean space, because I can't stop seeing vectors as real numbers instead
of sets of real numbers that, in my case, I use to pin-point a location in Euclidean space. I'm pretty sure I'm missing some sine or cosine somewhere in the last loop (of course, not exactly sine or cosine, but the equivalent in cartesian coordinates, since we don't know any angles.
Addendum to EDIT 5: About the problems proposed by Eric Lippert:
(1) argh too trivial :p
(2) I will do it for the circle first; I will add a class Line for that.
Point a, b, c; //they are not collinear
Point midOfAB = a.Midpoint(b);
Point midOfBC = b.Midpoint(c);
//multiplying the vector by a scalar as I do bellow doesn't matter right?
Point perpendicularToAB = midOfAB.Perpendicular() * 3;
Point perpendicularToBC = midOfBC.Perpendicular() * 3;
Line bisectorAB = new Line(perpendicularToAB, midOfAB);
Line bisectorBC = new Line(perpendicularToBC, midOfBC);
Point center = bisectorAB.Intersection(bisectorBC);
float distA = center.Distance(a);
float distB = center.Distance(b);
float distC = center.Distance(c);
if(distA == distB && distB == distC)
//it works (spoiler alert: it doesn't)
else
//you're a failure, programmer, pick up your skate and practice some ollies
Sorry, but your algorithm is wrong. It doesn't solve the problem.
Counter example (3 points):
A = (0, 0, 0) - closest to origin (0)
B = (3, 3, 0) - farthest from origin (3 * sqrt(2) == 4.2426...)
C = (4, 0, 0)
your naive algorithm declares that the sphere has center at
P = (3 / sqrt(2), 3 / sqrt(2), 0)
and radius
R = 3 / sqrt(2)
and you can see that the point C = (4, 0, 0) is beyond the sphere
Edit the updated (but naive) algorithm is still wrong.
Counter example (3 points):
A = (0, 0, 0)
B = (1, 2, 0)
C = (4, 1, 0)
according the algorithm the sphere has its center at
P = (2, 1, 0)
with radius
R = sqrt(5)
and you can see that the sphere is not a minimal (smallest) one.
Nth Edit you still have an incorrect algorithm. When exploring gray zone (you know the problem, but partially, with holes) it's a good practice to invest into testing automatition. As you should know, in case of triangle all the vertexes should be on the sphere; let's validate your the solution on this fact:
public static class SphereValidator {
private static Random m_Random = new Random();
private static String Validate() {
var triangle = Enumerable
.Range(0, 3)
.Select(i => new Point(m_Random.Next(100), m_Random.Next(100), m_Random.Next(100)))
.ToArray();
Sphere solution = new Sphere(triangle);
double tolerance = 1.0e-5;
for (int i = 0; i < triangle.Length; ++i) {
double r = triangle[i].Distance(solution.Center);
if (Math.Abs(r - solution.Radius) > tolerance) {
return String.Format("Counter example\r\n A: {0}\r\n B: {1}\r\n C: {2}\r\n expected distance to \"{3}\": {4}; actual R {5}",
triangle[0], triangle[1], triangle[2], (char) ('A' + i), r, solution.Radius);
}
}
return null;
}
public static String FindCounterExample(int attempts = 10000) {
for (int i = 0; i < attempts; ++i) {
String result = Validate();
if (!String.IsNullOrEmpty(result))
Console.WriteLine(result);
return;
}
Console.WriteLine(String.Format("Yes! All {0} tests passed!", attempts));
}
}
I've just run the code above and got:
Counter example
A: (3, 30, 9)
B: (1, 63, 40)
C: (69, 1, 16)
expected distance to "A": 35.120849609375; actual R 53.62698
For a crude approximation, compute the Axis-Aligned Bounding Box, then the bounding sphere of that box (same center, diameter = √(W² + H² + D²) ).
You can refine by computing the largest distance from that center.
Given the image below, what algorithm might I use to detect whether regions one and two (identified by color) have a border?
http://img823.imageshack.us/img823/4477/borders.png
If there's a C# example out there, that would be awesome, but I'm really just looking for any example code.
Edit: Using Jaro's advice, I came up with the following...
public class Shape
{
private const int MAX_BORDER_DISTANCE = 15;
public List<Point> Pixels { get; set; }
public Shape()
{
Pixels = new List<Point>();
}
public bool SharesBorder(Shape other)
{
var shape1 = this;
var shape2 = other;
foreach (var pixel1 in shape1.Pixels)
{
foreach (var pixel2 in shape2.Pixels)
{
var xDistance = Math.Abs(pixel1.X - pixel2.X);
var yDistance = Math.Abs(pixel1.Y - pixel2.Y);
if (xDistance > 1 && yDistance > 1)
{
if (xDistance * yDistance < MAX_BORDER_DISTANCE)
return true;
}
else
{
if (xDistance < Math.Sqrt(MAX_BORDER_DISTANCE) &&
yDistance < Math.Sqrt(MAX_BORDER_DISTANCE))
return true;
}
}
}
return false;
}
// ...
}
Clicking on two shapes that do share a border returns fairly quickly, but very distance shapes or shapes with a large number of pixels take 3+ seconds at times. What options do I have for optimizing this?
2 regions having border means that within a certain small area there should be 3 colors present: red, black and green.
So a very ineffective solution presents itself:
using Color pixelColor = myBitmap.GetPixel(x, y); you could scan an area for those 3 colors. The area must be larger than the width of the border of course.
There is of course plenty room for optimizations (like going in 50 pixels steps and decreasing the precision continually).
Since black is the least used color, you would search around black areas first.
This should explain what I have written in various comments in this topic:
namespace Phobos.Graphics
{
public class BorderDetector
{
private Color region1Color = Color.FromArgb(222, 22, 46);
private Color region2Color = Color.FromArgb(11, 189, 63);
private Color borderColor = Color.FromArgb(11, 189, 63);
private List<Point> region1Points = new List<Point>();
private List<Point> region2Points = new List<Point>();
private List<Point> borderPoints = new List<Point>();
private Bitmap b;
private const int precision = 10;
private const int distanceTreshold = 25;
public long Miliseconds1 { get; set; }
public long Miliseconds2 { get; set; }
public BorderDetector(Bitmap b)
{
if (b == null) throw new ArgumentNullException("b");
this.b = b;
}
private void ScanBitmap()
{
Color c;
for (int x = precision; x < this.b.Width; x += BorderDetector.precision)
{
for (int y = precision; y < this.b.Height; y += BorderDetector.precision)
{
c = this.b.GetPixel(x, y);
if (c == region1Color) region1Points.Add(new Point(x, y));
else if (c == region2Color) region2Points.Add(new Point(x, y));
else if (c == borderColor) borderPoints.Add(new Point(x, y));
}
}
}
/// <summary>Returns a distance of two points (inaccurate but very fast).</summary>
private int GetDistance(Point p1, Point p2)
{
return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y);
}
/// <summary>Finds the closests 2 points among the points in the 2 sets.</summary>
private int FindClosestPoints(List<Point> r1Points, List<Point> r2Points, out Point foundR1, out Point foundR2)
{
int minDistance = Int32.MaxValue;
int distance = 0;
foundR1 = Point.Empty;
foundR2 = Point.Empty;
foreach (Point r1 in r1Points)
foreach (Point r2 in r2Points)
{
distance = this.GetDistance(r1, r2);
if (distance < minDistance)
{
foundR1 = r1;
foundR2 = r2;
minDistance = distance;
}
}
return minDistance;
}
public bool FindBorder()
{
Point r1;
Point r2;
Stopwatch watch = new Stopwatch();
watch.Start();
this.ScanBitmap();
watch.Stop();
this.Miliseconds1 = watch.ElapsedMilliseconds;
watch.Start();
int distance = this.FindClosestPoints(this.region1Points, this.region2Points, out r1, out r2);
watch.Stop();
this.Miliseconds2 = watch.ElapsedMilliseconds;
this.b.SetPixel(r1.X, r1.Y, Color.Green);
this.b.SetPixel(r2.X, r2.Y, Color.Red);
return (distance <= BorderDetector.distanceTreshold);
}
}
}
It is very simple. Searching this way only takes about 2 + 4 ms (scanning and finding the closest points).
You could also do the search recursively: first with precision = 1000, then precision = 100 and finally precision = 10 for large images.
FindClosestPoints will practically give you an estimated rectangual area where the border should be situated (usually borders are like that).
Then you could use the vector approach I have described in other comments.
I read your question as asking whether the two points exist in different regions. Is this correct? If so, I would probably use a variation of Flood Fill. It's not super difficult to implement (don't implement it recursively, you will almost certainly run out of stack space) and it will be able to look at complex situations like a U-shaped region that has a border between two points, but are not actually different regions. Basically run flood fill, and return true when your coordinate matches the target coordinate (or perhaps when it's close enough for your satisfaction, depending on your use case)
[Edit] Here is an example of flood fill that I wrote for a project of mine. The project is CPAL-licensed, but the implementation is pretty specific to what I use it for anyway, so don't worry about copying parts of it. And it doesn't use recursion, so it should be able to scale to pixel data.
[Edit2] I misunderstood the task. I don't have any example code that does exactly what you're looking for, but I can say that comparing pixel-per-pixel the entire two regions is not something you want to do. You can reduce the complexity by partitioning each region into a larger grid (maybe 25x25 pixels), and comparing those sectors first, and if any of those are close enough, do a pixel-per-pixel comparison just within those two sectors.
[Edit2.5] [Quadtree]3 might be able to help you too. I don't have a lot of experience with it, but I know it's popular in 2D collision detection, which is similar to what you're doing here. Might be worth researching.