Hi everyone!

I was hoping to get some help with the writing of a Fuse that is above my head for the moment.

The Fuse is based on the tetra Blink script written by Steve Yedlin, snippets of the code can be seen in his display prep demo followup:

http://yedlin.net/DisplayPrepDemo/DispP ... owup.html

The script is shown around 14min 36s

I made a quick transcription that has most of the code, but some of it is missing.

The first intention of the script is to rearrange the color rendition to a subtractive model, so there are no sliders or adjustments that need values.

But it would also be possible to make it as a modifier, where you can target a specific hue and adjust the saturation levels, as shown at the end of the discussion (around 1h mark).

I already had some help so there is a basis to start from, but I'm new to coding myself so I hope some of you can join in and help me with this.

Thanks!

Pieter

Added in 47 seconds:

Blink script:

kernel Tetra : ImageComputationKernel<ePixelWise>

(

Image<eRead, eAccessPoint, eEdgeClamped> src; // the input image

Image>eWrite> dst; // the output image

param:

float3 red;

float3 grn;

float3 blu;

float3 cyn;

float3 mag;

float3 yel;

bool inv;

void define () {

defineParam(red, "red", float3 (1.f.0.f.0.f));

defineParam(grn, "grn", float3 (0.f.1.f.0.f));

defineParam(blu, "blu", float3 (0.f.0.f.1.f));

defineParam(cyn, "cyn", float3 (0.f.1.f.1.f));

defineParam(mag, "mag", float3 (1.f.0.f.1.f));

defineParam(yel, "yel", float3 (1.f.1.f.0.f));

}

float determinantMinor ( int rowHeight, int columnWidht, float3x3 matrix)

{

int y1 = rowHeight == 0 ? 1 : 0;

int y2 =

int x1 = columnWidht == 0 ? 1 : 0;

int x2 = columnWidht == 2 ? 1 : 2;

return (matrix{y1}{x1} * matrix{y2}{x2}) - (matrix{y1}{x2}) * matrix {y2}{x1});

}

float determinant (float3x3 thMatrix)

{

return (theMatrix{0} {0} * determinantMinor (0.f,0.f,theMatrix))

- (theMatrix{0} {1} * determinantMinor (0.f.1.f.theMatrix))

+ (theMatrix{0} {2} * determinantMinor (0.f,2.f,theMatrix))

}

float3x3 matrixInverse (float3x3 input)

{

float det = determinant (input)

// if the determinant is basically zero, then it is zero...

// if (ABS (det) < 1e-2)

// {

// memset (output, 0, sizeof(&output));

// return false;

// }

float3x3 output

int x,y;

for (y = 0; y<3; y++)

{

output{y}{x} = determinantMinor (x,y,input) * (1.f / det);

if( 1 == ((x+y) & 2) )

output {y}{x} = - output {y}{x};

}

return output

}

//A function that returns the rotational distance of a point (around the gray diagonal) from red

float pt_ang(float3triplet , float3 corners [6]) {

75 - 92 missing

float b_ang = atan2 (1.4142f,1.f)-pi/2f;

float3 tr2;

tr2.x = tr1.x * cos(b_ang) + tr1.y * sin (b_ang);

tr2.y = tri.x * (-sin(b_ang)) + tr1.y * cos (b_ang);

tr2.z = tr1.z;

float3 rc2;

rc2.x = rc1.x * cos(b_ang) + rc1.y * sin(b_ang);

rc2.y = rc1.x * (-sin(b_ang)) + rc1.y *cos(b_ang);

rc2.z = rc1.z;

// now rotate on the now vertical gray axis so that red is at -pi rotation angle

float f_ang = atan2(rc2.x,rc2.z) - pi;

float3 tr3;

tr3.x = tr2.x * cos(g_ang) + tr2.z * (-sin(g_ang));

tr3.y = tr2.y;

tr3.z = tr2.x * sin(g_ang) + tr2.z * cos(g_ang);

float3 rc3;

rc3.x = rc2.x * cos(g_ang) + rc2.z * (-sin(g_ang));

rc3.y = rc2.y;

rc3.z = rc2.x * sin(g_ang) + rc2.z * cos(g_ang);

return atan2 (tr3.x , tr.z) - atan2(rc3.x , rc3.z);

}

//A function that finds which of the 6 tetrahedrons a point is contained in,

//even if the cube has been deformed

