Skip to main content
replaced http://mathematica.stackexchange.com/ with https://mathematica.stackexchange.com/
Source Link

To overcome the issue I mention in the commentthe comment, also to make it more general, here is an upgraded version, which can deal with clipping and pointwise exclusion, and preserve styles specified in the original plot.

To overcome the issue I mention in the comment, also to make it more general, here is an upgraded version, which can deal with clipping and pointwise exclusion, and preserve styles specified in the original plot.

To overcome the issue I mention in the comment, also to make it more general, here is an upgraded version, which can deal with clipping and pointwise exclusion, and preserve styles specified in the original plot.

added 4466 characters in body
Source Link
Silvia
  • 27.6k
  • 3
  • 85
  • 166

Update:

To overcome the issue I mention in the comment, also to make it more general, here is an upgraded version, which can deal with clipping and pointwise exclusion, and preserve styles specified in the original plot.

The main function is this discontinuousHighlighter:

Clear[discontinuousHighlighter]
discontinuousHighlighter[origplot_, {excluMarker_, clipMarker_}, radius_: 3] :=
 Module[{clipIntvX, black = Black, white, edgestyle},
        edgestyle = {AbsoluteThickness[1], black};
        white = If[# === None, White, #] &[Background /. AbsoluteOptions[origplot, Background]];
        (* clipped intervals: *)
        clipIntvX = Interval @@ Join @@ Cases[origplot,
                        {style___,lines:Longest[Line[_]..],___}/;Not[FreeQ[{style}, clipMarker]]:>{lines}[[All,1,All,1]],
                                              ∞];
        origplot /.
            (* exclusion boundary markers on curve: *)
            {style___, Point[pts__]} /; Not[FreeQ[{style}, excluMarker]] :>
             (black = If[# === {}, black, #[[-1]]] &@
                          DeleteCases[Cases[{style}, (RGBColor | Hue)[__], ∞], excluMarker];
              edgestyle = Join[edgestyle, {style /. Directive | EdgeForm -> Sequence} // Flatten];
              Flatten[{
                       EdgeForm[edgestyle],
                       FaceForm[white],
                       Module[{pt = #},
                              If[IntervalMemberQ[clipIntvX, pt[[1]]],
                                 {},
                                 Disk[pt, Offset[radius]]
                                ]
                          ] & /@ pts} /. excluMarker -> Sequence[]]
             ) /.
           (* exclusion markers on x axis: *)
           {style___, lines : (Line[_] ..)} /; FreeQ[{style}, clipMarker] && Not[FreeQ[{style}, excluMarker]] :>
            Join[
                 {style, lines} /. excluMarker -> Sequence[],
                 Flatten[{
                          EdgeForm[edgestyle],
                          FaceForm[black],
                          Disk[{Mean[#[[All, 1]]], 0}, Offset[radius]] & @@@ {lines}
                         }] /. excluMarker -> black
                ] /.
          (* clipped interval: *)
          {style___,lines:Longest[Line[_]..],post___}/;Not[FreeQ[{style},clipMarker]]&&Not[FreeQ[{post},clipMarker]] :>
           ({style, lines} /. clipMarker -> Sequence[]) //
          Show[#, PlotRangeClipping -> False, PlotRangePadding -> Scaled[.05]] &
       ]

Basically, we calculate the x coordinates of the filled disks from the exclusion lines, and convert the endpoints on curve to hollow disks.

To use the function, wrap Plot[...] with it:

Module[{excluMarker = RGBColor @@ RandomReal[1, 3], clipMarker = RGBColor @@ RandomReal[1, 3]},
       discontinuousHighlighter[
                                Plot[Exp[(Ceiling[Sin[x^2]] - 9/10) x] - 1.01, {x, -10, 10},
                                     PlotStyle -> Directive[AbsoluteThickness[3]],
                                     PlotPoints -> 1000, MaxRecursion -> 15,
                                     PlotRange -> {-1, 1},
                                     ExclusionsStyle -> {
                                                         Directive[GrayLevel[.8], Dashed, excluMarker],
                                                         Directive[EdgeForm[{Lighter[Purple]}], excluMarker]
                                                        },
                                     ClippingStyle -> Directive[Darker[Green], Thin, clipMarker],
                                     Background -> Lighter[Yellow, .9]
                                    ],
                                {excluMarker, clipMarker},
                                3 ]
      ]

better result

You can use any style in ExclusionsStyle and ClippingStyle. As long as they contain excluMarker and clipMarker, the result should be parsed correctly by discontinuousHighlighter. And hollow disks will automatically fit the background color.

Original answer:

Update:

To overcome the issue I mention in the comment, also to make it more general, here is an upgraded version, which can deal with clipping and pointwise exclusion, and preserve styles specified in the original plot.

The main function is this discontinuousHighlighter:

Clear[discontinuousHighlighter]
discontinuousHighlighter[origplot_, {excluMarker_, clipMarker_}, radius_: 3] :=
 Module[{clipIntvX, black = Black, white, edgestyle},
        edgestyle = {AbsoluteThickness[1], black};
        white = If[# === None, White, #] &[Background /. AbsoluteOptions[origplot, Background]];
        (* clipped intervals: *)
        clipIntvX = Interval @@ Join @@ Cases[origplot,
                        {style___,lines:Longest[Line[_]..],___}/;Not[FreeQ[{style}, clipMarker]]:>{lines}[[All,1,All,1]],
                                              ∞];
        origplot /.
            (* exclusion boundary markers on curve: *)
            {style___, Point[pts__]} /; Not[FreeQ[{style}, excluMarker]] :>
             (black = If[# === {}, black, #[[-1]]] &@
                          DeleteCases[Cases[{style}, (RGBColor | Hue)[__], ∞], excluMarker];
              edgestyle = Join[edgestyle, {style /. Directive | EdgeForm -> Sequence} // Flatten];
              Flatten[{
                       EdgeForm[edgestyle],
                       FaceForm[white],
                       Module[{pt = #},
                              If[IntervalMemberQ[clipIntvX, pt[[1]]],
                                 {},
                                 Disk[pt, Offset[radius]]
                                ]
                          ] & /@ pts} /. excluMarker -> Sequence[]]
             ) /.
           (* exclusion markers on x axis: *)
           {style___, lines : (Line[_] ..)} /; FreeQ[{style}, clipMarker] && Not[FreeQ[{style}, excluMarker]] :>
            Join[
                 {style, lines} /. excluMarker -> Sequence[],
                 Flatten[{
                          EdgeForm[edgestyle],
                          FaceForm[black],
                          Disk[{Mean[#[[All, 1]]], 0}, Offset[radius]] & @@@ {lines}
                         }] /. excluMarker -> black
                ] /.
          (* clipped interval: *)
          {style___,lines:Longest[Line[_]..],post___}/;Not[FreeQ[{style},clipMarker]]&&Not[FreeQ[{post},clipMarker]] :>
           ({style, lines} /. clipMarker -> Sequence[]) //
          Show[#, PlotRangeClipping -> False, PlotRangePadding -> Scaled[.05]] &
       ]

Basically, we calculate the x coordinates of the filled disks from the exclusion lines, and convert the endpoints on curve to hollow disks.

To use the function, wrap Plot[...] with it:

Module[{excluMarker = RGBColor @@ RandomReal[1, 3], clipMarker = RGBColor @@ RandomReal[1, 3]},
       discontinuousHighlighter[
                                Plot[Exp[(Ceiling[Sin[x^2]] - 9/10) x] - 1.01, {x, -10, 10},
                                     PlotStyle -> Directive[AbsoluteThickness[3]],
                                     PlotPoints -> 1000, MaxRecursion -> 15,
                                     PlotRange -> {-1, 1},
                                     ExclusionsStyle -> {
                                                         Directive[GrayLevel[.8], Dashed, excluMarker],
                                                         Directive[EdgeForm[{Lighter[Purple]}], excluMarker]
                                                        },
                                     ClippingStyle -> Directive[Darker[Green], Thin, clipMarker],
                                     Background -> Lighter[Yellow, .9]
                                    ],
                                {excluMarker, clipMarker},
                                3 ]
      ]

better result

You can use any style in ExclusionsStyle and ClippingStyle. As long as they contain excluMarker and clipMarker, the result should be parsed correctly by discontinuousHighlighter. And hollow disks will automatically fit the background color.

Original answer:

Source Link
Silvia
  • 27.6k
  • 3
  • 85
  • 166

If you want to draw those disks and circles automatically, a convenient way would be post-processing a styled plot.

First we generate a unique mark for the ExclusionsStyle and ClippingStyle:

excluColor = RGBColor @@ RandomReal[1, 3];
clipColor = RGBColor @@ RandomReal[1, 3]

Use them to style the exclusions:

origplot = Plot[Floor[Tan[x]], {x, 0, π},
                PlotStyle -> Directive[Thick],
                Exclusions -> {Automatic, Cos[x] == 0},
                ExclusionsStyle -> {excluColor, excluColor}]

original exclusion plot

and the clipped intervals (we need this because the ExclusionsStyle seems to have a higher priority than ClippingStyle, and we don't want to mistake the clipped intervals for the discontinuous intervals):

clipplot = Plot[Floor[Tan[x]], {x, 0, π},
                Exclusions -> {Automatic, Cos[x] == 0},
                ExclusionsStyle -> None,
                ClippingStyle -> clipColor]

clipIntv = Interval @@ Cases[clipplot,
                             {___, clipColor, lines : (Line[_] ..)} :>
                                    Through[{Min, Max}@Flatten[#[[All, 1]] & @@@ {lines}]],
                             ∞];

To draw the circles correctly, we need to know the aspect-ratio and set a appropriate radius:

radius = 0.02;
{rgx, rgy} = PlotRange.{-1, 1} /. AbsoluteOptions[origplot, PlotRange];
asprat = AspectRatio /. AbsoluteOptions[origplot, AspectRatio];

The remaining work is some replacements:

origplot /.
  {excluColor, lines : (Line[_] ..)} :>
   Flatten[{Black, Module[{x = Mean[#[[All, 1]]]},
                          If[IntervalMemberQ[clipIntv, x],
                             {},
                             Disk[{x, 0}, radius {1, 1/asprat rgy/rgx}]
                            ]
                          ] & @@@ {lines}}] /.
 {excluColor, Point[pts__]} :>
  Flatten[{EdgeForm[Black], FaceForm[White], Module[{pt = #},
                         If[IntervalMemberQ[clipIntv, pt[[1]]],
                            {},
                            Disk[pt, radius {1, 1/asprat rgy/rgx}]
                            ]
                        ] & /@ pts}] //
 Show[#, PlotRangeClipping -> False, PlotRangePadding -> Scaled[.05]] &

discontinous points