OK, I thought this was an interesting problem and decided to have a quick go at it. I have a solution, but if I am honest, it’s not the best.
I am using line primitives to do this, it would probably be better to do this in a shader, and I might even give it a go another time. I think the principle will be the same though “Only draw the circle where it does not intersect another circle”
Anyway, hope this experiment helps
So, here is where I am drawing my circles
This is how it looks when I run my exclusion code on it (rendering the circle in white, and where it intersects another circle it’s rendered transparent)
If I run them at the same time, you can see the intended circles (in black) and the outline (in white)
As I say, I have done this pretty quick and dirty, but it does the job.
Here is my code.
The Draw call looks like this:-
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
List<Vector3> circles = new List<Vector3>()
{
new Vector3(0,0,.5f),
new Vector3(.5f,1f,1),
new Vector3(-.5f,.5f,.5f),
new Vector3(-1f,-.25f,.5f),
new Vector3(-1f,.5f,.5f)
};
foreach (Vector3 c in circles)
{
DrawCircle(c, Color.Black);
var otherCircles = circles.Where(w => w != c).ToList();
DrawCircle(c, Color.White, otherCircles);
}
}
So, I create a list of “circles” each one is a Vector3 with the X,Y being it’s position and the Z it’s radius. Naturally you won’t do this in your draw call, as I say, this is very quick and dirty.
I then loop through each one and draw it, for the outline, I pass it all the other circles, again, quick and dirty.
First call to DrawCircle draws the whole circle in black each time, the second draws the white outline.
DrawCircle, is a grubby old thing used to generate the line primitives for the circle to draw, the key element, the bit that does the draw outline only is this:-
// If the point in inside any of the other circles, don't draw it..
bool insideOtherCircle = circles != null && circles.Any(a => Vector3.Distance(p, new Vector3(a.X, a.Y, -dist)) < a.Z);
The function as a whole looks like this:
protected void DrawCircle(Vector3 circle, Color? color = null, List<Vector3> circles = null, int segments = 256)
{
if (color == null)
{
color = Color.Red;
}
List<VertexPositionColor> pointsList = new List<VertexPositionColor>();
float dist = 5;
List<short> inds = new List<short>();
double step = MathHelper.TwoPi / segments;
Vector3 center = new Vector3(circle.X, circle.Y, -dist);
float radius = circle.Z;
Vector3 p = Vector3.Zero;
for (double angle = 0; angle < MathHelper.TwoPi; angle += step)
{
float x = radius * (float)Math.Cos(angle);
float y = radius * (float)Math.Sin(angle);
p = center + new Vector3(x, y, 0);
// If the point in inside any of the other circles, don't draw it..
bool insideOtherCircle = circles != null && circles.Any(a => Vector3.Distance(p, new Vector3(a.X, a.Y, -dist)) < a.Z);
if (insideOtherCircle) // still record the segment, but make it transparent.
{
pointsList.Add(new VertexPositionColor(p, Color.Transparent));
inds.Add((short)inds.Count);
x = radius * (float)Math.Cos(angle + step);
y = radius * (float)Math.Sin(angle + step);
p = center + new Vector3(x, y, 0);
pointsList.Add(new VertexPositionColor(p, Color.Transparent));
inds.Add((short)inds.Count);
}
else
{
pointsList.Add(new VertexPositionColor(p, color.Value));
inds.Add((short)inds.Count);
x = radius * (float)Math.Cos(angle + step);
y = radius * (float)Math.Sin(angle + step);
p = center + new Vector3(x, y, 0);
pointsList.Add(new VertexPositionColor(p, color.Value));
inds.Add((short)inds.Count);
}
}
DrawPoints(pointsList.ToArray(), inds.ToArray(), segments);
}
And just so you can draw the points for your circle, DrawPoints looks like this:
protected void DrawPoints(VertexPositionColor[] points, short[] index, int primatives)
{
if (basicEffect == null)
basicEffect = new BasicEffect(GraphicsDevice);
GraphicsDevice.BlendState = BlendState.AlphaBlend;
basicEffect.World = Matrix.Identity;
basicEffect.View = Matrix.CreateScale(Vector3.One) *
Matrix.CreateTranslation(new Vector3(0,0,0));
basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.PresentationParameters.BackBufferWidth / (float)GraphicsDevice.PresentationParameters.BackBufferHeight, 0.1f, 1000);
basicEffect.VertexColorEnabled = true;
basicEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.LineList, points, 0, points.Length, index, 0, primatives);
}
Hope this helps and has not added confusion to the topic.
Happy coding