20
$\begingroup$

The documentation for Texture states that "other filled objects" can be texturized:

Texture[obj] is a graphics directive that specifies that obj should be used as a texture on faces of polygons and other filled graphics objects.

And also:

Texture can be used in FaceForm to texture front and back faces differently.

Though I fail to apply a simple texture to any of the following objects. It seems like that "other filled objects" only include Polygons and FilledPolygons, and FaceForm does not work with those.

img = Rasterize@
   DensityPlot[Sin@x Sin@y, {x, -4, 4}, {y, -3, 3}, 
    ColorFunction -> "BlueGreenYellow", Frame -> None, 
    ImageSize -> 100, PlotRangePadding -> 0];
{
 Graphics[{Texture@img, Disk[]}],
 Graphics[{FaceForm@Texture@img, Disk[]}],
 Graphics[{Texture@img, Rectangle[]}],
 Graphics[{FaceForm@Texture@img, Rectangle[]}],

 (* Only this one works *)
 Graphics[{Texture@img, 
   Polygon[{{0, 0}, {1, 0}, {1, 1}, {0, 1}}, 
    VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}]}],
 Graphics[{FaceForm@Texture@img, 
   Polygon[{{0, 0}, {1, 0}, {1, 1}, {0, 1}}, 
    VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}]}]
 }

Mathematica graphics

Edit:

It turns out that "Applying Texture to a disk directly isn't possible" (according to Heike, thanks s.s.o. for the link). This unfortunately means that:

  1. the official documentation of Texture is wrong (or at least is misleading, as graphics objects usually include primitives);
  2. either Texture is not fully integrated with the system, as it is not applicable for such primitives as a Rectangle, which seems to be just a very specific Polygon; or Rectangle is something else and is defined some other way at the lowest level than a Polygon (maybe it is some OS-dependent object).

Frankly, it is quite hard to imagine what kept developers to include this functionality, but I must assume they had a good reason.

$\endgroup$
6
  • $\begingroup$ I imagine that you know how to do this with ParametricPlot and PlotStyle as in the documentation for ParametricPlot? $\endgroup$ Commented Jun 29, 2012 at 15:34
  • $\begingroup$ @Mark: But any Plot is not a graphics primitive, is it? (btw, are you referring to RegionPlot?) I would like to texturize primitives, if possible. I am also aware of these threads: this and this. $\endgroup$ Commented Jun 29, 2012 at 15:40
  • $\begingroup$ I don't think you can Texture primitives like Disk[], Sphere[], etc $\endgroup$
    – rm -rf
    Commented Jun 29, 2012 at 15:44
  • $\begingroup$ @Istvan Right. I guess I meant to say that I suspect that you know how to achieve the basic effect with ParametricPlot, not how to produce the exact primitives. Sometimes an alternative approach is of interest but, since you're a regular, I didn't think I'd bother typing up a response based on that. $\endgroup$ Commented Jun 29, 2012 at 15:45
  • $\begingroup$ @Mark: As always, I am interested in any bypass solution, please post yours if it hasn't been posted anywhere else! $\endgroup$ Commented Jun 29, 2012 at 15:54

7 Answers 7

11
$\begingroup$

I noticed an example in the document of Texture which used the alpha channel. So I think a disk-shape primitive may be simulated to a limited degree by mapping the image img, which has been set to 100% transparent outside of the circle, onto a rectangle-shape Polygon.

My code:

img = Rasterize[
                DensityPlot[Sin[x] Sin[y],
                            {x, -4, 4}, {y, -3, 3},
                            ColorFunction -> "BlueGreenYellow",
                            Frame -> None, ImageSize -> 100, PlotRangePadding -> 0
              ]];

imgdim = ImageDimensions[img]

