Update: As an alternative and more flexible approach, we can post-process ContourPlot
output to inject contour labels.
ClearAll[addLabels]
addLabels[colors_, labels_: Automatic, threshold_: 1/3][g_Graphics] :=
Module[{diag = ArcLength@Line[Transpose @ PlotRange @ g],
assoc = AssociationThread[colors,
labels /. Automatic ->
MapIndexed[Framed[Style["(" <> ToString@#2[[1]] <> ")", #, 12],
FrameStyle -> None, Background -> White, FrameMargins -> Small] &, colors]]},
ReplaceAll[{d_Directive, l__Line} :> {d, l,
Module[{BSF = BSplineFunction[First@#],
pos = If[ArcLength[#] < diag threshold, {.5},
If[Norm[Subtract @@ #[[1, {1, -1}]]] <=
diag threshold, {.3, .8}, {0, 1}]]},
Text[assoc@FirstCase[d, _?ColorQ], #] & /@ (BSF /@ pos)] & /@ {l}}] @ Normal[g]]
addLabels[colors_, labels_: Automatic, threshold_: 1/3][Legended[g_, legend_]] :=
Legended[addLabels[colors, labels, threshold] @ g, legend]
The first argument, colors
, is a list of colors with the same length as functionlist
in ContourPlot[functionlist,...]
.
The (optional) second argument, labels
, is a list of labels with the same length (if this argument is Automatic
or not provided, the labels are taken to be numbers from 1 to length of colors
).
The third argument, threshold
, is also optional and it is used to control the number of labels and label positions on a contour curve. If a contour curve is too short (in comparison to the length of the diagonal of the plotting area) then a single label is placed in the middle of the curve. If the start and end points of the curve not too close two labels are placed at the two ends of the curve. If they are too close (as in a closed curve) two labels are placed so that they are not too close to each other.
We can use addLabels
in two ways: (1) as the the setting for the option DisplayFunction
by adding the option DisplayFunction -> addLabels[colors, labels, threshold]
to ContourPlot
, or (2) by wrapping ContourPlot[...]
with it, i.e., using addLabels[colors, labels, threshold] @ ContourPlot[...]
.
Examples:
{g1, g2, b} = {x + 2 y, 2 x + y, {2, 2}};
funcs = {g1 - b[[1]], g2 - b[[2]]};
colors = {Blue, Magenta};
options = {Frame -> False, Axes -> True, ContourShading -> None,
ImageSize -> 400, PlotRangePadding -> Scaled[.05]};
ContourPlot[funcs, {x, -2, 4}, {y, -2, 4},
Evaluate @ options,
ContourStyle -> Thread[Directive[colors, Thick]],
DisplayFunction -> addLabels[colors]]
addLabels[colors] @ ContourPlot[funcs, {x, -2, 4}, {y, -2, 4},
Evaluate @ options, ContourStyle -> Thread[Directive[colors, Thick]]]
same picture
With
funcs = {g1 - b[[1]], g2 - b[[2]], Cos[x - 1] + Cos[y - 1] - 1/3,
Sin[ x - 1] + x y Cos[y + 1] - 1/4};
colors = {Blue, Magenta, Red, Green};
we get
We can use the second argument of addLabels
to inject pre-specified labels instead of automatically generated ones:
labels = MapIndexed[Framed[Style["(" <> ToString@#2[[1]] <> ")", #, Bold, 18],
FrameStyle -> None, Background -> White] &, colors];
ContourPlot[funcs, {x, -2, 4}, {y, -2, 4},
Evaluate @ options,
ContourStyle -> Thread[Directive[colors, Thick]],
DisplayFunction -> addLabels[colors, labels]]
Add the option PlotLegends->"Expressions"
to get
Original answer:
{g1, g2, b} = {x + 2 y, 2 x + y, {2, 2}};
colors = {Blue, Magenta};
labels = MapIndexed[Framed[Style["(" <> ToString@#2[[1]] <> ")", #, 18],
FrameStyle -> None, Background -> White] &, colors];
Show[MapThread[
ContourPlot[#, {x, -2, 4}, {y, -2, 4},
Frame -> False,
Axes -> True,
ContourShading -> None,
Contours -> {0},
ContourStyle -> Directive[#2, Thick],
ContourLabels -> Function[{x, y, z}, Text[#3, {x, y}]],
ImageSize -> 400] &,
{{g1 - b[[1]], g2 - b[[2]]}, colors, labels}]]