int tetra_region(float3 rgb, float3 corners{6}){

float pi = 3.14159265f;

float ang = pt_ang(rgb , corners);

float corner_ang(6);

for (int 1 = 0; 1 < 6; ++1) {

corner_angle = pt_ang (corners {i}, corners);

}

int region = 5;

for (int i = 0; i < 5; ++i) {

if ( ang>=corner_angs(i) && ang < corner_angs (i+i) ) {

region = i;

break;

}

}

return region;

}

float tetra ( float3 triplet , float3 corners [6]){

float r = triplet.x;

147 - 152 missing

float3 yel = corners[1];

float3 grn = corners[2];

float3 cyn = corners[3];

float3 blu = corners[4];

float3 mag = corners[5];

if (r>g) {

//r>g>b

if (g>b) (

return r*red + g*(yel-red) + b*(wht-yel);

)

//r>b>g

else if (r>b) {

return r*red + g*(whit-mag) + b*(mag-red);

}

//b>r>g

168 - 173 missing

if (b>g) {

return r*(wht-cyn) + g*(cyn-blu) + b*blu;

}

//g>b>r

else if (b>r) {

return r*(wht-cyn) + g*grn + b*(cyn-grn);

}

//g>r>b

else {

return r*(yel-grn) + g*grn + b*(wht-yel);

}

}

}

float3 inv_tetra ( float3 triplet , float3 corners[6]){

float r = triplet.x;

float g = triplet.y;

float b = triplet.z;

float3 wht = float3(1.f.1.f,1.f);

float3 red = corners[0];

float3 yel = corners[1];

float3 grn = corners[2];

float3 cyn = corners[3];

float3 blu = corners[4];

float3 mag = corners[5];

int region = tetra_region (triplet,corners);

float3 cR;

float3 cG;

float3 cB;

if (region == 0) {}

//r>g>b

cR = red;

cG = yel-red;

cB = wht-yel;

}

else if (region == 1){

//g>r>b

cR = yel-grn;

cG = grn;

cB = wht-yel

}

else if (region == 2){

//g>b>r

cR = wht-cyn;

cG = grn;

cB = cyn-grn;

}

else if (region == 3){

227 - 231 Missing

else if (region == 4){

//b>r>g

cR = mag-blu;

cG = wht-mag;

cB = blu;

}

else {

//r>b>g

cR = red;

cG = wht-mag;

cB = mag-red;

}

float3x3 matrix = float3x3(cR.x.cR.y,cR.z.cG.x,cG.y,cG.z,cB.x,cB.x,cB.y,cB.z);

float3x3 inverse = matrixInverse(matrix);

cR.x = inverse[0][0];

cR.y = inverse[0][1];

cR.z = inverse[0][2];

252 - 255 missing

cB.x = inverse[2][0];

cB.y = inverse[2][1];

cB.z = inverse[2][2];

return (r * cR + g * cG + b * cB);

}

void process () {

// Read the input image

SampleType (src) input = src();

float3 rgb;

rgb.x = input.x;

rgb.y = input.y;

rgb.z = input.z;

float3 corners [6];

corners[0] = red;

corners[1] = yel;

corners[2] = grn;

corners[3] = cyn;

corners[4] = blu;

corners[5] = mag;

float output = tetra ( rgb , corners );

if (inv) {

output = inv_tetra( rgb , corners);

}

//Write the result tot the output image

dst() = float4 ( output.x, output.y , output.z , input.w);

}

};

Added in 1 minute 38 seconds:

Fuse basis:

function Process(req)

local img = InImage:GetValue(req)

local red = { 1, 0, 0 }

local grn = { 0, 1, 0 }

local blu = { 0, 0, 1 }

local cyn = { 0, 1, 1 }

local mag = { 1, 0, 1 }

local yel = { 1, 1, 0 }

local inv

--[[--

This is a block comment, so you can just copy-paste all of this without having

to worry about removing my commentary.

Next we have some function declarations. We usually don't want to nest these

inside our Process function. It will work, so long as you never call the function

prior to it being declared, but it's not good practice. Look to the bottom of the

fuse for the functions.

Of course, nothing in the code is actually calling these functions, and there is

one call to a function that doesn't exist here. So nothing will happen in this fuse

as it currently exists

--]]--

local matrix = {cR.x, cR.y, cR.z, cG.x, cG.y, cG.z, cB.x, cB.y, cB.z}

local inverse = matrixInverse(matrix)

--[[--

We create a matrix here, then invert it, but nothing is being done with the matrix,

so there's not yet much point to it. We'll also need to invoke the matrixutils

library in order for the inversion to work, or write our own. As it stands, this

Fuse will not compile due to the absence of matrixInverse() and tetra_region().

--]]--

OutImage:Set(req, img) --img has not been defined, which will likely cause Fusion to crash