alphamask = Array[
                  If[
                     Norm[{#1, #2} - imgdim/2] < imgdim[[1]]/2,
                     1,0]&,
                  imgdim];

alphaimg = MapThread[Append, {img // ImageData, alphamask}, 2];

Graphics[{
          Polygon[{{0, 0}, {1, 0}, {1, 1}, {0, 1}} + .3],
          Texture[alphaimg],
          Polygon[{{0, 0}, {1, 0}, {1, 1}, {0, 1}}, 
                  VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}
                 ],
          Gray, Disk[{0, 0}, .5]
         }]

which gives result like this:

result graph

$\endgroup$
3
  • 1
    $\begingroup$ Clever! I always forget that such problems can be solved by image processing tricks. $\endgroup$ Commented Jun 29, 2012 at 22:00
  • $\begingroup$ @IstvánZachar but it's less effective than your fallback method, and the boundary is ugly OTL $\endgroup$
    – Silvia
    Commented Jun 29, 2012 at 22:13
  • $\begingroup$ Definitely the best method (+1). $\endgroup$
    – Jens
    Commented Jun 29, 2012 at 23:10
8
$\begingroup$

Like RM, I've not been able to texture a Disk primitive. We can create a textured disk using ParametricPlot, however.

ParametricPlot[{r*Cos[t], r*Sin[t]}, {r, 0, 1}, {t, 0, 2 Pi},
  Mesh -> False, BoundaryStyle -> None, Axes -> False,
  PlotStyle -> {Opacity[1], 
   Texture[ExampleData[{"ColorTexture", "LightCherry"}]]}]

enter image description here

$\endgroup$
5
  • $\begingroup$ You will also want to look into the option TextureCoordinateFunction; for instance, compare Mark's plot with ParametricPlot[{r Cos[t], r Sin[t]}, {r, 0, 1}, {t, 0, 2 Pi}, Mesh -> False, BoundaryStyle -> None, Axes -> False, PlotStyle -> {Opacity[1], Texture[ExampleData[{"ColorTexture", "LightCherry"}]]}, TextureCoordinateFunction -> ({#4, #3} &)]. $\endgroup$ Commented Jun 29, 2012 at 16:17
  • $\begingroup$ Yes - In fact, I lifted that code right out of the documentation for TextureCoordinateFunction. $\endgroup$ Commented Jun 29, 2012 at 16:40
  • $\begingroup$ @J.M. In version 10.1.0 under Windows I am getting ParametricPlot::optx: "Unknown option TextureCoordinateFun‌​ction->({#4,#3}&) in ParametricPlot" Has this Option been removed since you wrote that comment? $\endgroup$
    – Mr.Wizard
    Commented Aug 16, 2016 at 18:21
  • $\begingroup$ @Mr. Wizard, how very odd; I just tested it in versions 8.0.4, 10.4.1, and 11, and it proceeds as expected in all three. $\endgroup$ Commented Aug 17, 2016 at 16:57
  • $\begingroup$ @J.M. It's too late in my day for me to check now, but I'll have to make sure I didn't cause the problem myself with some modification I forgot about. $\endgroup$
    – Mr.Wizard
    Commented Aug 18, 2016 at 7:18
5
$\begingroup$

My fallback method for the moment is the following: approximate a circle with a polygon, fill the latter with the texture and finally conceal the angular edge with an overlaid Circle. If the whole image is small, the number of nodes of the polygon can be further reduced. One annoying sideeffect is though that the Circle is not antialiased...

img = Rasterize@
   DensityPlot[Sin@x Sin@y, {x, -4, 4}, {y, -3, 3}, 
    ColorFunction -> "BlueGreenYellow", Frame -> None, 
    ImageSize -> 200, PlotRangePadding -> 0];
coord = Block[{n = 100}, 
   Table[{Cos[2 \[Pi] k/n], Sin[2 \[Pi] k/n]}, {k, 0, n - 1}]];
Graphics[{Texture@img, EdgeForm@None, 
  Polygon[coord, VertexTextureCoordinates -> (coord/2 + .5)], Black, 
  Thick, Circle[]}, ImageSize -> 200, Background -> [email protected]]

Mathematica graphics

$\endgroup$
4
  • $\begingroup$ You can replace Circle[] with Style[Circle[], Antialiasing -> True] to get the circle antialiased. (+1) $\endgroup$
    – kglr
    Commented Jun 29, 2012 at 21:54
  • $\begingroup$ @kguler: Strangely, this does not work on my end, no matter how hard I try. $\endgroup$ Commented Jun 29, 2012 at 21:59
  • $\begingroup$ Strange... perhaps os/version/hardware differences? Suggestion was from the docs on Antialiasing. $\endgroup$
    – kglr
    Commented Jun 29, 2012 at 22:01
  • $\begingroup$ @kguler: I have no idea, I probably had messed up something myself, but I gave up on this for now. $\endgroup$ Commented Jul 1, 2012 at 20:02
5
$\begingroup$

Mathematica 10 introduced Regions which make this kind of operation much easier.

img = Rasterize@
   DensityPlot[Sin@x Sin@y, {x, -4, 4}, {y, -3, 3}, 
    ColorFunction -> "BlueGreenYellow", Frame -> None, ImageSize -> 100, 
    PlotRangePadding -> 0];

RegionPlot[
  RegionUnion[Disk[{0, 0}, {2, 2}], Disk[{0, 0}, {3.5, 0.7}]]
  , PlotStyle -> Texture[img]
][[{1}]]

enter image description here

$\endgroup$
5
$\begingroup$

As of version 12.1, we can use (yet undocumented) SurfaceAppearance["TextureShading", Texture[img]] to texturize 2D primitives such as Disk, Rectangle as well as 3D primitives like Sphere, Tube etc.

lena = ExampleData[{"TestImage", "Lena"}];
mandrill = ExampleData[{"TestImage", "Mandrill"}];

Graphics[{SurfaceAppearance["TextureShading", Texture[ImageMultiply[lena, Green]]], 
  Disk[], 
  SurfaceAppearance["TextureShading", Texture[mandrill]], 
  Rectangle[{2, -3/2}, {4, 1/2}], 
  BoundaryDiscretizeRegion @ RegionUnion[Disk[{1, 1}], Rectangle[{3/2, 1}]]}] 

enter image description here

$\endgroup$
4
$\begingroup$

If you click the disk and check Drawing tools from the menu there is only color fill options no texture options available. Also see Heike's related answers: in math group or mathematica group mainly she states "Applying Texture to a disk directly isn't possible, but you could for example use RegionPlot with the TextureCoordinateFunction option, e.g"

img = Rasterize@
   DensityPlot[Sin@x Sin@y, {x, -4, 4}, {y, -3, 3}, 
    ColorFunction -> "BlueGreenYellow", Frame -> None, 
    ImageSize -> 100, PlotRangePadding -> 0];

RegionPlot[x^2 + y^2 < 1, {x, -1, 1}, {y, -1, 1}, 
 BoundaryStyle -> None, 
 Axes -> False, Frame -> False, 
 PlotStyle -> Directive[Opacity[1], Texture[img]], 
 TextureCoordinateFunction -> ({#1, #2} &)] 
$\endgroup$
4
$\begingroup$

Here's an extension of Silvia's method of setting an alpha channel in the texture. The alpha mask is obtained directly from the shape using Rasterize, allowing the code to work with ellipses, rectangles, etc.

img = Rasterize@
   DensityPlot[Sin@x Sin@y, {x, -4, 4}, {y, -3, 3}, 
    ColorFunction -> "BlueGreenYellow", Frame -> None, 
    ImageSize -> 300, PlotRangePadding -> 0];

texturedShape[img_, shape_] := Module[{g, p, ar, i},
  g = Graphics[shape, PlotRangePadding -> 0];
  p = Polygon[
    AbsoluteOptions[g, PlotRange][[1, 
       2]] /. {{l_, r_}, {b_, t_}} :> {{l, b}, {l, t}, {r, t}, {r, 
        b}}, VertexTextureCoordinates -> {{0, 0}, {0, 1}, {1, 1}, {1, 
       0}}];
  ar = AbsoluteOptions[g, AspectRatio][[1, 2]];
  i = SetAlphaChannel[img, 
    ColorNegate@Rasterize[g, ImageSize -> ImageDimensions@img]];
  i = ImageCrop[i, 
    Round[If[ar > 1, {1/ar, 1}, {1, ar}] ImageDimensions@img]];
  {Texture[ImageData@i], p}]

The Rasterize gets its ImageDimensions from the original texture image, so I've increased the size of that to 300 to get cleaner edges.

Examples:

Graphics[{texturedShape[img,Disk[{0,0},2]],Red,Disk[{0,1},1]}]

enter image description here

Graphics[{texturedShape[img,Disk[{0,0},{2,1}]],Red,Disk[{0,1},1]}]

enter image description here

Graphics[{Red,Disk[{0,0},0.5],texturedShape[img,Rectangle[]]}]

enter image description here

$\endgroup$

Your Answer

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

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