tag:blogger.com,1999:blog-7918809034174026712024-03-20T02:33:26.390-07:00Mass Writing Chunk 48: Bringing it together 3Antonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-791880903417402671.post-48014752076202924302009-08-03T15:34:00.000-07:002009-08-03T15:43:35.379-07:00Chunk 48 - FINAL VERSION<span style="font-weight:bold;">Bringing it together 3</span><br />In this chunk we will continue on from the last by using the vertex function to create shapes. However, this time we will extend the animation to three-dimensional shapes. What we are attempting to draw is shown in figure 48.1 below<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s1600-h/figure+48.1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s320/figure+48.1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501773389394" /></a><br /><span style="font-style:italic;">Figure 48.1 The final animation</span><br /><br />This looks extraordinarily complex, so we will break it down into parts and explain how each part is done. As we have already drawn hexagons in the previous chunk, we will start with those, but we will alter the code so we can reuse it to create other shapes.<br /><br />Each vertex in a polygon can be calculated using a formula related to the number of sides. Most polygons (that is 2D shapes) can be drawn using the same formula, so we will be able to reuse it to draw polygons with different numbers of sides. Using trigonometric functions, which we will describe in more detail later in the book, we can calculate each vertex by<br /><pre><br /> x-coordinate = (coordinate + edge size * cos(current side index * 2 * PI / sides));<br /> y-coordinate = (coordinate + edge size * sin(current side index * 2 * PI / sides));<br /></pre><br />We can use this to create a generic function for creating polygons by looping over these statements by the number of sides we want and drawing a vertex at the calculated coordinates. <br /><pre> <br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> <br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /><br />}<br /></pre><br />The centre point of the hexagon can be calculated in a similar way and the lines drawn from each vertex to the centre. What we need to do is work out the coordinates of each vertex, add them together, then divide this value by the number of sides. This will give us the centre-point coordinates of the shape. For example<br /><pre><br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // add them all together<br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /> // calculate the centre coordinates<br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /></pre><br />The code above will calculate the centre-point of the shape, and that point used as the basis for drawing the vertices that make up the sides. This will mean that, when we rotate it, the shape will rotate around its centre.<br /><br />Although this initially seems like it is more complicated than the method seen in chunk 47, it is a much more generic way of creating the hexagon that means we can use it to create other polygons. This means we can easily draw a smaller pentagon simply by calling our function with different arguments. If we add a loop, similar to how we implemented the previous 'hexagon wave', we can create our background of rotating polygons. <br /><pre><br /> // loop over the height of the screen<br /> for (int i = hexagonSize; i < height + hexagonSize; i = i + (hexagonSize * 2)) {<br /> // loop over the width of the screen<br /> for (int j = hexagonSize; j < width; j = j + (hexagonSize * 2)) {<br /></pre><br />This time, however, we will create a background field of rotating hexagons and pentagons by drawing the pentagons on top of the hexagons, then rotating them in different directions. As we now have our function for creating polygons, all we need do is call it with the number of sides and size we want. The code now looks like the below<br /><pre><br />int hexagonSize = 40;<br />int pentagonSize = 30;<br /><br />void setup() {<br /><br /> size(800, 600, P3D);<br /><br />}<br /><br />void draw() {<br /> <br /> // define rotation direction variables<br /> float clockwiseDirection = frameCount * PI;<br /> float antiClockwiseDirection = frameCount * -PI;<br /> <br /> background(255);<br /> <br /> // loop over the height of the screen<br /> for (int i = hexagonSize; i < height + hexagonSize; i = i + (hexagonSize * 2)) {<br /> // loop over the width of the screen<br /> for (int j = hexagonSize; j < width; j = j + (hexagonSize * 2)) {<br /><br /> pushMatrix();<br /> translate(j, i, 0);<br /> // hexagon<br /> rotateZ(antiClockwiseDirection / 150);<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> draw2DPolygon(6, hexagonSize);<br /> // pentagon<br /> rotateZ(clockwiseDirection / 255);<br /> rotateX(clockwiseDirection / 255);<br /> rotateY(clockwiseDirection / 255);<br /> draw2DPolygon(5, pentagonSize);<br /><br /> popMatrix();<br /><br /> }<br /> }<br /> <br />}<br /><br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // fill colour<br /> fill(85, 26, 139, 30);<br /> // line colour<br /> stroke(138, 43, 226, 20);<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> // centre coordinates<br /> float centreX = 0;<br /> float centreY = 0;<br /> // work out the centre coordinates using the coordinates of each vertex<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // add them all together<br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /> // calculate the centre coordinates<br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /><br />}<br /></pre><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s1600-h/figure+48.2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s320/figure+48.2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501937233218" /></a><br /><span style="font-style:italic;">Figure 48.2 Background of rotating hexagons and pentagons</span><br /><br />We can also do the same with the internal lines that we drew. As we have already calculated the centre-point, we can just draw a line from each vertex to the centre. This means it will work for both the hexagon and the pentagon.<br /><pre><br /> // draw the internal lines<br /> // loop for the number of sides<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot a line from the coordinate to the centre coordinates<br /> line(xCoord, yCoord, centreX, centreY);<br /><br /> }<br /></pre><br />This function could be used, if we wished, to create any regular (that is, with equal side lengths) 2D polygon with 3 sides (triangle) or more. This makes the more complicated code we just created worth the effort because we don't need to keep creating functions every time we want a polygon with a different number of sides.<br /><br />We can now look at drawing the 3D shapes which, as you might expect, are somewhat more complicated than 2D shapes, just as 2D shapes were more complicated than the 1D shapes (lines) you saw in chunk 46. Processing already has functions for some 3D shapes such as cubes and spheres, so we can utilise those for certain shapes. However, if we want a complicated shape, we will need to draw it ourselves using vertices, as we did with the 2D shapes. This time, though, we need to specify three coordinates for each vertex to plot it in three-dimensions. Processing's vertex function can have 2 or 3 arguments, so we will be using the 3 arguments required for 3D in the form<br /><pre><br /> vertex(x, y, z);<br /></pre><br />where the x and y coordinates are the same as seen previously, and the z coordinate is the third dimension – positive values for z are directly 'out' of the screen, so negative values are 'into' the screen.<br />The shape we are going to draw is a cuboctahedron – a 24-sided polyhedron (a 3D shape made up of polygons) with faces made up of 8 triangles and 6 squares, as shown in figure 48.3<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s1600-h/figure+48.3.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s320/figure+48.3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121509887103058" /></a><br /><span style="font-style:italic;">Figure 48.3 Cuboctahedron with vertices marked</span><br /><br />In order to create some code that is readable, we will define each vertex in terms of the letters shown in figure 48.3. The simplest way to define a reusable shape is to create a class that defines how the shape is drawn. However, this is covered later in the book, so for now we will use an array and a switch statement – an array to define which points are drawn in which order, and a switch statement to draw the vertex in each element of the array. <br /><br />The reason we are defining an array is because we need to draw the shape by drawing from one vertex to another without stopping – a little like trying to draw without taking your pen off the paper. This means that we end up drawing some of the lines more than once in order to reach the next vertex. The order we have determined, using the labelled points above, is:<br /><pre><br /> A,I,E,K,A,B,J,F,I,B,D,L,H,J,D,C,K,G,L,C,A,I,E,G,H,F,E<br /></pre><br />We can create an array of these characters and use it in a switch statement to create the vertex for each associated point by using a for statement to loop over the elements in the array. We will therefore create a function that draws the vertices when given an array of points and an edge size for each line in the shape – this will allow us to create a cuboctahedron of any size we wish.<br /><br />Firstly we need to calculate the size of one of the diagonals from the size of a straight edge. We won't explain this fully except to say that this is a standard calculation of the diagonal using Pythagoras theorem<br /><pre><br />// calculate the diagonal length<br />float halfEdgeSize = edgeSize / 2;<br />float halfDiagonalSize = (sqrt((sq(edgeSize)) + (sq(edgeSize)))) / 2; <br /></pre><br />We also need to draw the shape in relation to its centre point, so that it rotates around the centre. The centre point of our cuboctahedron is at point (0,0,-edgeSize) – that is one line-length into the screen – the half-way point between vertices I and L. If we draw our vertices in relation to these coordinates, the shape will be centred around that point and so rotate properly. We will loop over the points in our array and, using a switch statement, draw the corresponding vertex. Our code, then, will look like this<br /><pre><br /> // define the centre point of the shape<br /> float x = 0;<br /> float y = 0;<br /> float z = -edgeSize;<br /><br /> // loop over the array of vertices<br /> for (int i = 0; i < vertices.length; i++) {<br /> char vertex = vertices[i];<br /> // draw the corresponding vertex<br /> switch (vertex) {<br /> case 'A':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'B':<br /> vertex(x + halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'C':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'D':<br /> vertex(x + halfEdgeSize, y - halfEdgeSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'E':<br /> vertex(x - halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'F':<br /> vertex(x + halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'G':<br /> vertex(x - halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'H':<br /> vertex(x + halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'I':<br /> vertex(x, y, z);<br /> break;<br /> case 'J':<br /> vertex(x + (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'K':<br /> vertex(x - (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'L':<br /> vertex(x, y, z + (halfEdgeSize * 4));<br /> break;<br /> }<br /> }<br /></pre><br />We won't explain how each vertex was calculated – however, it is relatively easy if you have a basic knowledge of trigonometry. You may have noticed that there is no default case in our switch statement. This is deliberate as we only want to draw vertices for the point we have defined – there is no sensible default case we could create, so it can be omitted. We will call this function drawCuboctahedron()<br /><pre><br /> void drawCuboctahedron(char[] vertices, float edgeSize) {<br /></pre><br />where vertices is the array we will create, and edgeSize is the size of one edge of the shape.<br />We now need to define the array that contains the order of the vertices we need to draw using the above code. We have already stated the order of the points, so this just needs to be converted into a character array and passed to the drawCuboctahedron() function we have just created<br /><pre><br />/**<br />* Draws a cuboctahedron of the given size<br />*<br />* @param size the edge size of the shape<br />*/<br />void cuboctahedron(int size) {<br /> // define the array with the point ordering<br /> char[] vertices = new char[] {<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'K',<br /> 'A',<br /> 'B',<br /> 'J',<br /> 'F',<br /> 'I',<br /> 'B',<br /> 'D',<br /> 'L',<br /> 'H',<br /> 'J',<br /> 'D',<br /> 'C',<br /> 'K',<br /> 'G',<br /> 'L',<br /> 'C',<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'G',<br /> 'H',<br /> 'F',<br /> 'E' };<br /> // draw the shape<br /> beginShape();<br /> drawCuboctahedron(vertices, size);<br /> endShape();<br /><br />}<br /></pre><br />If we now call this cuboctahedron() function from the draw method, we will create our 3D shape. We can then add rotation functions to rotate the shape as shown below<br /><pre><br /> pushMatrix();<br /> // move to the centre of the screen<br /> translate(width / 2, height / 2, 0);<br /> <br /> // rotate in all 3 axes<br /> rotateX(clockwiseDirection / 250);<br /> rotateY(clockwiseDirection / 250);<br /> rotateZ(clockwiseDirection / 250);<br /> cuboctahedron(250);<br /> <br /> popMatrix();<br /></pre><br />This will draw and rotate the cuboctahedron along all 3 axes as shown in figure 48.4. The parameters we have given to the rotate functions is defining how fast it should rotate and in which direction. In this case we have stipulated a clockwise direction. The /250 part is the speed, in this case we are dividing by 250, so the speed will be quite slow<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNgUtU2Tj5aiOfcfrtVHYtRPu-xcT3KG4O9OCQ0WGjISgahvDqX6cyfXPesV1gNW7iVkjKB0YxJv4hYsJbzj-CE_a_muiKwieFwE7nIT08QRS441imQw3tMk0WT4HNCQTxbrQYsAI67vE/s1600-h/figure+48.4.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNgUtU2Tj5aiOfcfrtVHYtRPu-xcT3KG4O9OCQ0WGjISgahvDqX6cyfXPesV1gNW7iVkjKB0YxJv4hYsJbzj-CE_a_muiKwieFwE7nIT08QRS441imQw3tMk0WT4HNCQTxbrQYsAI67vE/s320/figure+48.4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365871374409057170" /></a><br /><span style="font-style:italic;">Figure 48.4 Cuboctahedron</span><br /><br />The next shape in our animation is the pyramid shape – actually a 'triangular dipyramid', a 9-sided 6-faced polyhedron where the faces are all triangles. For this we are going to cheat and use one of Processing's built-in functions – oddly, the sphere() function. The sphere() function, as you would expect, creates a 3D sphere, with a parameter that defines the radius of the sphere. However, the resolution of the sphere can be altered using the sphereDetail() function. A spheres resolution is the number of faces that are used to display the sphere – the default is 30. Each face in the sphere is actually a triangle, so changing the resolution to 1 like this:<br /><pre><br /> sphereDetail(1);<br /> sphere(200);<br /></pre><br />will result in sphere with 6 sides – our triangular dipyramid. We can add some colour and some transparency so all the sides can be seen using the fill() function – colour and transparency is covered later in the book, so we won't explain here except to say that this is a semi-transparent purple colour<br /><pre><br /> fill(138, 43, 226, 100);<br /></pre><br /><br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7v3BNLPhpcDaX33mHwdXhpUE3iqAL4F-eMls-XbO6PK6eseudxbLQIjAlMMgenz9U6l_bEhskTsyxSSat0zYCckagM9A-mQx2IpsJZ4CP84xiPV90VWsAVGzJbLdHqnXXcjJly3c0YAg/s1600-h/figure+48.5.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7v3BNLPhpcDaX33mHwdXhpUE3iqAL4F-eMls-XbO6PK6eseudxbLQIjAlMMgenz9U6l_bEhskTsyxSSat0zYCckagM9A-mQx2IpsJZ4CP84xiPV90VWsAVGzJbLdHqnXXcjJly3c0YAg/s320/figure+48.5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365871378941166802" /></a><br /><span style="font-style:italic;">Figure 48.5 Triangular Dipyramid</span><br /><br />We can now add this into our animation and rotate it slightly differently so that they appear to rotate inside each other, for example<br /><pre><br />rotateX(antiClockwiseDirection / 250);<br />rotateY(antiClockwiseDirection / 250);<br />rotateZ(antiClockwiseDirection / 250);<br />sphereDetail(1);<br />sphere(200);<br /></pre><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-EcZEhE0IC4hfQwGeTCxHvkoKjolq3815GontmmjsiqGE-DabsWvHCEg3dGCOD1YQz_U89q34kJ82fp7Rtl6pC3mF84dO2PFLXKUpdhhVoE6E2pTmE0UU8RodHXtbgpHZHvqMO9DfiFo/s1600-h/figure+48.6.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-EcZEhE0IC4hfQwGeTCxHvkoKjolq3815GontmmjsiqGE-DabsWvHCEg3dGCOD1YQz_U89q34kJ82fp7Rtl6pC3mF84dO2PFLXKUpdhhVoE6E2pTmE0UU8RodHXtbgpHZHvqMO9DfiFo/s320/figure+48.6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365871381473271602" /></a><br /><span style="font-style:italic;">Figure 48.6 Triangular Dipyramid rotating inside a cuboctahedron</span><br /><br />From here on most of the hard work has already been done for us. Our next shape is a cube which, fortunately, Processing already provides a function for in the form<br /><pre><br /> box(size);<br /></pre><br />where the parameter is the size of the x, y and z dimensions, creating a cube (incidentally, you can provide different parameters for the 3 sides, which creates a non-square box). All we need do, therefore, is provide rotation function calls and create our box, easy!<br /><pre><br />rotateX(clockwiseDirection / 200);<br />rotateY(clockwiseDirection / 200);<br />rotateZ(clockwiseDirection / 200);<br />box(120);<br /></pre><br />This time we are rotating the box faster than the previous shapes which provides us with an interesting animation of having the shapes rotate in different directions and at different speeds<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPBdP9nNxoepZqelr4Hysf_lUWB6jTlYDN9zKvRYRiAakPQQrBfCJz9E2fhjEAv5aFkcRYlWYEwpc8Guvg9KxDyouS0gwuRhj-Mvi3uFyqFuKVqz-FjpzMdpRX3y8KbP8QThsRGRbprcY/s1600-h/figure+48.7.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPBdP9nNxoepZqelr4Hysf_lUWB6jTlYDN9zKvRYRiAakPQQrBfCJz9E2fhjEAv5aFkcRYlWYEwpc8Guvg9KxDyouS0gwuRhj-Mvi3uFyqFuKVqz-FjpzMdpRX3y8KbP8QThsRGRbprcY/s320/figure+48.7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365871383355355362" /></a><br /><span style="font-style:italic;">Figure 48.7 Cube inside a triangular dipyramid inside a cuboctahedron</span><br /><br />The last shape we are going to animate, just to show that our code is reusable, is another, smaller cuboctahedron. Again, all we need to do is provide rotation function calls and create our cuboctahedron<br /><pre><br />rotateX(antiClockwiseDirection / 150);<br />rotateY(antiClockwiseDirection / 150);<br />rotateZ(antiClockwiseDirection / 150);<br />cuboctahedron(50);<br /></pre><br />This time a really small one rotating even faster, which finishes off our animation. The final code for creating the animation shown at the start, in figure 48.1, is as below<br /><pre><br />// sizes of the 2D shapes<br />int hexagonSize = 40;<br />int pentagonSize = 30;<br /><br />void setup() {<br /><br /> size(800, 600, P3D);<br /><br />}<br /><br />void draw() {<br /><br /> // define rotation direction variables<br /> float clockwiseDirection = frameCount * PI;<br /> float antiClockwiseDirection = frameCount * -PI;<br /> // reset the bakcground<br /> background(255);<br /> // fill the shapes with a purpley-colour<br /> fill(138, 43, 226, 100);<br /><br /> pushMatrix();<br /> // move to the centre of the screen<br /> translate(width / 2, height / 2, 0);<br /> // cuboctahedron<br /> rotateX(clockwiseDirection / 250);<br /> rotateY(clockwiseDirection / 250);<br /> rotateZ(clockwiseDirection / 250);<br /> cuboctahedron(250);<br /> // triangular dipyramid<br /> rotateX(antiClockwiseDirection / 250);<br /> rotateY(antiClockwiseDirection / 250);<br /> rotateZ(antiClockwiseDirection / 250);<br /> sphereDetail(1);<br /> sphere(200);<br /> // cube<br /> rotateX(clockwiseDirection / 200);<br /> rotateY(clockwiseDirection / 200);<br /> rotateZ(clockwiseDirection / 200);<br /> box(120);<br /> // small cuboctahedron<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> rotateZ(antiClockwiseDirection / 150);<br /> cuboctahedron(50);<br /><br /> popMatrix();<br /><br /> // loop over the height of the screen<br /> for (int i = hexagonSize; i < height + hexagonSize; i = i + (hexagonSize * 2)) {<br /> // loop over the width of the screen<br /> for (int j = hexagonSize; j < width; j = j + (hexagonSize * 2)) {<br /><br /> pushMatrix();<br /> translate(j, i, 0);<br /> // hexagon<br /> rotateZ(antiClockwiseDirection / 150);<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> draw2DPolygon(6, hexagonSize);<br /> // pentagon<br /> rotateZ(clockwiseDirection / 255);<br /> rotateX(clockwiseDirection / 255);<br /> rotateY(clockwiseDirection / 255);<br /> draw2DPolygon(5, pentagonSize);<br /><br /> popMatrix();<br /><br /> }<br /> }<br /><br />}<br /><br />/**<br /> * Draw a 2D polygon with the given number of sides and size<br /> * @param sides the number of sides<br /> * @param size the size of one edge<br /> */<br />void draw2DPolygon(int sides, int size) {<br /> // fill colour<br /> fill(85, 26, 139, 30);<br /> // line colour<br /> stroke(138, 43, 226, 20);<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> // centre coordinates<br /> float centreX = 0;<br /> float centreY = 0;<br /> // work out the centre coordinates using the coordinates of each vertex<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // add them all together<br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /> // calculate the centre coordinates<br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /> // draw the internal lines<br /> // loop for the number of sides<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot a line from the coordinate to the centre coordinates<br /> line(xCoord, yCoord, centreX, centreY);<br /><br /> }<br /> // reset the stroke colour to black<br /> stroke(0);<br /><br />}<br /><br />/**<br />* Draw a cuboctahedron of the given size<br />* @param size the size of the shape<br />*/<br />void cuboctahedron(int size) {<br /> // define the vertices<br /> char[] vertices = new char[] {<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'K',<br /> 'A',<br /> 'B',<br /> 'J',<br /> 'F',<br /> 'I',<br /> 'B',<br /> 'D',<br /> 'L',<br /> 'H',<br /> 'J',<br /> 'D',<br /> 'C',<br /> 'K',<br /> 'G',<br /> 'L',<br /> 'C',<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'G',<br /> 'H',<br /> 'F',<br /> 'E' };<br /> // draw the shape<br /> beginShape();<br /> drawCuboctahedron(vertices, size);<br /> endShape();<br /><br />}<br /><br />/**<br />* Draw the vertices for a cuboctahedron of the given size<br />* @param size the size of the shape<br />* @param vertices an array of vertex labels<br />*/<br />void drawCuboctahedron(char[] vertices, float edgeSize) {<br /><br /> // calculate the diagonal length<br /> float halfEdgeSize = edgeSize / 2;<br /> float halfDiagonalSize = (sqrt((sq(edgeSize)) + (sq(edgeSize)))) / 2;<br /> // define the centre-point<br /> float x = 0;<br /> float y = 0;<br /> float z = -edgeSize;<br /> // loop over the array of vertices<br /> for (int i = 0; i < vertices.length; i++) {<br /> // extract the current vertex label<br /> char vertex = vertices[i];<br /> // draw the right one<br /> switch (vertex) {<br /> case 'A':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'B':<br /> vertex(x + halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'C':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'D':<br /> vertex(x + halfEdgeSize, y - halfEdgeSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'E':<br /> vertex(x - halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'F':<br /> vertex(x + halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'G':<br /> vertex(x - halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'H':<br /> vertex(x + halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'I':<br /> vertex(x, y, z);<br /> break;<br /> case 'J':<br /> vertex(x + (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'K':<br /> vertex(x - (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'L':<br /> vertex(x, y, z + (halfEdgeSize * 4));<br /> break;<br /> }<br /> }<br /><br />}<br /></pre><br /><br />Hopefully this section has shown you that is it not as difficult as it looks to create exciting looking animations with Processings line and vertex functions, especially if you take it in small steps to build up your animation. These three 'bringing it together' chunks we have just covered should allow you to create interesting graphics and animations using shapes drawn in 1, 2 or 3 dimensions.Antonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0tag:blogger.com,1999:blog-791880903417402671.post-88874056456583018442009-08-03T06:04:00.000-07:002009-08-03T06:08:18.226-07:00First 1000 words (draft)<span style="font-weight:bold;">Bringing it together 3</span><br />In this chunk we will continue on from the last by using the vertex function to create shapes. However, this time we will extend the animation to three-dimensional shapes. What we are attempting to draw is shown in figure 48.1 below<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s1600-h/figure+48.1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s320/figure+48.1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501773389394" /></a><br /><span style="font-style:italic;">Figure 48.1 The final animation</span><br /><br />This looks extraordinarily complex, so we will break it down into parts and explain how each part is done. As we have already drawn hexagons in the previous chunk, we will start with those, but we will alter the code so we can reuse it to create other shapes. <br /><br />Each vertex in a polygon can be calculated using a formula related to the number of sides. Most polygons (that is 2D shapes) can be drawn using the same formula, so we will be able to reuse it to draw polygons with different numbers of sides. Using trigonometric functions, which we will describe in more detail later in the book, we can calculate each vertex by<br /><pre><br /> x-coordinate = (coordinate + edge size * cos(current side index * 2 * PI / sides));<br /> y-coordinate = (coordinate + edge size * sin(current side index * 2 * PI / sides));<br /></pre><br />We can use this to create a generic function for creating polygons <br /><pre><br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> <br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /><br />}<br /></pre><br />The centre point of the hexagon can be calculated in a similar way and the lines drawn from each vertex to the centre. Although this initially seems like it is more complicated than the method seen in chunk 47, it is a much more generic way of creating the hexagon that means we can use it to create other polygons. This means we can easily draw a smaller pentagon simply by calling our function with different arguments. If we add a loop, similar to how we implemented the previous 'hexagon wave', we can create our background of rotating polygons. The code now looks like the below<br /><pre><br />int hexagonSize = 40;<br />int pentagonSize = 30;<br /><br />void setup() {<br /><br /> size(800, 600, P3D);<br /><br />}<br /><br />void draw() {<br /> <br /> // define rotation direction variables<br /> float clockwiseDirection = frameCount * PI;<br /> float antiClockwiseDirection = frameCount * -PI;<br /> <br /> background(255);<br /> <br /> // loop over the hieght of the screen<br /> for (int i = hexagonSize; i < height + hexagonSize; i = i + (hexagonSize * 2)) {<br /> // loop over the width of the screen<br /> for (int j = hexagonSize; j < width; j = j + (hexagonSize * 2)) {<br /><br /> pushMatrix();<br /> translate(j, i, 0);<br /> // hexagon<br /> rotateZ(antiClockwiseDirection / 150);<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> draw2DPolygon(6, hexagonSize);<br /> // pentagon<br /> rotateZ(clockwiseDirection / 255);<br /> rotateX(clockwiseDirection / 255);<br /> rotateY(clockwiseDirection / 255);<br /> draw2DPolygon(5, pentagonSize);<br /><br /> popMatrix();<br /><br /> }<br /> }<br /> <br />}<br /><br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // fill colour<br /> fill(85, 26, 139, 30);<br /> // line colour<br /> stroke(138, 43, 226, 20);<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> // centre coordinates<br /> float centreX = 0;<br /> float centreY = 0;<br /> // work out the centre coordinates using the coordinates of each vertex<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // add them all together<br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /> // calculate the centre coordinates<br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /> // draw the internal lines<br /> // loop for the number of sides<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot a line from the coordinate to the centre coordinates<br /> line(xCoord, yCoord, centreX, centreY);<br /><br /> }<br /> // reset the stroke colour to black<br /> stroke(0);<br /><br />}<br /></pre><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s1600-h/figure+48.2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s320/figure+48.2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501937233218" /></a><br /><span style="font-style:italic;">Figure 48.2 Background of rotating hexagons and pentagons</span><br /><br />Now we can look at drawing the 3D shapes. Processing already has functions for some 3D shapes such as cubes and spheres, so we can utilise those for certain shapes. However, if we want a complicated shape, we will need to draw it ourselves using vertices, as we did with the 2D shapes. This time, though, we also need to specify three coordinates for each vertex to plot it in three-dimensions. Processing's vertex function can have 2 or 3 arguments, so we will be using the 3 arguments required for 3D in the form<br /><pre><br /> vertex(x, y, z);<br /></pre><br />where the x and y coordinates are the same as seen previously, and the z coordinate is the third dimension – positive values for z are directly 'out' of the screen, so negative values are 'into' the screen.<br />The shape we are going to draw is a cuboctahedron – a 24-sided polyhedron (a 3D shape made up of polygons) with faces made up of 8 triangles and 6 squares, as shown in figure 48.3<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s1600-h/figure+48.3.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s320/figure+48.3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121509887103058" /></a><br /><span style="font-style:italic;">Figure 48.3 Cuboctahedron with vertices marked</span><br /><br />In order to create some code that is readable, we will define each vertex in terms of the letters shown in figure 48.3. The simplest way to define a reusable shape is to create a class that defines how the shape is drawn. However, this is covered later in the book, so for now we will use an array and a switch statement – an array to define which points are drawn in which order, and a switch statement to draw the vertex in each element of the array. <br /><br />The reason we are defining an array is because we need to draw the shape by drawing from one vertex to another – a little like trying to draw without taking your pen off the paper. This means that we end up drawing some of the lines more than once in order to reach the next vertex. The order we have determined, using the labelled points above, is:<br /><pre><br /> A,I,E,K,A,B,J,F,I,B,D,L,H,J,D,C,K,G,L,C,A,I,E,G,H,F,E<br /></pre><br />We can create an array of these characters and use it in a switch statement to create the vertex for each associated point by using a for statement to loop over the elements in the array. We will therefore create a function that draws the vertices when given an array of points and an edge size for each line in the shape – this will allow us to create a cuboctahedron of any size we wish.<br /><br />Firstly we need to calculate the size of one of the diagonals from the size of a straight edge. We won't explain this fully except to say that this is a standard calculation of the diagonal using Pythagoras theorem<br /><pre><br />// calculate the diagonal length<br />float halfEdgeSize = edgeSize / 2;<br />float halfDiagonalSize = (sqrt((sq(edgeSize)) + (sq(edgeSize)))) / 2; <br /></pre><br />We also need to draw the shape in relation to its centre point, so that it rotates around the centre. The centre point of our cuboctahedron is at point (0,0,-edgeSize) – that is one line-length into the screen – the half-way point between vertices I and L. If we draw our vertices in relation to these coordinates, the shape will be centred around that point and so rotate properly. Our code, then, will look like this<br /><pre><br /> // define the centre point of the shape<br /> float x = 0;<br /> float y = 0;<br /> float z = -edgeSize;<br /><br /> // loop over the array of vertices<br /> for (int i = 0; i < vertices.length; i++) {<br /> char vertex = vertices[i];<br /> // draw the corresponding vertex<br /> switch (vertex) {<br /> case 'A':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'B':<br /> vertex(x + halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'C':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'D':<br /> vertex(x + halfEdgeSize, y - halfEdgeSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'E':<br /> vertex(x - halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'F':<br /> vertex(x + halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'G':<br /> vertex(x - halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'H':<br /> vertex(x + halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'I':<br /> vertex(x, y, z);<br /> break;<br /> case 'J':<br /> vertex(x + (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'K':<br /> vertex(x - (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'L':<br /> vertex(x, y, z + (halfEdgeSize * 4));<br /> break;<br /> }<br /> }<br /></pre><br />We won't explain how each vertex was calculated – however, it is relatively easy if you have a basic knowledge of trigonometry. You may have noticed that there is no default case in our switch statement. This is deliberate as we only want to draw vertices for the point we have defined – there is no sensible default case we could create, so it can be omitted. We will call this function <br /><pre><br /> void drawCuboctahedron(char[] vertices, float edgeSize) {<br /></pre><br />We now need to define the array that contains the order of the vertices we need to draw using the above code. We have already stated the order of the points, so this just needs to be converted into a character array and passed to the drawCuboctahedron() function we have just created<br /><pre><br />/**<br />* Draws a cuboctahedron of the given size<br />*<br />* @param size the edge size of the shape<br />*/<br />void cuboctahedron(int size) {<br /> // define the array with the point ordering<br /> char[] vertices = new char[] {<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'K',<br /> 'A',<br /> 'B',<br /> 'J',<br /> 'F',<br /> 'I',<br /> 'B',<br /> 'D',<br /> 'L',<br /> 'H',<br /> 'J',<br /> 'D',<br /> 'C',<br /> 'K',<br /> 'G',<br /> 'L',<br /> 'C',<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'G',<br /> 'H',<br /> 'F',<br /> 'E' };<br /> // draw the shape<br /> beginShape();<br /> drawCuboctahedron(vertices, size);<br /> endShape();<br /><br />}<br /></pre><br />If we now call this cuboctahedron() function from the draw method, we will create our 3D shape. We can then add rotation functions to rotate the shape as shown below<br /><pre><br /> pushMatrix();<br /> // move to the centre of the screen<br /> translate(width / 2, height / 2, 0);<br /> <br /> // rotate in all 3 axes<br /> rotateX(clockwiseDirection / 250);<br /> rotateY(clockwiseDirection / 250);<br /> rotateZ(clockwiseDirection / 250);<br /> cuboctahedron(250);<br /> <br /> popMatrix();<br /></pre><br />This will draw and rotate the cuboctahedron along all 3 axes as shown in figure 48.4. The parameters we have given to the rotate functions is defining how fast it should rotate and in which direction. In this case we have stipulated a clockwise direction. The /250 part is the speed, in this case we are dividing by 250, so the speed will be quite slow<br /><br /><br /><span style="font-style:italic;">Figure 48.4 Cuboctahedron</span><br /><br />The next shape in our animation is the pyramid shape – actually a 'triangular dipyramid', a 9-sided 6-faced polyhedron where the faces are all triangles. For this we are going to cheat and use one of Processing's built-in functions – oddly, the sphere() function. The sphere() function, as you would expect, creates a 3D sphere, with a parameter that defines the radius of the sphere. However, the resolution of the sphere can be altered using the sphereDetail() function. A spheres resolution is the number of faces that are used to display the sphere – the default is 30. Each face in the sphere is actually a triangle, so changing the resolution to 1 like this:<br /><pre><br /> sphereDetail(1);<br /> sphere(200);<br /></pre><br />will result in sphere with 6 sides – our triangular dipyramid. We can add some colour and some transparency so all the sides can be seen using the fill() function – colour and transparency is covered later in the book, so we won't explain here except to say that this is a semi-transparent purple colour<br /><pre><br /> fill(138, 43, 226, 100);<br /></pre><br /><br /><span style="font-style:italic;">Figure 48.5 Triangular Dipyramid</span><br /><br />We can now add this into our animation and rotate it slightly differently so that they appear to rotate inside each other, for example<br /><pre><br /> rotateX(antiClockwiseDirection / 250);<br /> rotateY(antiClockwiseDirection / 250);<br /> rotateZ(antiClockwiseDirection / 250);<br /> sphereDetail(1);<br /> sphere(200);<br /></pre><br /><br /><span style="font-style:italic;">Figure 48.6 Triangular Dipyramid rotating inside a cuboctahedron</span>Antonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0tag:blogger.com,1999:blog-791880903417402671.post-29441574311259068342009-08-01T15:08:00.000-07:002009-08-01T15:12:40.286-07:00First 500 words (draft)<span style="font-weight:bold;">Bringing it together 3</span><br />In this chunk we will continue on from the last by using the vertex function to create shapes. However, this time we will extend the animation to three-dimensional shapes. What we are attempting to draw is shown in figure 48.1 below<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s1600-h/figure+48.1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPoc8CsD3lYax9RKctzP0RT_QqdXtQvqTyk23EgqYdsB51AYE7oRgaf5ntcgeZDOVGhqtOyhRXWaC0vTFyRoKaVGIAEM56XNjRcX1LJo4tv_HSMbnuyKCCtGnPhbM3SqzaHpEkFPw_JJs/s320/figure+48.1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501773389394" /></a><br /><span style="font-style:italic;">Figure 48.1 The final animation</span><br /><br />This looks extraordinarily complex, so we will break it down into parts and explain how each part is done. As we have already drawn hexagons in the previous chunk, we will start with those, but we will alter the code so we can reuse it to create other shapes. <br /><br />Each vertex in a polygon can be calculated using a formula related to the number of sides. Most polygons (that is 2D shapes) can be drawn using the same formula, so we will be able to reuse it to draw polygons with different numbers of sides. Using trigonometric functions, which we will describe in more detail later in the book, we can calculate each vertex by<br /><pre><br /> x-coordinate = (coordinate + edge size * cos(current side index * 2 * PI / sides));<br /> y-coordinate = (coordinate + edge size * sin(current side index * 2 * PI / sides));<br /></pre><br />We can use this to create a generic function for creating polygons <br /><pre><br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> <br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /><br />}<br /></pre><br />The centre point of the hexagon can be calculated in a similar way and the lines drawn from each vertex to the centre. Although this initially seems like it is more complicated than the method seen in chunk 47, it is a much more generic way of creating the hexagon that means we can use it to create other polygons. This means we can easily draw a smaller pentagon simply by calling our function with different arguments. If we add a loop, similar to how we implemented the previous 'hexagon wave', we can create our background of rotating polygons. The code now looks like the below<br /><pre><br />int hexagonSize = 40;<br />int pentagonSize = 30;<br /><br />void setup() {<br /><br /> size(800, 600, P3D);<br /><br />}<br /><br />void draw() {<br /> <br /> // define rotation direction variables<br /> float clockwiseDirection = frameCount * PI;<br /> float antiClockwiseDirection = frameCount * -PI;<br /> <br /> background(255);<br /> <br /> // loop over the hieght of the screen<br /> for (int i = hexagonSize; i < height + hexagonSize; i = i + (hexagonSize * 2)) {<br /> // loop over the width of the screen<br /> for (int j = hexagonSize; j < width; j = j + (hexagonSize * 2)) {<br /><br /> pushMatrix();<br /> translate(j, i, 0);<br /> // hexagon<br /> rotateZ(antiClockwiseDirection / 150);<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> draw2DPolygon(6, hexagonSize);<br /> // pentagon<br /> rotateZ(clockwiseDirection / 255);<br /> rotateX(clockwiseDirection / 255);<br /> rotateY(clockwiseDirection / 255);<br /> draw2DPolygon(5, pentagonSize);<br /><br /> popMatrix();<br /><br /> }<br /> }<br /> <br />}<br /><br />/**<br />* Draw a 2D polygon with the given number of sides and size<br />* @param sides the number of sides<br />* @param size the size of one edge<br />*/<br />void draw2DPolygon(int sides, int size) {<br /> // fill colour<br /> fill(85, 26, 139, 30);<br /> // line colour<br /> stroke(138, 43, 226, 20);<br /> // starting coordinates<br /> int x = 0;<br /> int y = 0;<br /> // centre coordinates<br /> float centreX = 0;<br /> float centreY = 0;<br /> // work out the centre coordinates using the coordinates of each vertex<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /> // add them all together<br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /> // calculate the centre coordinates<br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /> // draw the polygon<br /> beginShape();<br /> // loop for the number of sides + 1 (so we join up the last side to the starting vertex)<br /> for (int i = 0; i <= sides; i++) {<br /> // work out the coordinates of the vertex<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot the vertex<br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /> // draw the internal lines<br /> // loop for the number of sides<br /> for (int i = 0; i < sides; i++) {<br /> // work out the coordinates<br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /> // plot a line from the coordinate to the centre coordinates<br /> line(xCoord, yCoord, centreX, centreY);<br /><br /> }<br /> // reset the stroke colour to black<br /> stroke(0);<br /><br />}<br /></pre><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s1600-h/figure+48.2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7E1MBQuXquVBW6IubIZ_sWT9hqBFMzpnONBdf2PSFH24uu8VL-DkKFeMoZVCmWc3vUQuAvSIzXxyvoALb9yHyS2lw4h3yJ540g0Y_SWahpzLY6hxI8awuyaEKc_wQKHUxiNYNSXLnA-8/s320/figure+48.2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121501937233218" /></a><br /><span style="font-style:italic;">Figure 48.2 Background of rotating hexagons and pentagons</span><br /><br />Now we can look at drawing the 3D shapes. Processing already has functions for some 3D shapes such as cubes and spheres, so we can utilise those for certain shapes. However, if we want a complicated shape, we will need to draw it ourselves using vertices, as we did with the 2D shapes. This time, though, we also need to specify three coordinates for each vertex to plot it in three-dimensions. Processing's vertex function can have 2 or 3 arguments, so we will be using the 3 arguments required for 3D in the form<br /><pre><br /> vertex(x, y, z);<br /></pre><br />The shape we are going to draw is a cuboctahedron – a 24-sided polyhedron (a 3D shape made up of polygons) with faces made up of 8 triangles and 6 squares, as shown in figure 48.3<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s1600-h/figure+48.3.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnJt6mAuRR1nuHxIBERj6uogkPdV-DribfeeGMBMLwHV-RuLX9Yf1NabBvzzlagA0YRwdLb87qHxr78IiFaMNHXuE0F4Pxsz9r5DyCW1R18k1mfP-jh-yLIiQerMi7rqvsJb2jCVagF88/s320/figure+48.3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5365121509887103058" /></a><br /><span style="font-style:italic;">Figure 48.3 Cuboctahedron with vertices marked</span><br /><br />In order to create some code that is readable, we will define each vertex in terms of the letters shown in figure 48.3. The simplest way to define a reusable shape is to create a class that defines how the shape is drawn. However, this is covered later in the book, so for now we will use an array and a switch statement – an array to define which points are drawn in which order, and a switch statement to draw the vertex in each element of the array. <br /><br />The reason we are defining an array is because we need to draw the shape by drawing from one vertex to another – a little like trying to draw without taking your pen off the paper. This means that we end up drawing some of the lines more than once in order to reach the next vertex. The order we have determined, using the labelled points above, is:<br /><pre><br /> A,I,E,K,A,B,J,F,I,B,D,L,H,J,D,C,K,G,L,C,A,I,E,G,H,F,E<br /></pre>Antonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0tag:blogger.com,1999:blog-791880903417402671.post-18319553997066624882009-07-27T08:54:00.001-07:002009-07-27T09:05:31.997-07:00Program writtenI've finally written the program for this chunk, it justs needs commenting and then the chunk text explanation writing:<br /><br /><pre><br /><br />void setup() {<br /><br /> size(800, 600, P3D);<br /><br />}<br /><br />void draw() {<br /><br /> float clockwiseDirection = frameCount * PI;<br /> float antiClockwiseDirection = frameCount * -PI;<br /><br /> background(255);<br /> fill(138, 43, 226, 100);<br /><br /> pushMatrix();<br /> // move to the centre of the screen<br /> translate(width / 2, height / 2, 0);<br /><br /> rotateX(clockwiseDirection / 250);<br /> rotateY(clockwiseDirection / 250);<br /> rotateZ(clockwiseDirection / 250);<br /> cuboctahedron(250);<br /><br /> rotateX(antiClockwiseDirection / 250);<br /> rotateY(antiClockwiseDirection / 250);<br /> rotateZ(antiClockwiseDirection / 250);<br /> sphereDetail(1);<br /> sphere(200);<br /><br /> rotateX(clockwiseDirection / 200);<br /> rotateY(clockwiseDirection / 200);<br /> rotateZ(clockwiseDirection / 200);<br /> box(120);<br /><br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> rotateZ(antiClockwiseDirection / 150);<br /> cuboctahedron(50);<br /><br /> popMatrix();<br /><br /> for (int i = 40; i < height + 40; i = i + 80) {<br /> for (int j = 40; j < width; j = j + 80) {<br /><br /> pushMatrix();<br /> translate(j, i, 0);<br /><br /> rotateZ(antiClockwiseDirection / 150);<br /> rotateX(antiClockwiseDirection / 150);<br /> rotateY(antiClockwiseDirection / 150);<br /> draw2DPolygon(6, 40);<br /><br /> rotateZ(clockwiseDirection / 255);<br /> rotateX(clockwiseDirection / 255);<br /> rotateY(clockwiseDirection / 255);<br /> draw2DPolygon(5, 30);<br /><br /> popMatrix();<br /><br /> }<br /> }<br /><br />}<br /><br />void draw2DPolygon(int sides, int size) {<br /><br /> fill(85, 26, 139, 30);<br /> stroke(138, 43, 226, 20);<br /><br /> int x = 0;<br /> int y = 0;<br /><br /> float centreX = 0;<br /> float centreY = 0;<br /><br /> for (int i = 0; i < sides; i++) {<br /><br /> float xCoord = (x + size * cos(i * 2 * PI / sides));<br /> float yCoord = (y + size * sin(i * 2 * PI / sides));<br /><br /> centreX += xCoord;<br /> centreY += yCoord;<br /><br /> }<br /><br /> centreX = (centreX / sides) * -1;<br /> centreY = (centreY / sides) * -1;<br /><br /> beginShape();<br /><br /> for (int i = 0; i <= sides; i++) {<br /><br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /><br /> vertex(xCoord, yCoord);<br /><br /> }<br /><br /> endShape();<br /><br /> for (int i = 0; i < sides; i++) {<br /><br /> float xCoord = (centreX + size * cos(i * 2 * PI / sides));<br /> float yCoord = (centreY + size * sin(i * 2 * PI / sides));<br /><br /> line(xCoord, yCoord, centreX, centreY);<br /><br /> }<br /><br /> stroke(0);<br /><br />}<br /><br />void cuboctahedron(int size) {<br /><br /> char[] vertices = new char[] {<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'K',<br /> 'A',<br /> 'B',<br /> 'J',<br /> 'F',<br /> 'I',<br /> 'B',<br /> 'D',<br /> 'L',<br /> 'H',<br /> 'J',<br /> 'D',<br /> 'C',<br /> 'K',<br /> 'G',<br /> 'L',<br /> 'C',<br /> 'A',<br /> 'I',<br /> 'E',<br /> 'G',<br /> 'H',<br /> 'F',<br /> 'E' };<br /><br /> beginShape();<br /> drawCuboctahedron(vertices, size);<br /> endShape();<br /><br />}<br /><br />void drawCuboctahedron(char[] vertices, float edgeSize) {<br /><br /> float halfEdgeSize = edgeSize / 2;<br /> float halfDiagonalSize = (sqrt((sq(edgeSize)) + (sq(edgeSize)))) / 2;<br /><br /> float x = 0;<br /> float y = 0;<br /> float z = -edgeSize;<br /><br /> for (int i = 0; i < vertices.length; i++) {<br /> char vertex = vertices[i];<br /> switch (vertex) {<br /> case 'A':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'B':<br /> vertex(x + halfEdgeSize, y - halfDiagonalSize, z + halfEdgeSize);<br /> break;<br /> case 'C':<br /> vertex(x - halfEdgeSize, y - halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'D':<br /> vertex(x + halfEdgeSize, y - halfEdgeSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'E':<br /> vertex(x - halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'F':<br /> vertex(x + halfEdgeSize, y + halfEdgeSize, z + halfEdgeSize);<br /> break;<br /> case 'G':<br /> vertex(x - halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'H':<br /> vertex(x + halfEdgeSize, y + halfDiagonalSize, z + (halfEdgeSize * 3));<br /> break;<br /> case 'I':<br /> vertex(x, y, z);<br /> break;<br /> case 'J':<br /> vertex(x + (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'K':<br /> vertex(x - (halfEdgeSize * 2), y, z + (halfEdgeSize * 2));<br /> break;<br /> case 'L':<br /> vertex(x, y, z + (halfEdgeSize * 4));<br /> break;<br /> }<br /> }<br /><br />}<br /><br /><br /><br /></pre>Antonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0tag:blogger.com,1999:blog-791880903417402671.post-91487107601776400192009-06-12T05:31:00.000-07:002009-06-12T05:32:04.482-07:00IdeaI've been thinking about how I can implement this chunk whilst still having it carry on from chunk 46 and 47, in order to allow the reader to read through without it looking like it is jumping topics<br /><br />So my idea is to continue with the theme from chunk 46, but introduce new features at each stage. I'm assuming that by 'lines' we mean straight lines (ie not curves), so I will attempt to do the following:<br /><br />Chunk 47 - follow the chunk 46 theme but add animation to the patterns<br />Chunk 48 - follow the chunk 47 theme but add 3D aspectsAntonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0tag:blogger.com,1999:blog-791880903417402671.post-76987063836442822952009-05-27T05:47:00.000-07:002009-06-02T18:03:06.972-07:00Chunk 48<span style="font-weight: bold;">CHUNK 48</span> <br />TITLE Bringing it together 3<br />DESCRIPTION This is a really difficult section. What we want you to do is to develop a program of about 200 lines which is visually stunning and which uses any of Processing's line functions.<br />OUTCOME<br /><ul><li>Understand a large Processing program</li></ul>REFERENCE Any facility presented in Greenberg up to 237<br />HINT Don't attempt this unless you really feel that you are a practised Java programmer.<br />PROGRAM No program neededAntonyhttp://www.blogger.com/profile/02876015928926947789noreply@blogger.com0