end -- End of Process()

--[[-- Comments from the fuse:

By calculating the sine and cosine of the angle, you can get the point one unit away from the origin at

the given rotation. Just multiply the coordinates by whatever distance you need. Cosine corresponds

to X and sine corresponds to Y.

In other words:

Decide on the angle.

Calculate X by getting the cosine of the angle.

Calculate Y by getting the sine of the angle.

Multiply X and Y by the distance.

Offset X and Y by your "source" point.

--]]--

function pt_ang(triplet, corners)

local b_ang = math.atan2(1.4142, 1)-math.pi/2

--[[--

Most trig functions are contained in the math library, and we have to use the

table notation to access them. math.sin(), math.exp(), etc.

I am not sure why b_ang is calculated this way. With no variables present,

it would be much more efficient to just set it equal to -0.61548422949032.

In any case, atan2() returns the angle in radians between the x axis and a ray

drawn from the origin through the point (y, x). Note that it's not (x,y); the

coordinate is reversed. Then we subtract a quarter revolution, forcing the angle

to be somewhere in Quadrants I or IV (+x)

--]]--

local tr2 = {}

-- If we're going to put a table in a variable, we need to call the constructor like this first.

--[[--

This is the first place where the lack of information in the source code is

harming us. We don't yet know what should be in tr1 or rc1. We'll have to try

to guess from context, which is currently lacking. We'll see if it becomes

clearer later on. I suspect that tr1 is the argument 'triplet' and rc1 is

'corners', but I can't be certain.

--]]--

tr2.x = tr1.x * math.cos(b_ang) + tr1.y * math.sin(b_ang)

tr2.y = tr1.x * -math.sin(b_ang) + tr1.y * cos(b_ang)

tr2.z = tr1.z

local rc2 = {}

rc2.x = rc1.x * math.cos(b_ang) + rc1.y * math.sin(b_ang)

rc2.y = rc1.x * -math.sin(b_ang) + rc1.y * math.cos(b_ang)

rc2.z = rc1.z

-- The above lines are simply rotations around the z (blue) axis

-- now rotate on the now vertical gray axis so that red is at -pi rotation angle

-- (Bryan) This was f_ang in your fuse. I suspect it's suppose to be g_ang to match

-- all of the lines that follow.

local g_ang = math.atan2(rc2.x, rc2.z) - math.pi

local tr3 = {}

tr3.x = tr2.x * math.cos(g_ang) + tr2.z * -math.sin(g_ang)

tr3.y = tr2.y

tr3.z = tr2.x * math.sin(g_ang) + tr2.z * math.cos(g_ang)

local rc3 = {}

rc3.x = rc2.x * math.cos(g_ang) + rc2.z * -math.sin(g_ang)

rc3.y = rc2.y

rc3.z = rc2.x * math.sin(g_ang) + rc2.z * cos(g_ang)

-- Those were rotations around the y (green) axis

-- We return a scalar value, but I'm not sure what exactly is happening here.

-- I'll have to do some graphing, maybe, to figure it out.

return math.atan2(tr3.x, tr3.z) - math.atan2(rc3.x, rc3.z)

end -- End of pt_ang()

--[[--

A function that finds which of the 6 tetrahedrons a point is contained in,

even if the cube has been deformed

--]]--

function tetra(triplet, corners)

-- Guessing the rest of the color variables are supposed to be

-- declared here, as they are in inv_tetra.

local b = triplet.z

local wht = (1, 1, 1)

local red = corners[0]

local yel = corners[1]

local grn = corners[2]

local cyn = corners[3]

local blu = corners[4]

local mag = corners[5]

--[[--

In Lua, if statements take the form of:

if <condition> then

<do stuff>

end

They can, of course, be nested, as we see below, and they

can use elseif to provide alternate conditions.

The indentation is not syntactically required, but it makes

it much easier to read the code. For very short statements,

the entire thing can be put one line:

if <condition> then <do stuff> end

The if can also use Boolean logic:

if <condition1> AND <condition2> then

<both conditions must be true for this code to execute>

end

if not <condition1> then

<this executes if the condition is false or nil>

end

--]]--

if r>g then

if g>b then

return r*red + g*(yel-red) + b*(wht-yel) --r>g>b

elseif r>b then

return r*red + g*(wht-mag) + b*(mag-red) --r>b>g

end

if b>r then

return r*(mag-blu) + g*(wht-mag) + b*blu -- b>g>r

end

end

if b>g then

return r*(wht-cyn) + g*(cyn-blu) + b*blu -- b>g>r

elseif b>r then

return r*(wht-cyn) + g*grn + b*(cyn-grn) -- g>b>r

else

return r*(yel-grn) + g*grn + b*(wht-yel) -- g>r>b

end

end -- End of tetra()

function inv_tetra(triplet, corners)

local r = triplet.x

local g = triplet.y

local b = triplet.z

local red = corners[0]

local yel = corners[1]

local grn = corners[2]

local cyn = corners[3]

local blu = corners[4]

local mag = corners[5]

local region = tetra_region(triplet, corners)

local cR

local cG

local cB

if region == 0 then

cR = red

cG = yel-red

cB = wht-yel

elseif region == 1 then

cR = yel-grn

cG = grn

cB = wht-yel

elseif region == 2 then

cR = wht-cyn

cG = grn

cB = cyn-grn

elseif region == 3 then

cR = wht-cyn

cG = cyn-blu

cB = blu

elseif region == 4 then

cR = mag-blu

cG = wht-mag

cB = blu

else

cR = red

cG = wht-mag

cB = mag-red

end

end -- End of inv_tetra()

**Welcome to WSL!**

Make yourself at home, but before posting, please may I ask you to read the following topics.

Make yourself at home, but before posting, please may I ask you to read the following topics.

PS. please pretty please:

**Posting 101****Server space, screenshots, and you****Thank you!**PS. please pretty please:

## [DEV] Tetrahedral interpolation (from the Yedlin demo)

Where the future is being made, today.

Welcome to the WSL development corner!

In this forum, please post your development projects. You get kudos and feedback here.

Topics ideally have preset prefixes, and this is what they (might) mean:

Once a development project has been released (hurray), topics can be marked as - you guessed it - [RELEASED]

Development topics only, please. For generic questions, how-to's, questions and inquiries about existing tools etc, please go to the appropriate other forums.

In this forum, please post your development projects. You get kudos and feedback here.

Topics ideally have preset prefixes, and this is what they (might) mean:

- [DEV] - very much work in progress, don't build a business on this, could go anywhere
- [BETA] - should kinda do what it's supposed to do, please test, give feedback
- [RC] - this may end up in Reactor soon, polishing up, now's the time for last minute thoughts
- [ABD] - died a premature death, sadness, will not see the light of day ever (unless someone picks up the scraps)

Once a development project has been released (hurray), topics can be marked as - you guessed it - [RELEASED]

Development topics only, please. For generic questions, how-to's, questions and inquiries about existing tools etc, please go to the appropriate other forums.

- PieterDumoulin
**Posts:**7**Joined:**Sat May 30, 2020 5:55 am

- cinewrangler
- Fusioneer
**Posts:**50**Joined:**Wed Nov 15, 2017 6:47 am**Location:**Europe**Been thanked:**1 time

### Re: [DEV] Tetrahedral interpolation (from the Yedlin demo)

Your version of matrixInverse() in the Blink script looks wrong, it is missing a for() loop over 'x'.

The missing code snippets in the tetrahedral interpolation are easy to fill in. Basically such a code determines which of the six tetrahedrons that make up the cube your RGB pixel falls into. Each tetrahedron has four corners (i.e. it touches four out of the eight corners of the full cube). - So just take a piece of paper and draw a cube and label the corners with the corresponding colors (hint: one corner is black and one is white and these stay constant in the Blink code, i.e. can not be moved around)... then it is easy to see which tetrahedron corresponds to the piece of code you're missing there.

The missing code snippets in the tetrahedral interpolation are easy to fill in. Basically such a code determines which of the six tetrahedrons that make up the cube your RGB pixel falls into. Each tetrahedron has four corners (i.e. it touches four out of the eight corners of the full cube). - So just take a piece of paper and draw a cube and label the corners with the corresponding colors (hint: one corner is black and one is white and these stay constant in the Blink code, i.e. can not be moved around)... then it is easy to see which tetrahedron corresponds to the piece of code you're missing there.

- cayubal
**Posts:**1**Joined:**Fri Oct 16, 2020 8:18 am

### Re: [DEV] Tetrahedral interpolation (from the Yedlin demo)

This might help for the tetra part (see pages 56 and 57 of that PDF): https://www.filmlight.ltd.uk/pdf/whitep ... areLib.pdf

Wondering if that code might be turned into a DCTL for use in Resolve

Wondering if that code might be turned into a DCTL for use in Resolve

- zecko
**Posts:**1**Joined:**Sun Oct 18, 2020 5:56 pm

### Re: [DEV] Tetrahedral interpolation (from the Yedlin demo)

watching the development here with great interest! I watched his demo and came away wondering the same thing.