/* * petri(dot)leskinen[at]aalto(dot)fi * Espoo, Finland, September 2009 - April 2011 */ #define distanceSquared(a) ((a.x*a.x)+(a.y*a.y))/size/size // mysterious clamp(...) takes care of antialiasing the edges #define addPixel pxlOut += (clamp(0.75*size*(1.0-dist/radius), 0.0, 1.0))*pxl; // adds a sample from a nearby circle: #define addSample(s) p1=(p+s)*rotR+base-outCoord();pxl=sampleNearest(src,p1+outCoord());dist=distanceSquared(p1);addPixel // Inverse of a 2x2 matrix, numbers reorganized and divided by determinant: #define inverse2x2(rot) float2x2(rot[1][1],-rot[0][1],-rot[1][0],rot[0][0])/(rot[0][0]*rot[1][1]-rot[1][0]*rot[0][1]) kernel Tiling < namespace : "Disks"; vendor : "Petri Leskinen"; version : 1; description : "Disk tiling "; > { parameter float size < minValue: float(1); maxValue: float(200); defaultValue: float(32); description: "Pattern Size"; >; parameter float radius < minValue: float(0.1); maxValue: float(4.4); defaultValue: float(0.67); description: "radius: how much a disks fills up its space"; >; parameter float2 base < minValue: float2(-200,-200); maxValue: float2(800,500); defaultValue: float2(0,0); description: "Base Point"; >; /* // replaced by presetting rotation in actionscript: parameter float angle < minValue: float(-3.14159); maxValue: float(3.14159); defaultValue: float(0.0); description: "Rotation around Base Point"; >; */ parameter float2x2 rotation < minValue: float2x2(-1,-1,-1,-1); maxValue: float2x2(1,1,1,1); defaultValue: float2x2(1,0,0,1); description: "Rotation around Base Point"; >; // btw: enter 1,-0.5,0.0,0.866 for a rectangular pattern // sqrt(3)/2, a constant always needed for hexagon maths const float halfSqrt3 = 0.866025404; /* coordinate system for hexagonal pattern, angle between x and y 60 degrees * transform hcs and it's inverse hcsR */ const float2x2 hcs = float2x2(halfSqrt3, 0.5, 0.0, 1.0); const float2x2 hcsR = inverse2x2(hcs); input image4 src; output pixel4 pxlOut; void evaluatePixel() { /* // replaced by presetting rotation in actionscript: float cs = cos(angle); float sn = sin(angle); float2x2 rotation = float2x2(cs,sn,-sn,cs); */ float2x2 rotationR = inverse2x2(rotation); // Transform from screen to hexagonal system: float2x2 rot = rotation*hcs/size; // Reverse, from hexagonal to screen coordinates: float2x2 rotR = hcsR*size*rotationR; // Transform to hexagonal system, integer coordinates float2 p = floor((outCoord()-base)*rot+0.5); // init output to (0,0,0,0) pxlOut = pixel4(0); // switch back to image coordinates, sample the pixel float2 p1 = p*rotR +base -outCoord(); pixel4 pxl = sampleNearest(src, p1+outCoord()); // if new point inside the disk, add sampled value to the output float dist = distanceSquared(p1); addPixel; // repeat same for neighbour cells, float2(+-1,+-1) addSample(float2(-1,-1)); addSample(float2(-1,0)); addSample(float2(0,-1)); addSample(float2(0,1)); addSample(float2(1,-1)); addSample(float2(1,0)); addSample(float2(1,1)); addSample(float2(-1,1)); // 1.0 // for larger radii more sampled points are needed: if (radius>2.25) { addSample(float2(-2,0)); // 2.4 addSample(float2(-2,-1)); // 2.4 addSample(float2(-1,-2)); // 2.25 addSample(float2(0,-2)); // 2.4 addSample(float2(0,2)); // 2.4 addSample(float2(1,2)); // 2.25 addSample(float2(2,0)); // 2.25 addSample(float2(2,1)); // 2.25 if (radius>3.0) { addSample(float2(-2,-2)); addSample(float2(2,2)); } } // adjust because in most cases there are several overlapping disks: // pxlOut *= 0.5/radius; // luminocity; // if (samplesTaken>0) pxlOut /= float(samplesTaken); } }