8
$\begingroup$

Suppose we have an array of color data

colorArray = ParallelTable[With[{v = RandomReal[]},
Append[List @@ ColorData["LightTemperatureMap"][v], If[v >= .8, .5, 0]]
], {z, 0, .1, .005}, {y, 0, 1, .1}, {x, 0, 10, 2}];

And we use Image3D to visualize them

is = 350;
Image3D[
colorArray, "Real", ColorSpace -> "RGB",
ViewPoint -> {1.3, -2.4, 2}, SphericalRegion -> True,
Boxed -> True, BoxRatios -> {1, 1, 1},
Axes -> True, AxesLabel -> {"X", "Y", "Z"},
ImageSize -> is, ImageResolution -> 3*is]

enter image description here

The problem is that the ticks are the relative position of each RGBColor element in the array, not the actual range of x, y, and z ({z, 0, .1, .005}, {y, 0, 1, .1}, {x, 0, 10, 2}). My question is how can we change the ticks to show the actual range of x, y, and z? A direct implement seems impossible because in colorArray no data about the original x, y, and z values are stored (actually we cannot store them in colorArray due to the requirement of Image3D).

$\endgroup$
2
  • 1
    $\begingroup$ Have you seen Raster3D? This allows you to specify the range of x, y and z. $\endgroup$ Nov 6, 2013 at 9:52
  • $\begingroup$ @Simon Woods, Yes I know it, but the Image3D has better plot quality for medium sized bins, so I wish there could be a solution for Image3D. I have tried to extract a bare frame and use Show to combine it and the 3D image, but that did not work. $\endgroup$
    – saturasl
    Nov 6, 2013 at 17:56

1 Answer 1

9
$\begingroup$

Edit Thanks to matheorem for simplifying this a good deal.

Image3D is relatively new and doesn't have all the options that other Graphics3D objects have, but we can work with it because it can be used with Show like any other 3D graphics object. The strategy here is to only give those options to Image3D which are unique to it, while giving all other options to Show.

Image3D produces a plot where the data ranges for the three dimensions are equal to the dimensions of the input array, similar to the 2D functions ArrayPlot and MatrixPlot. However, unlike those functions we can't supply the option DataRange to Image3D. So we need to manually specify a scaling function for the tickmarks. That is, when you give the Ticks manually, you can specify the position and the label for each tick mark, i.e. in a list like $\{\{x_1, label_1\},\{x_2, label_2\},...\}$. This is tedious to do by hand, so we can do it functionally.

There are two ways I can think of to do this. The first is by using the CustomTicks package and the second is by sending a function to Ticks.

I like to use the CustomTicks package, available here: http://library.wolfram.com/infocenter/Demos/5599/ It makes it really easy to rescale the tickmark values. Just download the CustomTicks.m file and place it somewhere in your $Path.

And this isn't really clear from this example, because the data is random, but in order for the plot ranges to match up it is necessary to reverse the first two levels of the 3D array.

Needs["CustomTicks`"]

colorArray = 
  ParallelTable[
   With[{v = RandomReal[]}, 
    Append[List @@ ColorData["LightTemperatureMap"][v], 
     If[v >= .8, .5, 0]]], {z, 0, .1, .005}, {y, 0, 1, .1}, {x, 0, 10,
     2}];
{xmax, ymax, zmax} = {10, 1, .1};
{zdim, ydim, xdim} = Dimensions[colorArray][[;; 3]];


Show[Image3D[Reverse[colorArray,{1,2}], "Real", ColorSpace -> "RGB"],
 ViewPoint -> {1.3, -2.4, 2},
 SphericalRegion -> True,
 Boxed -> True,
 BoxRatios -> {1, 1, 1},
 Axes -> True,
 AxesLabel -> {"X", "Y", "Z"},
 ImageSize -> 350,
 ImageResolution -> 750,
 Ticks -> {
   LinTicks[0, 10, TickPostTransformation -> (# xdim/xmax &)], 
   LinTicks[0, 1, TickPostTransformation -> (# ydim/ymax &)], 
   LinTicks[0, .1, TickPostTransformation -> (# zdim/zmax &)]}]

enter image description here

A somewhat less elegant solution that doesn't rely on an external package is

Show[Image3D[Reverse[colorArray,{1,2}], "Real", ColorSpace -> "RGB"],
 ViewPoint -> {1.3, -2.4, 2},
 SphericalRegion -> True,
 Boxed -> True,
 BoxRatios -> {1, 1, 1},
 Axes -> True,
 AxesLabel -> {"X", "Y", "Z"},
 ImageSize -> 350,
 ImageResolution -> 750,
 Ticks -> {
   Function[{min, max}, {# xdim/xmax, NumberForm[N@#, {3, 2}] } & /@ 
     FindDivisions[Round[{min, max}] xmax/xdim, 5]], 
   Function[{min, max}, {# ydim/ymax, NumberForm[N@#, {3, 2}] } & /@ 
     FindDivisions[Round[{min, max}] ymax/ydim, 5]], 
   Function[{min, max}, {# zdim/zmax, NumberForm[N@#, {3, 2}] } & /@ 
     FindDivisions[Round[{min, max}] zmax/zdim, 5]]
   }]

enter image description here

$\endgroup$
19
  • 1
    $\begingroup$ Perfect solution, thank you! $\endgroup$
    – saturasl
    Nov 6, 2013 at 20:58
  • $\begingroup$ Hi, Jason B. What do you mean by " edit the tickmarks manually "? I didn't find a way for manually editing $\endgroup$
    – matheorem
    Feb 7, 2016 at 11:22
  • $\begingroup$ @matheorem I meant enter the tickmarks for the contouplot3D manually, sorry. You can enter a list for the tickmarks that would let you specify the positions, lengths, and label for each tickmark. But it's easier to specify a scaling function like above. $\endgroup$
    – Jason B.
    Feb 7, 2016 at 11:35
  • $\begingroup$ @JasonB Yeah, this package is really handy. Thanks : ) $\endgroup$
    – matheorem
    Feb 7, 2016 at 14:41
  • $\begingroup$ @JasonB Hi, Jason B. I found you actually don't need to create an empty Frame. Just embedded the Image3D in Show and apply the Ticks options in Show is a direct way. $\endgroup$
    – matheorem
    Feb 8, 2016 at 11:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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