I am using GATE(which uses Geant4) to do MC studies on dosimetric output. I am using a cylindrical cobalt source at 80 cm SAD to measure the PDD in a water phantom and dose at depth of 10 cm.
I now want to simulate a smaller source (say, r/2 and h/2) and compare the dosimetric output at a depth of 10 cm. Besides the geometry, I see that I am able to control the number of particles and time of the simulation. What would be the best way to change these two parameters to mimic the lower output from a smaller source? Or is there any other parameter that can be changed to mimic a smaller source? I am trying to calculate the output factor of the smaller source w.r.t. to the original source.
Not sure if it helps, this is cylindrical source with Co60
Source::Source():
_particleGun{nullptr},
_sourceMessenger{nullptr},
_radius{-1.0},
_halfz{-1.0},
_nof_particles{10}
{
_particleGun = new G4ParticleGun( 1 );
G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
G4String particleName = "gamma"; // "geantino"
_particleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
_particleGun->SetParticlePosition(G4ThreeVector(0., 0., 0.));
_particleGun->SetParticleMomentumDirection(G4ThreeVector(0., 0., 1.));
_particleGun->SetParticleEnergy(1000.0*MeV);
_sourceMessenger = new SourceMessenger(this);
}
Source::~Source()
{
delete _particleGun;
delete _sourceMessenger;
}
troika Source::sample_direction()
{
double phi = 2.0 * M_PI * G4UniformRand();
double cos_z = 2.0 * G4UniformRand() - 1.0;
double sin_z = sqrt( (1.0 - cos_z) * (1.0 + cos_z) );
return troika{ sin_z * cos(phi), sin_z * sin(phi), cos_z };
}
double Source::sample_energy()
{
return (G4UniformRand() < P_lo) ? E_lo : E_hi;
}
void Source::GeneratePrimaries(G4Event* anEvent)
{
for(int k = 0; k != _nof_particles; ++k) // we generate _nof_particles at once
{
// here we sample spatial decay vertex uniformly in the cylinder
double z = _halfz * ( 2.0*G4UniformRand() - 1.0 );
double phi = 2.0 * M_PI * G4UniformRand();
double r = _radius * sqrt(G4UniformRand());
auto x = r * cos(phi);
auto y = r * sin(phi);
_particleGun->SetParticlePosition(G4ThreeVector(x, y, z));
// now uniform-on-the-sphere direction
auto dir = sample_direction();
_particleGun->SetParticleMomentumDirection(G4ThreeVector(dir._wx, dir._wy, dir._wz));
// energy 50/50 1.17 or 1.33
auto e = sample_energy();
_particleGun->SetParticleEnergy(e);
// all together in a vertex
_particleGun->GeneratePrimaryVertex(anEvent);
}
}
I don't know much about multi-threading and I have no idea why this is happening so I'll just get to the point.
I'm processing an image and divide the image in 4 parts and pass each part to each thread(essentially I pass the indices of the first and last pixel rows of each part). For example, if the image has 1000 rows, each thread will process 250 of them. I can go in details about my implementation and what I'm trying to achieve in case it can help you. For now I provide the code executed by the threads in case you can detect why this is happening. I don't know if it's relevant but in both cases(1 thread or 4 threads) the process takes around 15ms and pfUMap and pbUMap are unordered maps.
void jacobiansThread(int start, int end,vector<float> &sJT,vector<float> &sJTJ) {
uchar* rgbPointer;
float* depthPointer;
float* sdfPointer;
float* dfdxPointer; float* dfdyPointer;
float fov = radians(45.0);
float aspect = 4.0 / 3.0;
float focal = 1 / (glm::tan(fov / 2));
float fu = focal * cols / 2 / aspect;
float fv = focal * rows / 2;
float strictFu = focal / aspect;
float strictFv = focal;
vector<float> pixelJacobi(6, 0);
for (int y = start; y <end; y++) {
rgbPointer = sceneImage.ptr<uchar>(y);
depthPointer = depthBuffer.ptr<float>(y);
dfdxPointer = dfdx.ptr<float>(y);
dfdyPointer = dfdy.ptr<float>(y);
sdfPointer = sdf.ptr<float>(y);
for (int x = roiX.x; x <roiX.y; x++) {
float deltaTerm;// = deltaPointer[x];
float raw = sdfPointer[x];
if (raw > 8.0)continue;
float dirac = (1.0f / float(CV_PI)) * (1.2f / (raw * 1.44f * raw + 1.0f));
deltaTerm = dirac;
vec3 rgb(rgbPointer[x * 3], rgbPointer[x * 3+1], rgbPointer[x * 3+2]);
vec3 bin = rgbToBin(rgb, numberOfBins);
int indexOfColor = bin.x * numberOfBins * numberOfBins + bin.y * numberOfBins + bin.z;
float s3 = glfwGetTime();
float pF = pfUMap[indexOfColor];
float pB = pbUMap[indexOfColor];
float heavisideTerm;
heavisideTerm = HEAVISIDE(raw);
float denominator = (heavisideTerm * pF + (1 - heavisideTerm) * pB) + 0.000001;
float commonFirstTerm = -(pF - pB) / denominator * deltaTerm;
if (pF == pB)continue;
vec3 pixel(x, y, depthPointer[x]);
float dfdxTerm = dfdxPointer[x];
float dfdyTerm = -dfdyPointer[x];
if (pixel.z == 1) {
cv::Point c = findClosestContourPoint(cv::Point(x, y), dfdxTerm, -dfdyTerm, abs(raw));
if (c.x == -1)continue;
pixel = vec3(c.x, c.y, depthBuffer.at<float>(cv::Point(c.x, c.y)));
}
vec3 point3D = pixel;
pixelToViewFast(point3D, cols, rows, strictFu, strictFv);
float Xc = point3D.x; float Xc2 = Xc * Xc; float Yc = point3D.y; float Yc2 = Yc * Yc; float Zc = point3D.z; float Zc2 = Zc * Zc;
pixelJacobi[0] = dfdyTerm * ((fv * Yc2) / Zc2 + fv) + (dfdxTerm * fu * Xc * Yc) / Zc2;
pixelJacobi[1] = -dfdxTerm * ((fu * Xc2) / Zc2 + fu) - (dfdyTerm * fv * Xc * Yc) / Zc2;
pixelJacobi[2] = -(dfdyTerm * fv * Xc) / Zc + (dfdxTerm * fu * Yc) / Zc;
pixelJacobi[3] = -(dfdxTerm * fu) / Zc;
pixelJacobi[4] = -(dfdyTerm * fv) / Zc;
pixelJacobi[5] = (dfdyTerm * fv * Yc) / Zc2 + (dfdxTerm * fu * Xc) / Zc2;
float weightingTerm = -1.0 / log(denominator);
for (int i = 0; i < 6; i++) {
pixelJacobi[i] *= commonFirstTerm;
sJT[i] += pixelJacobi[i];
}
for (int i = 0; i < 6; i++) {
for (int j = i; j < 6; j++) {
sJTJ[i * 6 + j] += weightingTerm * pixelJacobi[i] * pixelJacobi[j];
}
}
}
}
}
This is the part where I call each thread:
vector<std::thread> myThreads;
float step = (roiY.y - roiY.x) / numberOfThreads;
vector<vector<float>> tsJT(numberOfThreads, vector<float>(6, 0));
vector<vector<float>> tsJTJ(numberOfThreads, vector<float>(36, 0));
for (int i = 0; i < numberOfThreads; i++) {
int start = roiY.x+i * step;
int end = start + step;
if (end > roiY.y)end = roiY.y;
myThreads.push_back(std::thread(&pwp3dV2::jacobiansThread, this,start,end,std::ref(tsJT[i]), std::ref(tsJTJ[i])));
}
vector<float> sJT(6, 0);
vector<float> sJTJ(36, 0);
for (int i = 0; i < numberOfThreads; i++)myThreads[i].join();
Other Notes
To measure time I used glfwGetTime() before and right after the second code snippet. The measurements vary but the average is about 15ms as I mentioned, for both implementations.
Starting a thread has significant overhead, which might not be worth the time if you have only 15 milliseconds worth of work.
The common solution is to keep threads running in the background and send them data when you need them, instead of calling the std::thread constructor to create a new thread every time you have some work to do.
Pure spectaculation but two things might be preventing the full power of parallelization.
Processing speed is limited by the memory bus. Cores will wait until data is loaded before continuing.
Data sharing between cores. Some caches are core specific. If memory is shared between cores, data must traverse down to shared cache before loading.
On Linux you can use Perf to check for cache misses.
if you wanna better time you need to split a cycle runs from a counter, for this you need to do some preprocessing. some fast stuff like make an array of structures with headers for each segment or so. if say you can't mind anything better you can just do vector<int> with values of a counter. Then do for_each(std::execution::par,...) on that. way much faster.
for timings there's
auto t2 = std::chrono::system_clock::now();
std::chrono::milliseconds f = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
So I'm trying to rotate a point about another point in a window, drawing it with DirectX. My issue is that the rotation is in a weird shape:
http://prntscr.com/iynh5f
What I'm doing is just rotating a point around the center of a window and drawing lines between the points.
vec2_t vecCenter1 { gui.iWindowSize[ 0 ] / 2.f, gui.iWindowSize[ 1 ] / 2.f };
for ( float i { 0.f }; i < 360.f; i += 2.f )
{
vec2_t vecLocation { vecCenter1.x, vecCenter1.y - 100.f };
static vec2_t vecOldLocation = vecLocation;
vecLocation.Rotate( i, vecCenter1 );
if ( i > 0.f )
Line( vecOldLocation, vecLocation, 2, true, D3DCOLOR_ARGB( 255, 255, 255, 255 ) );
vecOldLocation = vecLocation;
}
Here is my rotation:
void vec2_t::Rotate( float flDegrees, vec2_t vecSubtractVector )
{
flDegrees = ToRadian( flDegrees );
float flSin = sin( flDegrees );
float flCos = cos( flDegrees );
*this -= vecSubtractVector;
x = x * flCos - y * flSin;
y = x * flSin + y * flCos;
*this += vecSubtractVector;
}
I've tried a few different methods of rotation and none of them seem to work. If anyone could tell my what I'm doing wrong, I'd appreciate it.
Key lines:
x = x * flCos - y * flSin;
y = x * flSin + y * flCos; << problem
The second line is using the modified value of x, whereas it should be using the original. You must cache both coordinates (or at least x) before updating:
void vec2_t::Rotate( float flDegrees, vec2_t vecSubtractVector )
{
float flRadians = ToRadian( flDegrees );
float flSin = sin( flRadians );
float flCos = cos( flRadians );
// cache both values + pre-subtract
float xOld = x - vecSubtractVector.x;
float yOld = y - vecSubtractVector.y;
// perform the rotation and add back
x = xOld * flCos - yOld * flSin + vecSubtractVector.x;
y = xOld * flSin + yOld * flCos + vecSubtractVector.y;
}
To get rid of the if-statement in your for-loop, just compute the first point outside the loop, and start from the delta value instead of zero
Don't use static because it might cause thread safety issues (although not important in your case) - just declare it outside the loop
You seem to be missing a line segment - the condition needs to be <= 360.f (ideally plus an epsilon)
vec2_t vecCenter1 = { gui.iWindowSize[ 0 ] / 2.f, gui.iWindowSize[ 1 ] / 2.f };
const float delta_angle = 2.f;
vec2_t vecOldLocation = { vecCenter1.x, vecCenter1.y - 100.f };
for ( float i = delta_angle; i <= 360.f; i += delta_angle ) // complete cycle
{
vec2_t vecLocation = { vecCenter1.x, vecCenter1.y - 100.f };
vecLocation.Rotate( i, vecCenter1 );
Line( vecOldLocation, vecLocation, 2, true, // no if statement
D3DCOLOR_ARGB( 255, 255, 255, 255 ) );
vecOldLocation = vecLocation;
}
I am trying to draw a Spherical Cap with cyclinders.I am calling that function from my Display function.(Display function is my glutDisplayFunc) It seems the loop inside drawSphericalCap() doesn't work appropriately. It only loops once when i =0. I thought maybe I cannot loop outside of the display function. So that I copied the same code inside the display function. It didn't work also.
void drawSphericalCap(float shpereRadius, float maxRadius)
{
float r = shpereRadius;
float a = maxRadius;
float h = r - sqrt((r * r) - (a * a));
//teta + 2 * beta = 180
float teta = asin(a / r);
float tanBeta = tan((180 - teta) / 2);
float numberOfCylinders = a * 10;
float heightOfEachCylinder = h / (10 * a);
glColor3f(1.0, 1.0, 1.0);
int i = 0;
for (i ; i < numberOfCylinders ; i++){
cout << i;
float translateOfEachCylinder = r - h + (heightOfEachCylinder * i);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, translateOfEachCylinder, 0);
glRotatef(90,1.0,0,0);
glutWireCylinder(a, heightOfEachCylinder, 50, 50);
glPopMatrix();
h = h - heightOfEachCylinder;
a = h * tanBeta;
}
}
in display function:
glPushMatrix();
drawSphericalCap(5,3);
glPopMatrix();
I couldn't find the code of Spherical cap so I looked at the formula and I think the code above would do the job.. when I find the problem ofcourse..
Thanks for tips..
tan function takes radians not degrees.
And the problem with the loop is related to tan function.(it returns negative so loop ends after first iteration).
I tried gluCyclinder() instead of glutWireCyclinder() because I could arrange both top and bottom radiuses of cyclinders.
So I came up with a code that almost satisfies me but there are small things that needs to be corrected. But I won't make myself busy with it. So final SphericalCap() function is like this (CAPMULTIPLIER can be changed for better quality):
#define PI 3.14159265
#define CAPMULTIPLIER 10
void drawSphericalCap(float shpereRadius, float maxRadius)
{
float r = shpereRadius;
float a = maxRadius;
float h = r - sqrt((r * r) - (a * a));
//teta + 2 * beta = 180
float teta = asin(a / r);
float tanBeta = tan((PI - teta) / 2);
float numberOfCylinders = a * CAPMULTIPLIER;
float heightOfEachCylinder = h / (10 * a);
glColor3f(1.0, 1.0, 1.0);
int i = 0;
GLUquadricObj *cyclinder;
cyclinder = gluNewQuadric();
for (i; i < numberOfCylinders; i++){
//cout << "i="<<i<< " height="<<heightOfEachCylinder<<endl;
float translateOfEachCylinder = r - h;
float smallRadius = tanBeta * (h - heightOfEachCylinder) ;
glPushMatrix();
glTranslatef(0, translateOfEachCylinder, 0);
glRotatef(-90, 1.0, 0, 0);
gluCylinder(cyclinder, a, smallRadius, heightOfEachCylinder , 50,5);
glPopMatrix();
h = h - heightOfEachCylinder;
a = smallRadius;
teta = asin(a / r);
tanBeta = tan((PI - teta) / 2);
}
}
I've recently started experimenting with noise (simple perlin noise), and have run into a slight problem with animating it. So far come I've across an awesome looking 3d noise (https://github.com/ashima/webgl-noise) that I could use in my project but that I understood nothing of, and a bunch of tutorials that explain how to create simple 2d noise.
For the 2d noise, I originally used the following fragment shader:
uniform sampler2D al_tex;
varying vec4 varying_pos; //Actual coords
varying vec2 varying_texcoord; //Normalized coords
uniform float time;
float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); }
float ease(float p) { return 3*p*p - 2*p*p*p; }
float cnoise(vec2 p, int wavelength)
{
int ix1 = (int(varying_pos.x) / wavelength) * wavelength;
int iy1 = (int(varying_pos.y) / wavelength) * wavelength;
int ix2 = (int(varying_pos.x) / wavelength) * wavelength + wavelength;
int iy2 = (int(varying_pos.y) / wavelength) * wavelength + wavelength;
float x1 = ix1 / 1280.0f;
float y1 = iy1 / 720.0f;
float x2 = ix2 / 1280.0f;
float y2 = iy2 / 720.0f;
float xOffset = (varying_pos.x - ix1) / wavelength;
float yOffset = (varying_pos.y - iy1) / wavelength;
xOffset = ease(xOffset);
yOffset = ease(yOffset);
float t1 = rand(vec2(x1, y1));
float t2 = rand(vec2(x2, y1));
float t3 = rand(vec2(x2, y2));
float t4 = rand(vec2(x1, y2));
float tt1 = mix(t1, t2, xOffset);
float tt2 = mix(t4, t3, xOffset);
return mix(tt1, tt2, yOffset);
}
void main()
{
float t = 0;
int minFreq = 0;
int noIterations = 8;
for (int i = 0; i < noIterations; i++)
t += cnoise(varying_texcoord, int(pow(2, i + minFreq))) / pow(2, noIterations - i);
gl_FragColor = vec4(vec3(t), 1);
}
The result that I got was this:
Now, I want to animate it with time. My first thought was to change the rand function to take a vec3 instead of vec2, and then change my cnoise function accordingly, to interpolate values in the z direction too. With that goal in mind, I made this:
sampler2D al_tex;
varying vec4 varying_pos;
varying vec2 varying_texcoord;
uniform float time;
float rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.2332, 58.5065))) * 43758.5453); }
float ease(float p) { return 3*p*p - 2*p*p*p; }
float cnoise(vec3 pos, int wavelength)
{
ivec3 iPos1 = (ivec3(pos) / wavelength) * wavelength; //The first value that I'll sample to interpolate
ivec3 iPos2 = iPos1 + wavelength; //The second value
vec3 transPercent = (pos - iPos1) / wavelength; //Transition percent - A float in [0-1) indicating how much of each of the above values will contribute to final result
transPercent.x = ease(transPercent.x);
transPercent.y = ease(transPercent.y);
transPercent.z = ease(transPercent.z);
float t1 = rand(vec3(iPos1.x, iPos1.y, iPos1.z));
float t2 = rand(vec3(iPos2.x, iPos1.y, iPos1.z));
float t3 = rand(vec3(iPos2.x, iPos2.y, iPos1.z));
float t4 = rand(vec3(iPos1.x, iPos2.y, iPos1.z));
float t5 = rand(vec3(iPos1.x, iPos1.y, iPos2.z));
float t6 = rand(vec3(iPos2.x, iPos1.y, iPos2.z));
float t7 = rand(vec3(iPos2.x, iPos2.y, iPos2.z));
float t8 = rand(vec3(iPos1.x, iPos2.y, iPos2.z));
float tt1 = mix(t1, t2, transPercent.x);
float tt2 = mix(t4, t3, transPercent.x);
float tt3 = mix(t5, t6, transPercent.x);
float tt4 = mix(t8, t7, transPercent.x);
float tt5 = mix(tt1, tt2, transPercent.y);
float tt6 = mix(tt3, tt4, transPercent.y);
return mix(tt5, tt6, transPercent.z);
}
float fbm(vec3 p)
{
float t = 0;
int noIterations = 8;
for (int i = 0; i < noIterations; i++)
t += cnoise(p, int(pow(2, i))) / pow(2, noIterations - i);
return t;
}
void main()
{
vec3 p = vec3(varying_pos.xy, time);
float t = fbm(p);
gl_FragColor = vec4(vec3(t), 1);
}
However, on doing this, the animation feels... strange. It's as though I'm watching a slideshow of perlin noise slides, with the individual slides fading in. All other perlin noise examples that I have tried (like https://github.com/ashima/webgl-noise) are actually animated with time - you can actually see it being animated, and don't just feel like the images are fading in, and not being actually animated. I know that I could just use the webgl-noise shader, but I want to make one for myself, and for some reason, I'm failing miserably. Could anyone tell me where I am going wrong, or suggest me on how I can actually animate it properly with time?
You should proably include z in the sin function:
float rand(vec3 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233)) + co.z) * 43758.5453); }
Apparently the somewhat random numbers are prime numbers. This is to avoid patterns in the noise. I found another prime number, 94418953, and included that in the sin/dot function. Try this:
float rand(vec3 co) { return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 9441.8953))) * 43758.5453); }
EDIT: You don't take into account wavelength on the z axis. This means that all your iterations will have the same interpolation distance. In other words, you will get the fade effect you're describing. Try calculating z the same way you calculate x and y:
int iz1 = (int(p.z) / wavelength) * wavelength;
int iz2 = (int(p.z) / wavelength) * wavelength + wavelength;
float z1 = iz1 / 720.0f;
float z2 = iz2 / 720.0f;
float zOffset = (varying_pos.z - iz1) / wavelength;
This means however that the z value will variate the same rate that y will. So if you want it to scale from 0 to 1 then you should proably multiply z with 720 before passing it into the noise function.
check this code. it's a simple version of 3d noise:
// Here are some easy to understand noise gens... the D line in cubic interpolation (rounding)
function rndng ( n: float ): float
{//random proportion -1, 1 ... many people use Sin to take
//linearity out of a pseudo random, exp n*n is faster on central processor.
var e = ( n *321.9234)%1;
return (e*e*111.07546)%2-1;
}
function lerps(o:float, v:float, alpha:float):float
{
o += ( v - o ) * alpha;
return o;
}
//3d ----------------
function lnz ( vtx: Vector3 ): float //3d perlin noise code fast
{
vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
,
lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
,
D.z
);
}
//1d ----------------
function lnzo ( vtx: Vector3 ): float //perlin noise, same as unityfunction version
{
var total = 0.0;
for (var i:int = 1; i < 5; i ++)
{
total+= lnz2(Vector3 (vtx.x*(i*i),0.0,vtx.z*(i*i)))/(i*i);
}
return total*5;
}
//2d 3 axis honeycombe noise ----------------
function lnzh ( vtx: Vector3 ): float // perlin noise, 2d, with 3 axes at 60'instead of 2 x y axes
{
vtx= Vector3 ( Mathf.Abs(vtx.z) , Mathf.Abs(vtx.z*.5-vtx.x*.866) , Mathf.Abs(vtx.z*.5+vtx.x*.866) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
//D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
,
lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
,
D.z
);
}
//2d ----------------
function lnz2 ( vtx: Vector3 ): float // i think this is 2d perlin noise
{
vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.z)
,
lerps( rndng(W+125.0), rndng(W+126.0),D.x)
,
D.z
);
}