5
$\begingroup$

It looks like ChartBaseStyle in BarChart3D doesn't work in Grid mode when the matrix is larger than 9x9:

BarChart3D[RandomReal[1, {9, 9}], ChartLayout -> "Grid", 
 ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}], 
 BarSpacing -> {0.7, 0.7}]

enter image description here

BarChart3D[RandomReal[1, {10, 10}], ChartLayout -> "Grid", 
 ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}], 
 BarSpacing -> {0.7, 0.7}]

enter image description here

Test done on mathematica 12 on MacOS Mojave

$\endgroup$
3
$\begingroup$

This appears to be an oversight in how EdgeForm[None] (the default for $\geq100$ bars) is handled when combining the different style directives for the bars1. Essentially, EdgeForm[None] overrides any explicit EdgeForm settings, which is why they are not applied in the second case (where there are $\geq100$ bars).

To circumvent this, you can execute the following code once before plotting anything:

Charting`iBarChart3D[{}];
prot = Unprotect@Charting`iBarChart3D;
DownValues@Charting`iBarChart3D = 
  DownValues@Charting`iBarChart3D /.
   HoldPattern[
     lhs : _["ChartBaseStyle"] = cd_[
       If[cond_,
        then_,
        else_
        ],
       rest__
       ]
     ] :> (
     If[cond,
      lhs = cd[rest];
      If[! MemberQ[lhs, _EdgeForm],
       AppendTo[lhs, then]
       ],
      lhs = cd[else, rest]
      ]
     );
Protect /@ prot;

Now the second example works as expected:

BarChart3D[RandomReal[1, {10, 10}], ChartLayout -> "Grid", 
 ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}], 
 BarSpacing -> {0.7, 0.7}]

enter image description here

This works by modifying the way the default style for many bars in handled: Instead of simply adding EdgeForm[None] to the list of directives (which overrides any other specifications), we combine the remaining directives first. If no explicit EdgeForm directive is present, we manually append EdgeForm[] to the list.

1The relevant code is in Charting`iBarChart3D (look for styleData["ChartBaseStyle"] = ...; handles the default styles for the bars), Charting`ConstructDirective (handles the combining of the styles) and Charting`ConstructDirectiveOrNone (handles the override of EdgeForm[None])

$\endgroup$
  • $\begingroup$ Thanks @Lukas Land , that worked perfectly. Do you reckon I should add the bug tag or is it an intended feature of the language? $\endgroup$ – Fraccalo Oct 4 at 20:16
  • $\begingroup$ I think it can be considered a bug - after all, it does work for less than 100 bars, and it doesn't really make sense to override the users choice. Btw, depending on what you want to do, DiscretePlot3D may be more suited for your needs - it is something like the modern version of BarChart3D $\endgroup$ – Lukas Lang Oct 4 at 20:42
  • $\begingroup$ Cheers, I'll play with DiscretePlot3D and see if it actually fits better my purposes. I've been using BarChart3D for about 5 years now, but I've honestly never been fully satisfied with it. $\endgroup$ – Fraccalo Oct 4 at 20:55
1
$\begingroup$

The threshold of 100 bars is hard-coded in Charting`iBarChart3D. In addition to its effect on styling as in OP, it also affects tooltips and highlighting of bars.

Fortunately, 100 appears only as the value of this threshold in Charting`iBarChart3D

Cases[DownValues[Charting`iBarChart3D], _[___, _[___, 100, ___], ___], All]

{If[System`BarChart3DDump`numberOfBars >= 100, System`BarChart3DDump`highlight = None; System`BarChart3DDump`labelingfunction = None], If[System`BarChart3DDump`numberOfBars >= 100, EdgeForm[], EdgeForm[GrayLevel[0.5]]], -(System`BarChart3DDump`numberOfBars/100)}

So you can define a pre-processing function that replaces 100 with a larger value in Charting`iBarChart3D using Internal`InheritedBlock (see this answer by rm-rf):

preProcess = Function[x, Internal`InheritedBlock[{Charting`iBarChart3D}, 
   Unprotect[Charting`iBarChart3D]; 
   DownValues[Charting`iBarChart3D] = DownValues[Charting`iBarChart3D] /. 100 -> 200; x], 
   HoldFirst];

and wrap BarChart3D with preProcess:

preProcess@BarChart3D[RandomReal[1, {12, 12}], 
    ChartLayout -> "Grid",
    ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}],
    BarSpacing -> {0.7, 0.7}] 

enter image description here

Alternatively, you can define your own barChart3D that takes the same arguments and options as BarChart3D:

barChart3D = Internal`InheritedBlock[{Charting`iBarChart3D},
   Unprotect[Charting`iBarChart3D];
   DownValues[Charting`iBarChart3D] = DownValues[Charting`iBarChart3D] /. 100 -> 200; 
   BarChart3D[##]] &;

barChart3D[RandomReal[1, {12, 12}], 
  ChartLayout -> "Grid",
  ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}],
  BarSpacing -> {0.7, 0.7}] 

same picture

A better alternative may be to make this threshold an option rather than replacing the hard-coded threshold with another hard-coded one:

ClearAll[barChart3D]
Options[barChart3D] = Append[Options[BarChart3D], "Threshold" -> 100];
barChart3D[args__, o : OptionsPattern[]] := Internal`InheritedBlock[{Charting`iBarChart3D},
   Unprotect[Charting`iBarChart3D];
   DownValues[Charting`iBarChart3D] = DownValues[Charting`iBarChart3D] /. 
     100 -> OptionValue["Threshold"]; 
   BarChart3D[args, FilterRules[{o}, Options[BarChart3D]]]];

Example:

Row[{barChart3D[RandomReal[1, {12, 12}], ChartLayout -> "Grid",
   ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}],
   BarSpacing -> {0.7, 0.7}, ImageSize -> 400], 
  barChart3D[RandomReal[1, {12, 12}], ChartLayout -> "Grid",
   ChartBaseStyle -> EdgeForm[{Thickness[.01], Opacity[1]}],
   BarSpacing -> {0.7, 0.7}, ImageSize -> 400, 
   "Threshold" -> 200]}, Spacer[10]]

enter image description here

Note: If you want to suppress tooltips and bar highlighting you can always use PerformanceGoal -> "Speed".

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.