/*Makes a grid based on cycloid grid from Baddeley, A.J., H.J.G. Gundersen, and L.M. Cruz-Orive, J. of Microsc. 1986, 142:259-276. for stereological quantification on vertical uniform random sections as non-destructive overlay. Image vertical axis should be aligned with the grid vertical axis (placed on the left side). Do not forget to "Set Scale" to get correct printout of the grid parameters, which are reflected in the "Cycloid grid parameters" window. "Central Points" makes two points in every tile. "Line Points" makes 4 additional points per tile. "Segmented lines" makes circular arcs with total length 2 times shorter than solid lines. Test line per point (l/p) constant is used to estimate surface density (surface area per unit volume) in uniform random sections. Version: 1.0 Date: 04/09/2014 Author: Aleksandr Mironov amj-box@mail.ru */ requires("1.49g"); var x0, y0, tmax, newStart = newArray(2); macro "Cycloid_Grid" { //help html = "" +"

Cycloid Grid

" +"is a linear test system for vertical uniform random sections

" +"based on Baddeley, Gundersen, Cruz-Orive (J. of Microsc. 1986, 142:259-276)

" +"Options:
" +"Tile density - determines density of the grid
" +"Central Points - two points per grid tile
" +"Line Points - 4 additional points per grid tile

" +"New Overlay - removes previous overlays
" +"Random Offset - randomizes grid location
" +"Segmented lines - arcs with half of total length of solid lines

" +"Set Scale to get correct printout of the grid parameters,
" +"which are reflected in the 'Cycloid grid parameters' window

" +"Useful parameters:

" +"Area per point can be used to estimate an area in 2D samples
" +"or volume density in uniform random sections

" +"Test line per point constant is used to estimate surface density
" +"(surface area per unit volume) in vertical uniform random sections

" // Cycloid settings tmax = 3.251; nPoints = 30; getDimensions(w, h, channels, slices, frames); name = getTitle(); stSegm = random; //Creating dialog box Dialog.create("Cycloid Grid, ver. 1.0"); Dialog.addNumber("Tile density =", 6,0,2,"within height"); Dialog.addCheckbox("Central Points", true); Dialog.addCheckbox("Line Points (x2 of central)", true); Dialog.addMessage("\n "); Dialog.addChoice("Line type", newArray("segmented", "solid")); Dialog.addNumber("Line thickness =", 1,0,2,"pixels"); Dialog.addChoice("Line color:", newArray("cyan", "red", "green", "magenta", "blue", "yellow", "orange", "black", "white")); Dialog.addChoice("Central Point color:", newArray("green", "cyan", "red", "magenta", "blue", "yellow", "orange", "black", "white")); Dialog.addCheckbox("New Overlay", true); Dialog.addCheckbox("Random Offset", true); Dialog.addCheckbox("Show Vertical Axis", true); Dialog.addHelp(html); Dialog.show(); //grid parameters ntiles = Dialog.getNumber();; tile = h/ntiles; rad = tile/4; cycles = w/PI/4/rad; point = Dialog.getCheckbox();; points = Dialog.getCheckbox();; typeCycloid = Dialog.getChoice(); th = Dialog.getNumber(); color = Dialog.getChoice(); colorC = Dialog.getChoice(); new = Dialog.getCheckbox(); if (new == true) Overlay.remove; //Creating offset off1 = random; off2 = random; offset = Dialog.getCheckbox(); if (offset == false) off1 = off2 = 0; xoff = -round(off1*PI*tile); yoff = -round(off2*tile); newStart[0] = xoff; newStart[1] = yoff; //vertical axis arrow = Dialog.getCheckbox(); //Filling image with grid tiles for (i = 0; i <= ntiles+1; i++){ for (j = 0; j <= cycles+1; j++){ startSegm = stSegm; drawCurve(typeCycloid, startSegm); run("Select None"); } newStart[0] = xoff; newStart[1] = newStart[1] + tile; for (j = 0; j <= cycles+1; j++){ startSegm = 1-stSegm; drawCurve(typeCycloid, startSegm); run("Select None"); } newStart[0] = xoff; newStart[1] = newStart[1] + tile; } if (arrow == true){ makeArrow(th*2, h, th*2, 0, "notched large outline"); Roi.setStrokeWidth(th*2); Roi.setStrokeColor(colorC); run("Add Selection..."); } //Drawing one segment of cycloid function makeSegm(x, y, rad, tmax, k1, k2, type){ xArr = newArray(nPoints); yArr = newArray(nPoints); for (pt = 0; pt < nPoints; pt++){ t = pt/nPoints * tmax; xArr[pt] = newStart[0] + rad * (t+k1*sin(t)); yArr[pt] = newStart[1] + k2*rad * (1-cos(t)); } if (type == "solid"){ makeSelection("polyline", xArr, yArr); run("Add Selection...", "width="+th+" stroke="+color); } //last segment coordinates newStart[0] = xArr[pt-1]; newStart[1] = yArr[pt-1]; return newStart; } //Drawing cycloid from segments function drawCurve(typeCycloid, startSegm){ //First segment if (typeCycloid == "segmented" && startSegm <= 0.5){ type = "empty"; } else { type = "solid"; } k1 = k2 = -1; makeSegm(x0, y0, rad, tmax, k1, k2, type); if (points == true){ drawEndLineV(); } if (point == true){ kk = 1; drawCentralPoint(kk); } //Second segment if (typeCycloid == "segmented" && startSegm > 0.5){ type = "empty"; } else { type = "solid"; } k1 = k2 = 1; makeSegm(x0, y0, rad, tmax, k1, k2, type); if (points == true){ drawEndLineH(); } //Third segment if (typeCycloid == "segmented" && startSegm <= 0.5){ type = "empty"; } else { type = "solid"; } k1 = -1; makeSegm(x0, y0, rad, tmax, k1, k2, type); if (points == true){ drawEndLineV(); } if (point == true){ kk = -1; drawCentralPoint(kk); } //Fourth segment if (typeCycloid == "segmented" && startSegm > 0.5){ type = "empty"; } else { type = "solid"; } k1 = 1; k2 = -1; makeSegm(x0, y0, rad, tmax, k1, k2, type); if (points == true){ drawEndLineH(); } } //Vertical End Points function drawEndLineV(){ setColor(color); setLineWidth(th); y1 = newStart[1] - tile/24; y2 = newStart[1] + tile/24; x = newStart[0]; Overlay.drawLine(x,y1,x,y2); Overlay.add; Overlay.show; } //Horizontal End Points function drawEndLineH(){ setColor(color); setLineWidth(th); x1 = newStart[0] - tile/24; x2 = newStart[0] + tile/24; y = newStart[1]; Overlay.drawLine(x1,y,x2,y); Overlay.add; Overlay.show; } //Central Points function drawCentralPoint(kk){ setColor(colorC); setLineWidth(th); x1 = newStart[0] - tile/24; x2 = newStart[0] + tile/24; y = newStart[1] + kk*tile/2; x = newStart[0]; y1 = newStart[1] + kk*tile/2 - tile/24; y2 = newStart[1] + kk*tile/2 + tile/24; Overlay.drawLine(x,y1,x,y2); Overlay.add; Overlay.drawLine(x1,y,x2,y); Overlay.add; Overlay.show; } // Printing the parameters of the grid getPixelSize(unit, pw, ph, pd); window = isOpen("Cycloid grid parameters"); title = "[Cycloid grid parameters]"; if (window == false){ run("Text Window...", "name="+ title +"width=60 height=16 menu"); setLocation(0, 0); } if (point == true) cp = 2; else cp = 0; if (points == true)ep = 4; else ap = 0; if (typeCycloid == "segmented") lr = 2; else lr = 1; print(title, "\nCycloid Grid for sample ["+name+"]"); print(title, "\n\nImage size = "+w+"x"+h+" pixels"); print(title, "\nPixel size = "+pw+" "+unit); print(title, "\nScale = "+1/pw+" pixels/"+unit); print(title, "\n\nGrid tiles per image = "+ntiles*w/PI/tile); print(title, "\nLine length per tile = "+tile*4*pw/lr+" "+unit); if (point || points == true) { print(title, "\nArea per point ="+tile*tile*2*PI*pw*ph/(cp+ep)+" "+unit+"^2"); print(title, "\nTest line per point(l/p) ="+pw*PI*tile/(cp+ep)/lr+" "+unit); } print(title, "\n_______________________\n"); }