TransWikia.com

Computing and dynamically displaying the intersections of three circles

Mathematica Asked by joka on February 22, 2021

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

enter image description here

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[...] *)

Answered by Michael E2 on February 22, 2021

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.

demo

Answered by m_goldberg on February 22, 2021

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}}])]]

enter image description here

To add a legend use

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:

enter image description here

Answered by kglr on February 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP