8
$\begingroup$

Given a mesh, I want to assign each triangle a color.

I tried to do it in this way:

MeshVertices[mesh_] := First@Cases[mesh, GraphicsComplex[x_, __] :> x, Infinity]

MeshFaces[mesh_] := Block[{faces},
    faces = Cases[mesh, Polygon[x_, ___] :> x, Infinity];
    If [faces == {}, 
        faces = Cases[mesh, Triangle[x_, ___] :> x, Infinity];
    ];
    If [faces == {}, {}, First@faces]
];

bunny = ExampleData[{"Geometry3D", "StanfordBunny"}];

faces = MeshFaces[bunny];
nFaces = Length[faces];
faceColors = RGBColor[#, 0.0, 0.0] & /@ (Range[nFaces] / (0.0 + nFaces));

vertices = MeshVertices[bunny];
coloredBunny = Graphics3D[
    GraphicsComplex[
        vertices,
        Thread[{faceColors, Triangle /@ faces}]
    ],
    Boxed -> False
]

The rendering was quite slow, and the result was not what I wanted:

Colored Bunny

Are there any ways to color the bunny? Thank you.

$\endgroup$
  • $\begingroup$ One quick fix is to insert and EdgeForm[None] into your graphics expression to get rid of the many black edges. You can get polygon data also directly: bunny = ExampleData[{"Geometry3D", "StanfordBunny"}, "PolygonObjects"]; {EdgeForm[None], Red, bunny} // Graphics3D $\endgroup$ – Yves Klett May 19 '16 at 8:02
  • $\begingroup$ @YvesKlett This also works! Thank you. $\endgroup$ – Purboo May 19 '16 at 8:38
  • $\begingroup$ BTW the slow rendering is due to the less efficient rendering of many individual polygons as compared to the much more efficient GraphicsComplex. $\endgroup$ – Yves Klett May 19 '16 at 8:41
8
$\begingroup$

There are 69,451 different polygons in the bunny, so that is why it takes so long to plot, so let's use a simpler example with only ~1,400 polygons:

MeshVertices[mesh_] := 
 First@Cases[mesh, GraphicsComplex[x_, __] :> x, Infinity]
MeshFaces[mesh_] := 
  Block[{faces}, 
   faces = Cases[mesh, Polygon[x_, ___] :> x, Infinity];
   If[faces == {}, 
    faces = Cases[mesh, Triangle[x_, ___] :> x, Infinity];];
   If[faces == {}, {}, First@faces]];
donut = ParametricPlot3D[{(3 + Cos[v]) Cos[u], (3 + Cos[v]) Sin[u], 
    Sin[v]}, {u, 0, 2 Pi}, {v, 0, 2 Pi}, Mesh -> None];
faces = MeshFaces[donut];
nFaces = Length[faces];
faceColors = RGBColor[#, 0.0, 0.0] & /@ Rest[Subdivide[1.0, nFaces]];
vertices = MeshVertices[donut];

When you run your code, it places a black border around every triangle. Compare these

Graphics3D /@ {Triangle[{{0, 0, 0}, {1, 0, 0}, {0, 1, 
     1}}], {EdgeForm[], Triangle[{{0, 0, 0}, {1, 0, 0}, {0, 1, 1}}]}}

two triangles

So you need to turn off the EdgeForm[] in your final line,

coloredDonut = 
 Graphics3D[
  GraphicsComplex[vertices, 
   Thread[{faceColors, {EdgeForm[], Polygon[#]} & /@ faces}]], 
  Boxed -> False]

red donut

Here is what it looks like with the borders:

red donut with borders

When you apply this to your bunny, here is the result:

colored bunny

By the way, here is slightly shorter code that produces the exact same result,

bunny = ExampleData[{"Geometry3D", "StanfordBunny"}];
colors = RGBColor[#, 0.0, 0.0] & /@ 
   Subdivide[1.0, 
    Length[Cases[Normal@bunny, Polygon[__], Infinity]]];
i = 1;
Normal[bunny] /. {Polygon[a__] :> 
   Sequence[i++; colors[[i]], Polygon[a]]}

The above code with replacement rule should work on many Graphics3D objects, like the donut above. But in that case it is necessary to adjust the lighting by adding another replacement rule HoldPattern[Lighting -> {__}] :> Lighting -> {{"Ambient", Red}}

But since you are working on a ExampleData["Geometry3D"] object, you can get the polygons as a list directly and then just use Thread to combine them with colors. Thanks to Yves Klett for pointing this out

With[{pgons = 
   ExampleData[{"Geometry3D", "UtahVWBug"}, "PolygonObjects"]}, 
 Graphics3D[{EdgeForm[], 
   Thread[{RGBColor[#, 0.0, 0.0] & /@ 
      Subdivide[1.0, Length@pgons - 1], pgons}]}, Boxed -> False]
 ]

red Volkswagen

Or a one-liner to give a psychedelic bunny

Normal[ExampleData[{"Geometry3D", "StanfordBunny"}]] /. {Polygon[
    a__] :> Sequence[RandomColor[], Polygon[a]]}

psychedelic bunny

$\endgroup$
  • $\begingroup$ Awesome! Thank you! $\endgroup$ – Purboo May 19 '16 at 8:37
  • 1
    $\begingroup$ Jason, what about getting the polygon data directly: ExampleData[{"Geometry3D", "StanfordBunny"}, "PolygonObjects"]? $\endgroup$ – Yves Klett May 19 '16 at 8:39
  • $\begingroup$ @YvesKlett - that does make for a shorter code, I hadn't thought of it (and then when I saw your comment I didn't want to take it in case you wanted to use it) $\endgroup$ – Jason B. May 19 '16 at 8:45
  • 2
    $\begingroup$ Na, your answer is so nice already, please feel free to include if it makes sense to you :D Actually, your psycho bunny reminds me of my oneliner cow, although that one uses VertexColors. $\endgroup$ – Yves Klett May 19 '16 at 8:49
  • $\begingroup$ That cow is great! I wasn't really aware of the ExampleData["Geometry3D"] stuff before $\endgroup$ – Jason B. May 19 '16 at 9:14
6
$\begingroup$

Jason already said a lot of what I wanted to say, so I'll just offer this little snippet that avoids Normal[] chicanery:

gc = ExampleData[{"Geometry3D", "StanfordBunny"}, "GraphicsComplex"];
Graphics3D[Insert[gc, EdgeForm[], {2, 1}] /. Polygon[m_?MatrixQ] :> 
           Riffle[Table[RandomColor[], {Length[m]}], Polygon /@ m]]

one crazily colored bunny


Just for variety, here's another method for coloring the bunny. Recall that GraphicsComplex[] takes a VertexColors option to specify how the polygons within should be colored. (Line[] objects are not affected by this, which may or may not be wanted in an application.) Thus,

(* some color function that takes a point as an argument *)
woody[pt_?VectorQ] := Blend[{RGBColor[3/4, 22/41, 1/3], RGBColor[1/2, 2/9, 7/38]}, 
                            Haversine[π SawtoothWave[50 EuclideanDistance[pt,
              {0.08, 0.06, 0.08}]]]]

Graphics3D[Insert[gc, EdgeForm[], {2, 1}] /. 
           gc : GraphicsComplex[pts_, rest___] :> 
           Append[gc, VertexColors -> (woody /@ pts)], Lighting -> "Neutral"]

wavy bunny

$\endgroup$
  • 1
    $\begingroup$ ...and as much as I'm averse to self-promotion: noisy bunnies $\endgroup$ – J. M. will be back soon May 19 '16 at 9:32
  • $\begingroup$ What do you have against Normal? GraphicsComplexes are great for the system I'm sure, but they're a pain to work with sometimes $\endgroup$ – Jason B. May 19 '16 at 10:03
  • $\begingroup$ Jason, "user21" and me mentioned some of the reasons in this thread; that being said, a number of my previous Gravatars needed the services of Normal[] at some point, and I agree that GraphicsComplex[] can be difficult to manipulate for sufficiently intricate operations. $\endgroup$ – J. M. will be back soon May 19 '16 at 11:53
  • $\begingroup$ lol, user21 didn't used to have such a mysterious username I see.... And I can see the benefit now that it's pointed out $\endgroup$ – Jason B. May 19 '16 at 11:55

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.