# Computing and dynamically displaying the intersections of three circles

I don't understand why I get the following output:

when in fact I was expecting to get $Failed, 1 or 2 instead of res$xxxx.

I am especially baffled because I think that.

Graphics[{Red, Circle[{Dynamic[x0], Dynamic[y0]}, Dynamic[r0]]}]


is syntactically equivalent to

allIntersections[
{Dynamic[x0], Dynamic[y0]}, Dynamic[r0],
{Dynamic[x1], Dynamic[y1]}, Dynamic[r1], {Dynamic[x2], Dynamic[y2]}, Dynamic[r2]]


and although the former displays and updates as expected, the later doesn't.

### Code

(* do two circles intersect in one or two points? *)
nIntersections[o0_, r0_, o1_, r1_] :=
Module[{res, hyp},
If[
(* concentric circles *)
o0 == o1,
res = $$Failed, (* non-concentric circles *) hyp = Total[(o1 - o0)^2]; res = Which[ (* do not intersect *) r0 + r1 < hyp,$$Failed,
(* single intersection point *)
r0 + r1 == hyp, 1,
(* two intersection points *)
r0 + r1 > hyp, 2,
(* something went wrong *)
True, $Failed]]; res] allIntersections[o0_, r0_, o1_, r1_, o2_, r2_] := Module[{ni01, ni02, ni12, nis}, (* count the number of possible intersections *) ni01 = nIntersections[Dynamic[o0], Dynamic[r0], Dynamic[o1], Dynamic[r1]]; ni02 = nIntersections[Dynamic[o0], Dynamic[r0], Dynamic[o2], Dynamic[r2]]; ni12 = nIntersections[Dynamic[o1], Dynamic[r1], Dynamic[o2], Dynamic[r2]]; nis = {ni01, ni02, ni12}; nis] (* styled row *) row = (Riffle[#, " "] &) /* Row; sldr[x_, tag_, x0_, xe_, dx_] := {tag, Slider[Dynamic[x], {x0, xe, dx}], Dynamic[x]} (* slider controls *) sldrs[{xs__}, {tags__}, {x0s__}, {xes__}, {dxs__}] := Module[{args = {{xs}, {tags}, {x0s},{xes}, {dxs}}}, MapThread[row[sldr[##]]&, args]] (* entry point *) DynamicModule[ {x0, y0, r0, x1, y1, r1, x2, y2, r2, vars, tags, x0s, xes, dxs, args, rng}, vars = {x0, y0, r0, x1, y1, r1, x2, y2, r2}; tags = {"x0", "y0", "r0", "x1", "y1", "r1", "x2", "y2", "r2"}; x0s = {0, 0, 0, 0, 0, 0, 0, 0, 0}; xes = {9, 9, 9, 9, 9, 9, 9, 9, 9}; dxs = {0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001}; rng = Transpose @ Map[Through[{Min, Max}[#]]&, {x0s, xes}]; args = {vars, tags, x0s, xes, dxs}; { (* display *) {Graphics[{Red, Circle[{Dynamic[x0], Dynamic[y0]}, Dynamic[r0]]}], Graphics[{Darker @ Green, Circle[{Dynamic[x1], Dynamic[y1]}, Dynamic[r1]]}], Graphics[{Darker@Blue, Circle[{Dynamic[x2], Dynamic[y2]}, Dynamic[r2]]}]} // Show[#, PlotRange -> rng] &, (* controls *) Column[sldrs @@ args, Alignment -> Left], (* numerical output *) allIntersections[ {Dynamic[x0], Dynamic[y0]}, Dynamic[r0], {Dynamic[x1], Dynamic[y1]}, Dynamic[r1], {Dynamic[x2], Dynamic[y2]}, Dynamic[r2]]}, Initialization :> ( x0 = 2.6; y0 = 4; r0 = 1.9; x1 = 4.; y1 = 4.; r1 = 1.65; x2 = 5.5; y2 = 4.; r2 = 1.4;)]  ## 3 Answers The answer to why you get the res$nnn in the output:

The condition o0 == o1 of If in nIntersections evaluates to neither True nor False, because the arguments are wrapped in Dynamic. I will insert two hooks, murf and foo, to trace what happens.

nIntersections[o0_, r0_, o1_, r1_] := Module[{res, hyp},
If[(*concentric circles*)o0 == o1,
murf = True;    (* first case: o0 == o1 is True *)
res = $$Failed,(*non-concentric circles*) murf = False; (* second case: o0 == o1 is False *) hyp = Total[(o1 - o0)^2]; res = Which[(*do not intersect*) r0 + r1 < hyp, foo = Less;$$Failed,(*single intersection point*)
r0 + r1 == hyp,
foo = Equal;
1,(*two intersection points*)
r0 + r1 > hyp,
foo = Greater;
2,(*something went wrong*)
True,
foo = True;
$Failed], murf = Equal (* third case: o0 == o1 does not evaluate to True or False *) ]; res]  Check after executing the DynamicModule: foo murf  (* foo <-- shows Which was never evaluated Equal <-- show If evaluated third case (4th argument) *)  So indeed, the condition in If was neither True nor False. It can probably be fixed by removing Dynamic from the arguments. Dynamic need only wrap the output that is displayed, I think. Appendix: Elaboration of remark about removing Dynamic First I removed all the Dynamic[] wrappers in the OP's code with the following, and then I edited Dynamic back in where it is needed in the output: Hold[< pasted OP's code >] /. Dynamic[x_] :> x // InputForm  The additions of Dynamic[] are preceded by comments. I gave two alternative codes for nIntersections but resisted other refactoring. nIntersections[o0_, r0_, o1_, r1_] := RegionMeasure@RegionIntersection[Circle[o0, r0], Circle[o1, r1]] /. 0 -> $$Failed; nIntersections[o0_, r0_, o1_, r1_] := With[{r2 = EuclideanDistance[o0, o1]}, 1 + Min[Sign[r0 + r1 - r2], Sign[r1 + r2 - r0], Sign[r2 + r0 - r1]] /. 0 ->$$Failed ]; allIntersections[o0_, r0_, o1_, r1_, o2_, r2_] := Module[{ni01, ni02, ni12, nis}, ni01 = nIntersections[o0, r0, o1, r1]; ni02 = nIntersections[o0, r0, o2, r2]; ni12 = nIntersections[o1, r1, o2, r2]; nis = {ni01, ni02, ni12}; nis]; row = (Riffle[#1, " "] & ) /* Row; sldr[x_, tag_, x0_, xe_, dx_] := {tag, Slider[x, {x0, xe, dx}], x}; sldrs[{xs__}, {tags__}, {x0s__}, {xes__}, {dxs__}] := Module[{args = {{xs}, {tags}, {x0s}, {xes}, {dxs}}}, MapThread[row[sldr[##1]] & , args]]; DynamicModule[ {x0, y0, r0, x1, y1, r1, x2, y2, r2, vars, tags, x0s, xes, dxs, args, rng}, vars = {x0, y0, r0, x1, y1, r1, x2, y2, r2}; tags = {"x0", "y0", "r0", "x1", "y1", "r1", "x2", "y2", "r2"}; x0s = {0, 0, 0, 0, 0, 0, 0, 0, 0}; xes = {9, 9, 9, 9, 9, 9, 9, 9, 9}; dxs = {0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001}; rng = Transpose[(Through[{Min, Max}[#1]] & ) /@ {x0s, xes}]; (*** wrap vars in Dynamic[] ***) args = {Dynamic /@ vars, tags, x0s, xes, dxs}; {(Show[#1, PlotRange -> rng] & )[ {(*** wrap Circle[]s in Dynamic[] ***) Graphics[{Red, Dynamic@Circle[{x0, y0}, r0]}], Graphics[{Darker[Green], Dynamic@Circle[{x1, y1}, r1]}], Graphics[{Darker[Blue], Dynamic@Circle[{x2, y2}, r2]}]}], Column[sldrs @@ args, Alignment -> Left], (*** wrap allIntersections[] in Dynamic[] ***) Dynamic@allIntersections[{x0, y0}, r0, {x1, y1}, r1, {x2, y2}, r2]}, Initialization :> (x0 = 2.6; y0 = 4; r0 = 1.9; x1 = 4.; y1 = 4.; r1 = 1.65; x2 = 5.5; y2 = 4.; r2 = 1.4; )]  Other alternatives Row[sldr[##1], " "] (* instead of row[sldr[##1]] *) (* set up vars and the rest can be constructed from it *) tags = Subscript @@ (* works if vars named by char + number *) Characters@First@StringSplit[SymbolName[#], "$"] & /@ vars;
x0s = 0 vars;
xes = 9 + x0s;
dxs = 0.001 + x0s;

Graphics[<all three circles>] (* instead of Show[...] *)

• yes, it is not evaluating If how I thought it were; however, removing Dynamic from the arguments does not resolve it
– joka
Oct 11 '20 at 16:00
• @joka I show how to remove Dynamic from the arguments but keep it on the output that is displayed. Oct 12 '20 at 13:50

There are many errors in your code. The most serious is that your definition of nIntersections doesn't compute the intersection correctly. When I rewrite your code as:

(*do two circles intersect in one or two points?*)
With[{ϵ = .01},
nIntersections[o0_, r0_, o1_, r1_] :=
If[(*concentric circles*)o0 == o1, 0,
(*non-concentric circles*)
With[{d = EuclideanDistance[o0, o1]},
Which[
(*single intersection point*)Abs[r0 + r1 - d] < ϵ, 1,
(*do not intersect*)r0 + r1 < d, 0,
(*two intersection points*)r0 + r1 > d, 2,
(*something went wrong*)True, $Failed]]]] (*styled row*) row = (Riffle[#, " "] &) /* Row; sldr[x_, tag_, x0_, xe_, dx_] := {tag, Slider[Dynamic[x], {x0, xe, dx}], Dynamic[x]} (*slider controls*) sldrs[{xs__}, {tags__}, {x0s__}, {xes__}, {dxs__}] := Module[{args = {{xs}, {tags}, {x0s}, {xes}, {dxs}}}, MapThread[row[sldr[##]] &, args]] DynamicModule[ {x0, y0, r0, x1, y1, r1, x2, y2, r2, vars, tags, x0s, xes, dxs, args, rng}, vars = {x0, y0, r0, x1, y1, r1, x2, y2, r2}; tags = {"x0", "y0", "r0", "x1", "y1", "r1", "x2", "y2", "r2"}; x0s = {0, 0, 0, 0, 0, 0, 0, 0, 0}; xes = {9, 9, 9, 9, 9, 9, 9, 9, 9}; dxs = {0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001}; rng = Transpose @ Map[Through[{Min, Max}[#]]&, {x0s, xes}]; args = {vars, tags, x0s, xes, dxs}; (*display*) Column[ {Show[ Graphics[{Red, Circle[{x0, y0}, r0]}], Graphics[{Darker@Green, Circle[{x1, y1}, r1]}], Graphics[{Darker@Blue, Circle[{x2, y2}, r2]}], PlotRange -> rng], (*sliders*) Column[sldrs @@ args, Alignment -> Left], (*intersection count*) Dynamic @ {nIntersections[{x0, y0}, r0, {x1, y1}, r1], nIntersections[{x0, y0}, r0, {x2, y2}, r2], nIntersections[{x1, y1}, r1, {x2, y2}, r2]}}], Initialization :> ( x0 = 2.6; y0 = 4; r0 = 1.9; x1 = 4.; y1 = 4.; r1 = 1.65; x2 = 5.5; y2 = 4.; r2 = 1.4;)]  I get something that works and even seems to get the intersections correctly. • you're right, my distance formula was missing a square root; other than that-please correct me if I'm wrong-I only see minor changes in your code; allIntersections is still not operational and you had to unwrap it in order for it to work; that's what I didn't get in the first place, why allIntersections refuses to cooperate when instead eg. sldr, which includes a Dynamic[x] (outside Slider I mean) works as expected? – joka Oct 11 '20 at 21:09 • @joka Another problem is that nIntersections (this answer) returns 2 when one circle lies inside the other. Shouldn't it be 0? Oct 11 '20 at 21:17 • @MichaelE2 sure-preferably it should return $Failed-but like I said these are minor issues; my problem is with Dynamic and what I understand as 'weird' behavior
– joka
Oct 12 '20 at 7:28
• @joka. allIntersections can be made to work by removing the Dynamic wrappers. I rejected the whole function because I think it simply isn't needed. Oct 12 '20 at 14:24

Michael E2. and m_goldberg's detailed answers address directly the questions in OP. This post suggests an alternative approach using LocatorPane with locators to modify centers ("●") and radii ("◆"):

DynamicModule[{pts = {{-5., 0.}, {0., 0.}, {5., 0.}, {-5., 2.}, {0., 1.}, {5., 3.}},
rd = {2., 1., 3}, ri, circles},
LocatorPane[Dynamic[pts,
With[{i = CurrentValue["CurrentLocatorPaneThumb"]},
If[1 <= i <= 3, pts[[i]] = #[[i]];
pts[[i + 3]] = pts[[i]] + rd[[i]] Normalize[pts[[i + 3]] - #[[i]]],
pts[[i]] = #[[i]]; rd[[i - 3]] = Norm[#[[i]] - pts[[i - 3]]]]] &],
Deploy @ Dynamic @ Legended[Framed @
Graphics[{Black, PointSize[Large], ri = RegionIntersection @@@
Subsets[circles = Circle @@@ Transpose[{pts[[;; 3]], rd}], {2}] /.
_EmptyRegion -> {},
Transpose[{{ Green, Red, Blue}, circles}]},
PlotRange -> 20, ImageSize -> 1 -> 12, Frame -> False], None],
Appearance -> (Style[##, 12] & @@@ Tuples[{{"●", "◆"}, {Green, Red, Blue}}])]]


legend = Grid[{Prepend[Style["○", 32, #] & /@ {Green, Red, Blue}, ""],
{"radius", ## & @@ Round[#2, 10.^-3]},
{"center", ## & @@ Round[#[[;; 3]], 10.^-3]},
{Item[Row[{"intersections :",
Total[Flatten[#3] /. Point -> Length]}], Alignment -> Left,
Background -> LightBlue], SpanFromLeft, SpanFromLeft},
## & @@ Thread[{Row[#, Spacer[1]] & /@
Subsets[Style["○", 32, #] & /@ {Green, Red, Blue}, {2}], #3 /.
Point -> (Row[#, ", "] &), SpanFromLeft, SpanFromLeft}]},
Dividers -> All, ItemSize -> {{9, 9, 9, 9}, Automatic}] &;


and replace Legended[..., None] above with Legended[..., Placed[legend[pts, rd, ri], Right]] to get: