; DJSClogo ;+ ; :Description: ; Create an animated GIF of 3-D rendered DJSC logo as follows: ; - make the balloon-animal letters inside models to allow easy rotation ; - add them to an XObjView window ; - take snapshots with models rotated at different angles ; - write images into an animated GIF file ; The XObjView window remains open for your viewing pleasure :-) ; ; :Author: ; Dick Jackson Software Consulting ; www.d-jackson.com ; ; :History: ; First version: December, 2012 ; Improved documentation: February 28, 2013 ;- PRO DJSClogo ;; Describe four shapes in four models, for d, j, s, c ; ;; Schematic diagram: ; - each row/column is one unit ; - every shape has diameter 1.0, radius 0.5, ; spacing 1.0 between shapes ; - '.' indicates centre of rotation for one or more shapes ; ; +Y ; ^ ; d j | s ; d j | s s ; d j | s c s ; d j | s c c s ; d d j | s c c ; d dd j |s c / ; d . d j . c - . - -> +X (+Z is toward viewer) ; d d j s c \ ; j d j s c c ; s j j s c c ; s j s c ; s s ; s gifDims = [147, 90] ; Desired image size nFacets = Ceil(40 * (Max(gifDims)/200.)); Based on image size for smooth surface gifLoopTime = 4 ; Length of animation loop, in seconds gifFramesPerSecond = 20 ; Animation frame rate gifRepeatCount = 2 ; Times for GIF to repeat. 0 = indefinitely ; (note: some browsers repeat n+1 times!) curvePower = 3 ; Controls style of rotation of shapes: ; 1=linear (boring), higher values = snappier, ; slowing down for "front" view gifFile = 'djsc-animation.gif' ; Output filename (in current directory) polygonExtra = {style: 2, $ ; Structure of properties used for all shading: 1, $ ; IDLgrPolygons for the shapes color: [128B, 192B, 255B], $ antialias: 0} theta = Transpose(2*!Pi/nFacets * FIndGen(nFacets+1)) ; Useful many times below ;; 'd': torus, cylinder, sphere oDmodel = Obj_New('IDLgrModel') dTorusCircleXYZ = [2+0.5*Cos(theta), $ ; X Replicate(0.0, [1, nFacets+1]), $ ; Y 0.5*Sin(theta)] ; Z Mesh_Obj, 6, verts, conn, $ ; Torus of 'd', revolution of circle dTorusCircleXYZ, P1=nFacets oDtorus = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn,_Extra=polygonExtra) Mesh_Obj, 5, verts, conn, $ ; Cylinder of 'd', extrusion of same circle! dTorusCircleXYZ, P1=1, P2=[0.0, 6.0, 0.0], P3=[0,0,1], P4=0, P5=2*!Pi oDcyl = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at end of 'd' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([2, 6, 0], [3, N_Elements(verts)/3]) oDsph = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) oDmodel -> Add, [oDtorus, oDcyl, oDsph] oDTransModel = Obj_New('IDLgrModel') ; Model for translating within world model oDTransModel -> Add, oDmodel oDTransModel -> Translate, -6, 0, 0 ; Position within world model ;; 'j': partial torus, cylinder, two spheres oJmodel = Obj_New('IDLgrModel') jTorusCircleXYZ = dTorusCircleXYZ + Rebin([2, 0, 0], 3, nFacets+1) Mesh_Obj, 6, verts, conn, $ ; Partial torus of 'j', revolution of circle jTorusCircleXYZ, $ P1=Round(nFacets*3/8.0), P2=[0,0,0], P3=[0,0,1], P4=225*!DtoR, P5=2*!Pi oJtorus = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn,_Extra=polygonExtra) Mesh_Obj, 5, verts, conn, $ ; Cylinder of 'j', extrusion of same circle! jTorusCircleXYZ, P1=1, P2=[0.0, 6.0, 0.0] oJcyl = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at lower-left end of 'j' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([4*Cos(225*!DtoR), 4*Sin(225*!DtoR), 0], [3,N_Elements(verts)/3]) oJsph0 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at upper end of 'j' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([4, 6, 0], [3, N_Elements(verts)/3]) oJsph1 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) oJmodel -> Add, [oJtorus, oJcyl, oJsph0, oJsph1] oJTransModel = Obj_New('IDLgrModel') ; Model for translating within world model oJTransModel -> Add, oJmodel oJTransModel -> Translate, -6, 0, 0 ; Position within world model ;; 's': two partial tori, two spheres oSmodel = Obj_New('IDLgrModel') sTorusCircleXYZ = [0.5*Cos(theta), $ ; X Replicate(0.0, [1, nFacets+1]), $ ; Y 0.5*Sin(theta)] ; Z Mesh_Obj, 6, verts, conn, $ ; Lower-left torus of 's', revolution of circle sTorusCircleXYZ, P1=Round(nFacets*3/8.0), P2=[-6,0,0], P3=[0,0,1], $ P4=225*!DtoR, P5=2*!Pi oStorus0 = Obj_New('IDLgrPolygon', Data=verts,Polygons=conn,_Extra=polygonExtra) Mesh_Obj, 6, verts, conn, $ ; Upper-right torus of 's', revolution of circle sTorusCircleXYZ, P1=Round(nFacets*3/8.0), P2=[6,0,0], P3=[0,0,1], $ P4=225*!DtoR, P5=2*!Pi oStorus1 = Obj_New('IDLgrPolygon', Data=verts,Polygons=conn,_Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at lower-left end of 's' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([(-6)+6*Cos(225*!DtoR), 6*Sin(225*!DtoR), 0], $ [3,N_Elements(verts)/3]) oSsph0 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at lower-left end of 's' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([6+6*Cos(45*!DtoR), 6*Sin(45*!DtoR), 0], $ [3,N_Elements(verts)/3]) oSsph1 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) oSmodel -> Add, [oStorus0, oStorus1, oSsph0, oSsph1] oSTransModel = Obj_New('IDLgrModel') ; Model for translating within world model oSTransModel -> Add, oSmodel oSTransModel -> Translate, 0, 0, 0 ; Position within world model ;; 'c': partial torus, two spheres oCmodel = Obj_New('IDLgrModel') cTorusCircleXYZ = jTorusCircleXYZ Mesh_Obj, 6, verts, conn, $ ; Torus of 'c', revolution of circle cTorusCircleXYZ, P1=Round(nFacets*3/4.0), P2=[0,0,0], P3=[0,0,1], $ P4=45*!DtoR,P5=315*!DtoR oCtorus = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn,_Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at lower-right end of 's' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([4*Cos(315*!DtoR), 4*Sin(315*!DtoR), 0], $ [3,N_Elements(verts)/3]) oCsph0 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) Mesh_Obj, 4, verts, conn, $ ; Sphere at lower-left end of 's' Replicate(0.5, [nFacets, nFacets]) verts += Rebin([4*Cos(45*!DtoR), 4*Sin(45*!DtoR), 0], $ [3,N_Elements(verts)/3]) oCsph1 = Obj_New('IDLgrPolygon', Data=verts, Polygons=conn, _Extra=polygonExtra) oCmodel -> Add, [oCtorus, oCsph0, oCsph1] oCTransModel = Obj_New('IDLgrModel') ; Model for translating within world model oCTransModel -> Add, oCmodel oCTransModel -> Translate, 6, 0, 0 ; Position within world model oRotatingModels = [oDModel, oJModel, oSModel, oCModel] ;; For each model, specify: around which axis and in which direction ;; it should rotate (I tried a few variants here) ; d:x, y,z j:x,y,z s:x, y,z c:x,y,z ;rotVectors = [[0,-1,0], [0,1,0], [0,-1,0], [1,0,0]] ;rotVectors = [[0,0,1], [1,0,0], [-1,0,0], [0,1,0]] rotVectors = [[0,-1,0], [1,0,0], [0,-1,0], [0,1,0]] ;; World model to contain all letter models oWorldModel = Obj_New('IDLgrModel') oWorldModel -> Add, [oDTransModel, oJTransModel, oSTransModel, oCTransModel] ;; Create XObjView window to display the world model XObjView, oWorldModel, XSize=gifDims[0], YSize=gifDims[1], Scale=1.5, $ Background='BLACK', TLB=wTLB nFrames = Float(gifLoopTime)*gifFramesPerSecond nFrames = Long(nFrames / 2) * 2 ; Ensure nFrames is even gifDelayTime = Round(100.0/gifFramesPerSecond) ;; Set up a list of rotation angles to be used for each animation frame thetasA = (FIndGen(nFrames/2+1)/(nFrames/2))^curvePower thetasA = thetasA / Max(thetasA) * 0.5 ; Scale to 0..0.5 thetas = [thetasA, 1.0-Reverse(thetasA[1:nFrames/2-1])] * 360 ;; A little XObjView hacking to get the IDLexObjViewWid object, which can ;; be asked to Draw to a buffer to get the current snapshot oBuffer = Obj_New('IDLgrBuffer', Dimensions=gifDims) Widget_Control, wTLB, Get_UValue=pState (*pState).oObjViewWid -> Draw, oBuffer oImage = oBuffer -> Read() oImage -> GetProperty, Data=firstImageRGB ;; Establish colour table for first image (GIF allows 8-bit images only!) firstImage8bit = Color_Quan(firstImageRGB, 1, r0, g0, b0, Colors=256) Write_GIF, gifFile, firstImage8bit, r0, g0, b0, /Multiple, $ Delay_Time=gifDelayTime, Repeat_Count=gifRepeatCount FOR frameI=1, nFrames-1 DO BEGIN ;; Rotate each model by the difference between this angle and previous one FOR modelI=0, N_Elements(oRotatingModels)-1 DO $ oRotatingModels[modelI] -> $ Rotate, rotVectors[*, modelI], thetas[frameI]-thetas[frameI-1] XObjView, Refresh=wTLB ; Update screen view ;; Get an image from the viewgroup (*pState).oObjViewWid -> Draw, oBuffer oImage = oBuffer -> Read() ;; Quantize this image with its own colour table and write to GIF oImage -> GetProperty, Data=imageRGB image8bit = Color_Quan(imageRGB, 1, r, g, b, Colors=256) Write_GIF, gifFile, image8bit, r, g, b, /Multiple, $ Delay_Time=gifDelayTime ENDFOR ;; Write first image again at end Write_GIF, gifFile, firstImage8bit, r0, g0, b0, /Multiple, $ Delay_Time=gifDelayTime, Repeat_Count=gifRepeatCount ;; Close the file, and we're done! Write_GIF, gifFile, /Close END