CHICIO CODING

Dirty clean code. Creative Stuff. Stuff.

Model View Presenter on Android: unit test for everything

In this post I will talk about the model view presenter architectural pattern and how it can improve you unit test and your TDD workflow when developing an Android application.


In a previous post I talked about the Model View Presenter architectural pattern and how it could help you to develop apps fully tested. But what about Android? Can we reach the same level of testability using this pattern and improve our TDD workflow? Let’s find it out :smirk:!!
In this post we will try to develop the same application we developed on iOS in the previous post: a simple product catalog that shows a list of products. When you tap on one of them, its details is shown. Below you can find the same mockup we used for the iOS version.

Model view presenter mockup

Let’s start by creating a Product class. We need also to create a ProductsRepository class: in our case it will be a fake one that return to listener a list of products after 3 seconds. We do this to simulate a web service call. This is our implementation for the Product class:

public class Product {
    private String name;
    private String description;
    private String image;

    public Product(String name, String description, String image) {
        this.name = name;
        this.description = description;
        this.image = image;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String getImage() {
        return image;
    }
}

And below there is the implementation of our ProductsRepository.

public class ProductsRepository implements Repository {
    private ProductsRepositoryListener productsRepositoryListener;

    @Override
    public void setListener(ProductsRepositoryListener productsRepositoryListener) {
        this.productsRepositoryListener = productsRepositoryListener;
    }

    public void get() {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                Product[] products = {
                    new Product("Car", "A beautiful car", "car"),
                    new Product("Book", "", "book")
                };
                productsRepositoryListener.onRetrieved(products);
            }
        }, 3000);
    }
}

As we said before, and as you can see above the repository return to a ProductsRepositoryListener the list of products. So the interface definition for that listener is:

public interface ProductsRepositoryListener {
    void onRetrieved(Product[] products);
}

Now we can start to create our presenter. First of all we will define a ProductsView that will be responsible to implement the real platform dependent UI code. Our presenter will be responsible for:

  • the start of the view in the onStart() method. In this method it will update the view title, show a loading status and more important it will start the retrieve of the product list and become its listener.
  • listening to the repository callback when the products are retrieve. So our presenter will implement the ProductsRepositoryListener interface and pass to the view the updated product list.
  • the show detail action in the onSelected(Product product) method. In this case it will need to check that we have a valid product and eventually show its detail (or an error message).

First let’s see our ProductsView interface:

public interface ProductsView {
    void showLoadingStatus();
    void hideLoadingStatus();
    void show(String title);
    void show(Product[] products);
    void showErrorWith(String message);
    void showDetailFor(Product product);
}

Our ProductsPresenter implementation will be the following:

public class ProductsPresenter implements ProductsRepositoryListener {
    private ProductsView productsView;
    private Repository productsRepository;

    public ProductsPresenter(ProductsView productsView, Repository productsRepository) {
        this.productsView = productsView;
        this.productsRepository = productsRepository;
    }

    public void onStart() {
        productsView.show("Products");
        productsView.showLoadingStatus();
        productsRepository.setListener(this);
        productsRepository.get();
    }

    @Override
    public void onRetrieved(Product[] products) {
        tryToShowTheProducts(products);
        productsView.hideLoadingStatus();
    }

    private void tryToShowTheProducts(Product[] products) {
        if (isValidProduct(products)) {
            productsView.show(products);
        } else {
            productsView.showErrorWith("No products available");
        }
    }

    private boolean isValidProduct(Product[] products) {
        return products != null;
    }

    public void onSelected(Product product) {
        if (hasValidDescription(product)) {
            productsView.showDetailFor(product);
        } else {
            productsView.showErrorWith("Product without description");
        }
    }

    private boolean hasValidDescription(Product product) {
        return !product.getDescription().equals("");
    }
}

And this are our presenter tests:

public class ProductsPresenterTest {
    private ProductsView productsView;
    private Repository productsRepository;
    private ProductsPresenter productsPresenter;
    private final Product[] products  = new Product[]{new Product("aName", "aDescription", "anImage")};
    private final Product product  = new Product("aName", "aDescription", "anImage");

    @Test
    public void onStart() throws Exception {
        givenAProductsRepository();
        givenAProductsView();
        givenAProductsPresenter();
        whenTheProductsPresenterStarts();
        thenTheTitleIsDisplayed();
        thenTheProductViewShowsLoadingStatus();
        thenTheProductsPresenterBecomeListenerOfTheRepository();
        thenTryToRetrieveProduct();
    }

    @Test
    public void onProductsRetrieved() throws Exception {
        givenAProductsRepository();
        givenAProductsView();
        givenAProductsPresenter();
        whenTheProductsHaveBeenRetrieved();
        thenTheProductsViewShowsTheProducts();
        thenTheProductsViewHidesLoadingStatus();
    }

    @Test
    public void onNoProductsRetrieved() throws Exception {
        givenAProductsRepository();
        givenAProductsView();
        givenAProductsPresenter();
        whenTheProductsHaveNotBeenRetrieved();
        thenTheProductsViewShowsAnErrorMessage();
        thenTheProductsViewHidesLoadingStatus();
    }

    @Test
    public void onProductSelectedWithDescription() throws Exception {
        givenAProductsRepository();
        givenAProductsView();
        givenAProductsPresenter();
        whenAProductIsSelected();
        thenTheViewShowsTheProductDetail();
    }

    private void givenAProductsPresenter() {
        productsPresenter = new ProductsPresenter(productsView, productsRepository);
    }

    private void givenAProductsRepository() {
        productsRepository = mock(Repository.class);
    }

    private void givenAProductsView() {
        productsView = mock(ProductsView.class);
    }

    private void whenTheProductsPresenterStarts() {
        productsPresenter.onStart();
    }

    private void whenTheProductsHaveBeenRetrieved() {
        productsPresenter.onRetrieved(products);
    }

    private void whenTheProductsHaveNotBeenRetrieved() {
        productsPresenter.onRetrieved(null);
    }

    private void whenAProductIsSelected() {
        productsPresenter.onSelected(product);
    }

    private void thenTheTitleIsDisplayed() {
        verify(productsView).show(anyString());
    }

    private void thenTryToRetrieveProduct() {
        verify(productsRepository).get();
    }

    private void thenTheProductsPresenterBecomeListenerOfTheRepository() {
        verify(productsRepository).setListener(productsPresenter);
    }

    private void thenTheProductViewShowsLoadingStatus() {
        verify(productsView).showLoadingStatus();
    }

    private void thenTheProductsViewHidesLoadingStatus() {
        verify(productsView).hideLoadingStatus();
    }

    private void thenTheProductsViewShowsTheProducts() {
        verify(productsView).show(products);
    }

    private void thenTheProductsViewShowsAnErrorMessage() {
        verify(productsView).showErrorWith(anyString());
    }

    private void thenTheViewShowsTheProductDetail() {
        verify(productsView).showDetailFor(product);
    }
}

It’s easy to see that with our presenter we are able to test, also on Android, each UI operation without have to deal with platform dependent code in our unit test. As you can see our unit test are written, as on iOS, following the typical Behaviour Driven Development given-than-when approch. Now the same question we had in the iOS version of the app will start floating around in your head: who is our view? Fragments!!! On Android we will use fragments as our View for all the UI operation :sunglasses:. So let’s see our ProductsFragment implementation:

public class ProductsFragment extends Fragment implements ProductsView {
    private RecyclerView productsRecyclerView;
    private ProductsPresenter productsPresenter;
    private LinearLayoutManager productsLinearLayoutManager;
    private ProgressBar productsProgressBar;
    private ProductsNavigator productsNavigator;

    public static ProductsFragment newInstance(ProductsNavigator productsNavigator) {
        ProductsFragment productsFragment = new ProductsFragment();
        productsFragment.productsNavigator = productsNavigator;
        return productsFragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_products, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        bindViews();
        initializeDependencies();
    }

    @Override
    public void onResume() {
        super.onResume();
        productsPresenter.onStart();
    }

    private void initializeDependencies() {
        ProductsRepository productsRepository = new ProductsRepository();
        productsPresenter = new ProductsPresenter(this, productsRepository);
    }

    private void setupProductRecyclerViewUsing(final Product[] products) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setupLayoutManager();
                setupStyle();
                setupAdapterUsing(products);
            }
        });
    }

    private void setupAdapterUsing(Product[] products) {
        ProductsAdapter productsAdapter = new ProductsAdapter(products, productsPresenter);
        productsRecyclerView.setAdapter(productsAdapter);
    }

    private void setupLayoutManager() {
        productsLinearLayoutManager = new LinearLayoutManager(getActivity());
        productsRecyclerView.setLayoutManager(productsLinearLayoutManager);
    }

    private void setupStyle() {
        productsRecyclerView.setHasFixedSize(true);
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
                productsRecyclerView.getContext(),
                productsLinearLayoutManager.getOrientation());
        productsRecyclerView.addItemDecoration(dividerItemDecoration);
    }

    private void bindViews() {
        productsProgressBar = (ProgressBar) getView().findViewById(R.id.products_progress_bar);
        productsRecyclerView = (RecyclerView) getView().findViewById(R.id.products_recycler_view);
    }

    @Override
    public void showLoadingStatus() {
        productsProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoadingStatus() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                productsProgressBar.setVisibility(View.GONE);
            }
        });
    }

    @Override
    public void show(String title) {
        getActivity().setTitle(title);
    }

    @Override
    public void show(Product[] products) {
        setupProductRecyclerViewUsing(products);
    }

    @Override
    public void showErrorWith(final String message) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                showErrorAlertWith(message);
            }
        });
    }

    private void showErrorAlertWith(String message) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(message)
                .setTitle(R.string.dialog_error_title)
                .setPositiveButton(R.string.dialog_error_ok_button, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                    }
                });
        AlertDialog errorAlert = builder.create();
        errorAlert.show();
    }

    @Override
    public void showDetailFor(Product product) {
        productsNavigator.goToDetailOf(product);
    }
}

As you can see it implements all the UI operation we defined in our view as the UIViewController do on iOS. But if you look well we have a little difference between the two platform. In the Android platform the tap on a product is managed in the adapter, so the presenter is passed to this component to manage the product selection. Let’s see the implementation of our ProductsPresenter:

class ProductsAdapter extends RecyclerView.Adapter<ProductsAdapter.ViewHolder> {
    private Product[] products;
    private ProductsPresenter productsPresenter;

    ProductsAdapter(Product[] products, ProductsPresenter productsPresenter) {
        this.products = products;
        this.productsPresenter = productsPresenter;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View rowProductView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_product, parent, false);
        return new ViewHolder(rowProductView, productsPresenter);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.textView.setText(products[position].getName());
        holder.product = products[position];
    }

    @Override
    public int getItemCount() {
        return products.length;
    }

    static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ProductsPresenter productsPresenter;
        TextView textView;
        Product product;

        ViewHolder(View itemView, ProductsPresenter productsPresenter) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.product_name_text_view);
            textView.setOnClickListener(this);
            this.productsPresenter = productsPresenter;
        }

        @Override
        public void onClick(View v) {
            productsPresenter.onSelected(product);
        }
    }
}

There’s also another little difference between the Android and the iOS version. If you look carefully in the showDetailFor(Product product) method, that is called after the product selected is checked in the presenter, there is another component responsible to manage the navigation between the screen of our app. In particular, the screen of our app will be Fragment objects and there will be an Activity, the ProductsActivity, that will implement the ProductsNavigator interface and will be responsible to manage the correct navigation between fragments in the app. Let’s see in order the ProductsNavigator interface:

public interface ProductsNavigator {
    void goToDetailOf(Product product);
}

And the ProductsActivity implementation:

public class ProductsActivity extends AppCompatActivity implements ProductsNavigator {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_products);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        showFragment(ProductsFragment.newInstance(this));
    }

    private void showFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.setCustomAnimations(
                R.animator.enter,
                R.animator.exit,
                R.animator.pop_enter,
                R.animator.pop_exit
        );
        fragmentTransaction.replace(R.id.products_activity_content, fragment);
        fragmentTransaction.addToBackStack("ProductsStack");
        fragmentTransaction.commit();
    }

    @Override
    public void goToDetailOf(Product product) {
        showFragment(ProductDetailFragment.newInstance(product));
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                break;
        }
        return false;
    }

    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 1) {
            getFragmentManager().popBackStack();
        } else {
            finish();
        }
    }
}

Ok, now we are ready to implement our product detail. First of all we define the new ProductDetailView:

public interface ProductDetailView {
    void show(String title);
    void show(Product product);
    void showErrorWith(String message);
}

And then our presenter:

public class ProductDetailPresenter {
    private ProductDetailView productDetailView;
    private Product product;

    public ProductDetailPresenter(ProductDetailView productDetailView, Product product) {
        this.productDetailView = productDetailView;
        this.product = product;
    }

    public void onStart() {
        productDetailView.show("Product");
        if (isValidProduct()) {
            productDetailView.show(product);
        } else {
            productDetailView.showErrorWith("Product not valid");
        }
    }

    private boolean isValidProduct() {
        return product != null;
    }
}

And its unit tests:

public class ProductDetailPresenterTest {
    private ProductDetailPresenter productDetailPresenter;
    private ProductDetailView productDetailView;
    private Product product;

    @Test
    public void onStartWithValidProduct() throws Exception {
        givenAValidProduct();
        givenAProductDetailView();
        givenAProductDetailPresenter();
        whenThePresenterStarts();
        thenTheTitleIsShown();
        thenTheProductDetailIsShown();
    }

    @Test
    public void onStartWithInvalidProduct() throws Exception {
        givenAnInvalidProduct();
        givenAProductDetailView();
        givenAProductDetailPresenter();
        whenThePresenterStarts();
        thenTheTitleIsShown();
        thenAnErrorMessageIsDisplayed();
    }

    private void givenAValidProduct() {
        product = new Product("aName", "aDescription", "anImage");
    }

    private void givenAnInvalidProduct() {
        product = null;
    }

    private void givenAProductDetailView() {
        productDetailView = mock(ProductDetailView.class);
    }

    private void givenAProductDetailPresenter() {
        productDetailPresenter = new ProductDetailPresenter(productDetailView, product);
    }

    private void whenThePresenterStarts() {
        productDetailPresenter.onStart();
    }

    private void thenTheProductDetailIsShown() {
        verify(productDetailView).show(product);
    }

    private void thenTheTitleIsShown() {
        verify(productDetailView).show(anyString());
    }

    private void thenAnErrorMessageIsDisplayed() {
        verify(productDetailView).showErrorWith(anyString());
    }
}

And finally our ProductDetailFragment:

public class ProductDetailFragment extends Fragment implements ProductDetailView {
    private Product product;
    private ImageView productImageView;
    private TextView productNameTextView;
    private TextView productDescriptionTextView;
    private ProductDetailPresenter productDetailPresenter;

    public static ProductDetailFragment newInstance(Product product) {
        ProductDetailFragment productDetailFragment = new ProductDetailFragment();
        productDetailFragment.product = product;
        return productDetailFragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_product_detail, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        bindViews();
        initializeDependencies();
    }

    @Override
    public void onResume() {
        super.onResume();
        productDetailPresenter.onStart();
    }

    private void bindViews() {
        productImageView = (ImageView) getActivity().findViewById(R.id.product_image_detail_image_view);
        productNameTextView = (TextView) getActivity().findViewById(R.id.product_name_detail_text_view);
        productDescriptionTextView = (TextView) getActivity().findViewById(R.id.product_description_detail_text_view);
    }

    private void initializeDependencies() {
        productDetailPresenter = new ProductDetailPresenter(this, product);
    }

    @Override
    public void show(String title) {
        getActivity().setTitle(title);
    }

    @Override
    public void show(Product product) {
        productImageView.setImageDrawable(getActivity().getDrawable(getActivity().getResources().getIdentifier(
                product.getImage(),
                "drawable",
                getActivity().getPackageName()
        )));
        productNameTextView.setText(product.getName());
        productDescriptionTextView.setText(product.getDescription());
    }

    @Override
    public void showErrorWith(String message) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(message)
                .setTitle(R.string.dialog_error_title)
                .setPositiveButton(R.string.dialog_error_ok_button, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                    }
                });
        AlertDialog errorAlert = builder.create();
        errorAlert.show();
    }
}

That’s it!!!!! You’ve made it!!! Now you master the Model View Presenter architectural pattern on Android and you’re ready to rock with unit test on all the main mobile platform on the market (somebody said Windows Phone!?!?!?!?!? :stuck_out_tongue_closed_eyes:).

Model view presenter platforms

Clean Code: meaningful names

In this post I will talk about clean code and how important are the name you choose while you’re developing software.


I always cared about the software I developed. I also always though that what makes a real professional software developer is how much he/she care about the software he/she is creating: the approach to the problem, the attention to details in code, the passion put into creating every single line of code, the focus on the mission to solve his/her customer problems with your software.
When I started to work at lastminute.com group I discovered that I’m not the only one to think about software development in this way.
In fact an entire new approach to software development has been create by Robert Cecil Martin, known as “Uncle Bob” (whatt??!?!!? :stuck_out_tongue:) and famous also to be the guy that invented the SOLID principles. The name of this new approach to software development is clean code.
So as I promised in my first post on this blog I will publish a series of articles in which I will talk about the foundation of clean code and my personal experiences with it. So let’s start the discussion with one of the principles of clean code that impressed me the most: meaningful naming.

Uncle bob meaningful names

As stated by Uncle Bob in the beautiful meme above, this principle is simple: choose your name carefully. But what does it really mean to “choose carefully”? :confused:. Choosing the right names means the following thing:

  • Names should releal intent. Names should answer why a variable, a class or a method exists, what it does and how it must be used.
  • Avoid disinformation. Names should not contains false clues about the scope of a variable, method or class. Taking an example from Uncle Bob’s clean code: “Do not refer to a grouping of accounts as an accountList, unless it is actually a list…So accountGroups or just plain accounts would be better”.
  • Don’t use noise words. Adding in the name something like “Info” , “Data” doesn’t give you any value.
  • Use pronounceable names. This is simple: use names that humans can pronounce without feeling stupid.
  • Use searchable names. Avoid single-letter names and numeric constants that make your components harder to find.
  • Avoid encodings. Avoid unnecessary encoding notation, like for example Hungarian Notation in which the name of a variable or function indicates its type.
  • Avoid member prefix. Don’t add unnecessary name prefix.
  • Avoid encoding interface and implementations. Avoid encoding interface with “I” and concrete implementation with “Impl”. If you really need to encode, do it for the implementation.
  • Use noun or noun phrase for classes.
  • Use verb or verb phrase for methods.
  • One word per concept. Choose a word for a concept and always use it each time you have to refer to it in code.
  • Use solution domain names. Your code will be read by other programmers. Math names, algorithm names, pattern names are all good choices.
  • Use problem domain names. If no “programmer oriented name” exists, go with names taken from the problem domain.

Let’s see an example to understand the real value of naming classes, methods, functions and variable in the right way. For example, take a look at the following C++ code:

struct pt {
    float x;
    float y;
    float z;
};

struct mt {
public:
    float m[4][4];
};

class Obj {
public:
    bool act;
    mt matr;
    std::vector<pt> ms;
};

class GameManager {
public:
    
    GameManager(std::vector<Obj> anObjList){
        objList = anObjList;
    }
    
    std::vector<Obj> get() {
        std::vector<Obj> newObjList;
        for (auto currObj : newObjList) {
            if (currObj.act && currObj.ms.size() > 0) {
                newObjList.push_back(currObj);
            }
        }
        return newObjList;
    }
    
private:
    
    std::vector<Obj> objList;
};

Even if you can maybe get a feeling of what is going, it’s hard to really understand all the details of this code, and every class, struct is supposed to do and to represent. What are pt and mt???? What is supposed to represent a GameManager and an Obj? And the method get, what wants to get????? :cold_sweat:. You can see that a lot of things are a little bit obscure in this code.
We can try to refactor it following the names guidelines we exposed above. Do you think the code will improve in this way? You can judge it by yourself:

struct Point {
    float x;
    float y;
    float z;
};

struct Matrix {
public:
    float values[4][4];
};

class GameObject {
public:
    bool isActive;
    Matrix transformation;
    std::vector<Point> mesh;
};

class Scene {
public:
    
    Scene(std::vector<GameObject> newGameObjects){
        gameObjects = newGameObjects;
    }
    
    std::vector<GameObject> getValidGameObjects() {
        std::vector<GameObject> activeGameObjects;
        for (auto gameObject : gameObjects) {
            if (isValid(gameObject)) {
                activeGameObjects.push_back(gameObject);
            }
        }
        return activeGameObjects;
    }
    
private:
    
    std::vector<GameObject> gameObjects;
    
    bool isValid(GameObject gameObject) {
        return gameObject.isActive && isValid(gameObject.mesh);
    }
    
    bool isValid(std::vector<Point> mesh) {
        return mesh.size() > 0;
    }
};

Whoaaah!! I mean, the code now seems self explained. Each instruction appears more clear in its intents. Each class, struct and method doesn’t need any additional comment/documentation. I think it’s really amazing how some changes to the names could improve the quality of a piece of code dramatically like in our case. We’re done with names :relieved:. You can find the complete example here I hope that this article will convince you that the names you choose define how much you code is “beautiful” in terms of readability and also, more import, maintainability. Quoting what Grady Booch said about clean code:

Clean Code reads like a well-written prose.

Choosing the right names is the first step to make your code more similar to a well-written prose.

How to calculate the reflection vector

In this post I will talk about reflection vector used in some lighting models, for example the Phong reflection model. I will show you how this vector is calculated.


In a previous post I talked about the Phong lighting model. I described all the different components contained inside it and how they try to describe and simulate different kind of light components. In particular you will remember that there’s a specular component that try to simulated the light reflected in a specific direction: the reflection direction. In the shader implementation this direction has been calculated using a GLSL method reflect:

vec3 reflectionDirection = reflect(-lightDirection, normalInterp);

Easy, isn’t it? But the most curios of you may asking: “How the f*$k this method calculate this reflection direction?” :stuck_out_tongue_closed_eyes:
We will suppose as in the previous post about the phong model that all vectors are normalized. Let’s start from the beginning. The formula to calculate the reflection direction is:

How is this formula obtained? Let’s start from a picture that represents our reflection vector and the other vectors used in the calculation.

Model view presenter ios unit tests

Before we start with the demonstration we also need to know what is the law of reflection:

The incident light ray L, the reflected ray R, and the normal N to the surface of the mirror all lie in the same plane. The angle of reflection $\Theta_R$ is equal to the angle of incidence of light $\Theta_L$. Both angles are measured with respect to the normal to the mirror. The reflected ray and the incident ray are on the opposite sides of the normal.

Now we are ready for our demonstration :sunglasses:.
From the law of refraction reported above we know that:

This equation could be rewritten as the dot product of the reflection direction with the normal equals the dot product of the incident light direction and the normal (remember that the dot product of two vector is equal to the cosine of the angle between them). So we have:

From the image above it’s also evident for symmetry that:

As you can see again from the image above, this two vectors could be easily calculated. In fact the first one is the difference between the reflection vector and the projection of it on the normal. The second one is the difference between the light incident vector and the projection of it on the normal. So for the reflection side we could write:

For the light side we could write:

As a consequence we obtain the following equation:

Now we can see again from the image above that the vector projections ${\hat {N}^{\prime}}$ and ${\hat {N}^{\prime \prime}}$ are equals, that means we could change the previous equation by substituting the first one with the second one. So we obtain the following equation:

Now we have all we need to calculate our R vector:

That’s it!! We get our formula. Now you’re ready to explain in detail the entire “magic” of the Phong model :relaxed:.

Model View Presenter on iOS: no more excuses, write your unit test

In this post I will talk about the model view presenter architectural pattern and how it can improve you unit test and your TDD workflow when developing an iOS application.


Unit test in iOS application is in some way “hard”. The architectural pattern implemented by default on iOS is the Model View Controller. This architecture provides you a clear separation between the UI and the business logic. The problem is that most of the time you have to fight with “Massive View Controllers” that act as glue between the model and a lot of UI/view code and that, for this reason, are not so easy to test. This basically means that most of the time the presentation logic, how the business model is displayed to the user in the User Interface, is tested in the wrong way, or maybe worst it is not tested.
This is were the Model View Presenter could save us. In this architectural pattern all the presentation logic is moved from the view controller to a new component, the presenter. This means that it will manage model objects to retrieve the data and then prepare it to be displayed by the view, that will be our View Controller. This one becomes a passive interface that has the only responsibility to show data returned by the presenter in the specific platform User Interface component. In this way the presenter is a component without any platform specific dependency, that works only on the view and other model objects, injected at construction time. In this way all dependencies could be mocked and you can unit test basically everything!! :relaxed:
Now it’s time to see the Model View Presenter in action!! :grin: We will create a simple app that shows a list on products in a UITableView. On the tap of a cell the product detail is shown. An error will be displayed if an error occurs when the products are retrieved or when it doesn’t contain all the data needed to show its detail. We will develop this app using Test Driven Development technique, and I will show the unit tests created for each class implemented. These unit tests will also be written using the “Given-then-when” structure, typically used in Behaviour Driven Development. Even if not related to this article, I like this way of writing unit tests because they are more expressive, so I will use it in all my code. Below you can find a mockup of what we want to achieve.

Model view presenter mockup

Let’s start by creating a Product struct that we will use to describe our products. Each product will be composed of a name, a description and an image (identified by its name):

public struct Product {
    let name: String
    let description: String
    let image: String
    
    public init(name: String, description: String, image: String) {
        self.name = name
        self.description = description
        self.image = image
    }
}

The products objects will be retrieved using a Repository. First of all we need to define a Repository protocol. Generally speaking, we will try to define a protocol for all our classes so that we can work using abstraction (and not concrete implementation) to obtain the highest decoupling between our classes. Last but not least (and maybe the most important thing) by using protocols we will be able to produce some mocks/spies of our components in our unit tests.

public protocol Repository {
    func get(finish: @escaping ([Product]?) -> Void)
}

In our case the repository will not retrieve the data from a real datasource or using a service. All data will be retrieved from a local array inside the repository. This is the final implementation of our repository:

public class ProductsRepository: Repository {
    
    public func get(finish: @escaping ([Product]?) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(3000)) {
            let products = [
                Product(name: "Car", description: "A beautiful car", image: "car"),
                Product(name: "Book", description: "", image: "book")
            ]
            finish(products)
        }
    }
}

Even if out of our scope, below you can find the unit tests for our ProductsRepository. They are interesting because you can find an example of unit test with expectation (another thing to be discussed sooner or later :stuck_out_tongue_winking_eye:).

class ProductsRepositoryTests: XCTestCase {
    private var productsRepository: ProductsRepository!
    
    func testProductsRetrieved() {
        givenAProductsRepository()
        whenTheRepositoryTryToRetrieveProducts { [unowned self] products, repositoryExpectation in
            self.thenTheProductsListIsRetrieved(products: products,
                                                expectation: repositoryExpectation)
        }
        thenTheRepositoryFinishedToTryToRetrieve()
    }
    
    private func givenAProductsRepository() {
        productsRepository = ProductsRepository()
    }
    
    func whenTheRepositoryTryToRetrieveProducts(finish: @escaping ([Product]?, XCTestExpectation) -> Void) {
        let repositoryExpectation = expectation(description: "RepositoryExpectation")
        productsRepository.get { products in
            finish(products, repositoryExpectation)
        }
    }
    
    private func thenTheProductsListIsRetrieved(products: [Product]?, expectation: XCTestExpectation) {
        XCTAssertNotNil(products)
        XCTAssertTrue(products!.count > 0)
        expectation.fulfill()
    }
    
    private func thenTheRepositoryFinishedToTryToRetrieve() {
        waitForExpectations(timeout: 10) { error in
            if let error = error {
                XCTFail("Repository finish() not called: \(error)")
            }
        }
    }
}

Now it’s time to develop our ProductsPresenter presenter. It will need a view to which it will delegate the real user interface operation and the Repository to retrieve the products data. It will be responsible to manage:

  • the start of the view in the onStart() method. In this method we will have to update the view to show a loading status (that will be a UIActivityIndicator in the view implementation), then try to retrieve the products from the repository and show them on the view if everything goes fine. If something goes wrong show on the view an error message. In any case, it also need to hide the loading status after the retrieve operation has been completed.
  • the show detail action in the onSelected(product: Product) method. In this method we will have to check if all the product data is correct, that in our case means that the product must have a valid product. If it is valid, show its detail in the view, else an error message.

We start by defining the protocol ProductView, that contains all the valid operation of the view that our presenter will use:

public protocol ProductsView {
    func showLoadingStatus()
    func hideLoadingStatus()
    func show(title aTitle: String)
    func show(products: [Product])
    func showErrorWith(message: String)
    func showDetailFor(product: Product)
}

Now we are ready to proceed with the ProductsPresenter implementation:

public class ProductsPresenter {
    private let productsView: ProductsView
    private let productsRepository: Repository
    
    public init(productsView: ProductsView, productsRepository: Repository) {
        self.productsView = productsView
        self.productsRepository = productsRepository
    }
    
    public func onStart() {
        productsView.show(title: "Products")
        productsView.showLoadingStatus()
        productsRepository.get { [unowned self] retrievedProducts in
            self.tryToShow(retrievedProducts: retrievedProducts)
            self.productsView.hideLoadingStatus()
        }
    }
    
    private func tryToShow(retrievedProducts: [Product]?) {
        if let products = retrievedProducts {
            productsView.show(products: products)
        } else {
            productsView.showErrorWith(message: "No products available")
        }
    }
    
    public func onSelected(product: Product) {
        if product.description != "" {
            productsView.showDetailFor(product: product)
        } else {
            productsView.showErrorWith(message: "Product without description")
        }
    }
}

Develop a class like this one in TDD it’s easy, given the fact that we can mock every dependecies it has and we can test in detail all the presentation flow. The unit test of our presenter are shown below. You can note that a lot of handcraft made mock objects are used but not reported here (you will find them in the complete project on Github reported at the end of this article).

class ProductsPresenterTests: XCTestCase {
    private var productsRepositoryWithProducts: ProductsRepositoryWithProductsSpy!
    private var productsRepositoryWithoutProducts: ProductsRepositoryWithoutProductsSpy!
    private var productsView: ProductsViewSpy!
    private var productPresenter: ProductsPresenter!

    func testOnStartWithProducts() {
        givenAProductsRepositoryWithProducts()
        givenAProductsView()
        givenAProductsPresenterWith(repository: productsRepositoryWithProducts)
        whenTheProductsPresenterStarts()
        thenTheTitleIsDisplayed()
        thenTheProductViewShowsLoadingStatus()
        thenTryToRetrieveProduct()
        thenTheProductViewHidesLoadingStatus()
        thenTheProductsViewShowsTheProducts()
    }
    
    func testOnStartWithoutProducts() {
        givenAProductsRepositoryWithoutProducts()
        givenAProductsView()
        givenAProductsPresenterWith(repository: productsRepositoryWithoutProducts)
        whenTheProductsPresenterStarts()
        thenTheTitleIsDisplayed()
        thenTheProductViewShowsLoadingStatus()
        thenTryToRetrieveProductFromEmptyRepository()
        thenTheProductViewHidesLoadingStatus()
        thenTheProductsViewShowsAnErrorMessage()
    }
    
    func testOnProductWithDescriptionSelected() {
        givenAProductsRepositoryWithProducts()
        givenAProductsView()
        givenAProductsPresenterWith(repository: productsRepositoryWithProducts)
        whenAProductWithDescriptionIsSelected()
        thenTheProductDetailIsShown()
    }
    
    func testOnProductWithoutDescriptionSelected() {
        givenAProductsRepositoryWithProducts()
        givenAProductsView()
        givenAProductsPresenterWith(repository: productsRepositoryWithProducts)
        whenAProductWithoutDescriptionIsSelected()
        thenTheProductsViewShowsAnErrorMessage()
    }
    
    private func givenAProductsRepositoryWithProducts() {
        productsRepositoryWithProducts = ProductsRepositoryWithProductsSpy()
    }
    
    private func givenAProductsRepositoryWithoutProducts() {
        productsRepositoryWithoutProducts = ProductsRepositoryWithoutProductsSpy()
    }
    
    private func givenAProductsView() {
        productsView = ProductsViewSpy()
    }
    
    private func givenAProductsPresenterWith(repository: Repository) {
        productPresenter = ProductsPresenter(productsView: productsView, productsRepository: repository)
    }
    
    private func whenTheProductsPresenterStarts() {
        productPresenter.onStart()
    }
    
    func whenAProductWithDescriptionIsSelected() {
        productPresenter.onSelected(product: Product(name: "Car",
                                                     description: "A beautiful car",
                                                     image: "car"))
    }
    
    func whenAProductWithoutDescriptionIsSelected() {
        productPresenter.onSelected(product: Product(name: "Car",
                                                     description: "",
                                                     image: "car"))
    }
    
    private func thenTheTitleIsDisplayed() {
        XCTAssertTrue(productsView.showTitleHasBeenCalled)
    }
    
    private func thenTryToRetrieveProduct() {
        XCTAssertTrue(productsRepositoryWithProducts.getHasBeenCalled)
    }
    
    private func thenTheProductViewShowsLoadingStatus() {
        XCTAssertTrue(productsView.showLoadingStatusHasBeenCalled)
    }

    private func thenTheProductViewHidesLoadingStatus() {
        XCTAssertTrue(productsView.hideLoadingStatusHasBeenCalled)
    }

    private func thenTheProductsViewShowsTheProducts() {
        XCTAssertTrue(productsView.showProductsHasBeenCalled)
    }
    
    private func thenTryToRetrieveProductFromEmptyRepository() {
        XCTAssertTrue(productsRepositoryWithoutProducts.getHasBeenCalled)
    }
    
    private func thenTheProductsViewShowsAnErrorMessage() {
        XCTAssertTrue(productsView.showsErrorMessageHasBeenCalled)
    }
    
    private func thenTheProductDetailIsShown() {
        XCTAssertTrue(productsView.showDetailForProductHasBeenCalled)
    }
}

It easy to see that the unit tests for our presenter describe the entire presentation flow. This basically means that our unit tests are the documentation of our presentation logic. Cooool!!!! :sunglasses: Now the next big question is: who is going to implement our ProductsView protocol? As we said in the introduction, our view controllers become the View in the Model View Presenter architecture. They act as passive platform specific user interface components updater. This means that our protocol will be implemented by ProductsViewController. It have the responsibility to launch the ProductsPresenter action at the right time and implement all the real passive User Interface update operation. In particular we will have our onStart() presenter method call in the viewDidLoad and the onSelected(product: Product) when a product cell is tapped, that means a product has been selected. The final implementation will be:

class ProductsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, ProductsView {
    @IBOutlet weak var productsTableView: UITableView!
    @IBOutlet weak var productsLoadingView: UIView!
    @IBOutlet weak var productsActivityIndicator: UIActivityIndicatorView!
    private var productsList: [Product]!
    private var productSelected: Product!
    private var productsRepository: ProductsRepository!
    private var productsPresenter: ProductsPresenter!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initializeDependencies()
        productsPresenter.onStart()
    }
    
    private func initializeDependencies() {
        productsList = []
        productsRepository = ProductsRepository()
        productsPresenter = ProductsPresenter(productsView: self, productsRepository: productsRepository)
    }
    
    //MARK: ProductsView
   
    public func show(title aTitle: String) {
        title = aTitle
    }

    public func showLoadingStatus() {
        productsActivityIndicator.startAnimating()
        productsLoadingView.isHidden = false
    }
    
    public func hideLoadingStatus() {
        productsActivityIndicator.stopAnimating()
        productsLoadingView.isHidden = true
    }
    
    public func show(products: [Product]) {
        productsList = products
        productsTableView.reloadData()
    }
    
    public func showErrorWith(message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { [unowned self] action in
            self.dismiss(animated: true, completion: nil)
        }))
        present(alert, animated: true, completion: nil)
    }
    
    public func showDetailFor(product: Product) {
        productSelected = product
        performSegue(withIdentifier: "ShowProductDetail", sender: nil)
    }
    
    //MARK: Segue
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let productDetailViewController = segue.destination as? ProductDetailViewController {
            productDetailViewController.product = productSelected
        }
    }
    
    //MARK: UITableView Datasource
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return productsList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell", for: indexPath)
        cell.textLabel?.text = productsList[indexPath.row].name
        return cell
    }
    
    //MARK: UITableView Delegate
    
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        productsPresenter.onSelected(product: productsList[indexPath.row])
    }
}

You don’t need unit tests for the controller: the presenter unit tests assure that our presentation logic is the one expected. The view controller is only updating iOS specific User Interface components (something we hope Apple tested for us :smirk:). In the same way we developed this components, we can go on and implement our product detail by defining first of all a ProductDetailView:

public protocol ProductDetailView {
    func show(title aTitle: String)
    func show(product: Product)
    func showErrorWith(message: String)
}

Then our ProductDetailPresenter presenter, that will be responsible to check that a valid product to show has been received:

public class ProductDetailPresenter {
    private let productDetailView: ProductDetailView
    private let product: Product?

    public init(productDetailView: ProductDetailView, product: Product?) {
        self.productDetailView = productDetailView
        self.product = product
    }

    public func onStart() {
        productDetailView.show(title: "Product")
        if let product = product {
            productDetailView.show(product: product)
        } else {
            productDetailView.showErrorWith(message: "Product not valid")
        }
    }
}

Its unit tests will be:

class ProductDetailPresenterTests: XCTestCase {
    private var productDetailPresenter: ProductDetailPresenter!
    private var productDetailView: ProductDetailViewSpy!
    
    func testShowDetailOfAProduct() {
        givenAProductDetailView()
        givenAProductDetailPresenterWith(product: Product(name: "aProduct",
                                                          description: "aDescription",
                                                          image: "image"))
        whenThePresenterStarts()
        thenTheTitleIsDisplayed()
        thenTheProductDetailIsShown()
    }
    
    func testShowDetailOfAnInvalidProduct() {
        givenAProductDetailView()
        givenAProductDetailPresenterWith(product: nil)
        whenThePresenterStarts()
        thenTheTitleIsDisplayed()        
        thenAnErrorMessageIsDisplayed()
    }
    
    private func givenAProductDetailView() {
        productDetailView = ProductDetailViewSpy()
    }
    
    private func givenAProductDetailPresenterWith(product: Product?) {
        productDetailPresenter = ProductDetailPresenter(productDetailView: productDetailView, product: product)
    }
    
    private func whenThePresenterStarts() {
        productDetailPresenter.onStart()
    }
    
    private func thenTheTitleIsDisplayed() {
        XCTAssertTrue(productDetailView.showTitleHasBeenCalled)
    }
    
    private func thenTheProductDetailIsShown() {
        XCTAssertTrue(productDetailView.showProductHasBeenCalled)
    }
    
    private func thenAnErrorMessageIsDisplayed() {
        XCTAssertTrue(productDetailView.showErrorHasBeenCalled)
    }
}

....

class ProductDetailViewSpy: ProductDetailView {
    private(set) var showTitleHasBeenCalled: Bool = false
    private(set) var showProductHasBeenCalled: Bool = false
    private(set) var showErrorHasBeenCalled: Bool = false
    
    func show(title aTitle: String) {
        showTitleHasBeenCalled = true
    }
    
    func show(product: Product) {
        showProductHasBeenCalled = true
    }
    
    func showErrorWith(message: String) {
        showErrorHasBeenCalled = true
    }
}

Finally our ProductDetailViewController that is the view controller for this app section:

class ProductDetailViewController: UIViewController, ProductDetailView {
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    var product: Product!
    private var productDetailPresenter: ProductDetailPresenter!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initializeDependencies()
        productDetailPresenter.onStart()
    }
    
    private func initializeDependencies() {
        productDetailPresenter = ProductDetailPresenter(productDetailView: self, product: product)
    }
    
    //MARK: ProductDetailView
    
    public func show(title aTitle: String) {
        title = aTitle
    }
    
    public func show(product: Product) {
        nameLabel.text = product.name
        descriptionLabel.text = product.description
        imageView.image = UIImage(named: product.image)
    }
    
    public func showErrorWith(message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { [unowned self] action in
            self.dismiss(animated: true, completion: nil)
        }))
        present(alert, animated: true, completion: nil)
    }
}

Yeaaaahh you made it!! You’re at the end of this never ending post :satisfied:!!
Now you can start to create your high quality unit tested apps :relieved:.

Model view presenter ios unit tests

Time to try it yourself in one of your project. If you wanna review the complete project code you can check this Github repository.

Phong reflection model

In this post I will talk about phong reflection model: one of the oldest and most popular lighting model in computer graphics.


We live in the era of Physically Based Rendering (PBR). Everyone in the computer graphics field know it is the future. I will talk a lot about it in some future post. But now, for this first post about computer graphics theory, I would like to talk about one of the most popular lighting model used in computer graphics: the Phong reflection post.
Why? Because even if PBR is winning the war (I know you’re scared about PBR theoretical foundation :fearful: :stuck_out_tongue:), the Phong reflection model is still used in a lot of application, and it is also a good starting point for everyone who want to start a journey in the computer graphics field. In this post I will show you what it is in detail and we will see an implementation in OpenGL ES GLSL shading language. All the vectors reported in this article will be normalized. First of all we need to do a classification of the lighting models available in computer graphics. The light at any give point on a surface in a scene is influenced by:

  • Direct illumination: light directly arriving on the surface
  • Indirect illumination: light arriving on the surface after having bounced off other surfaces

As a consequence of this fact lighting methods can be:

  • Local models: only the direct illumination is considered
  • Global models: both direct and indirect illuminations are considered

The Phong reflection model is an empirical (so based on observations) local illumination model. Let’s see how we is it composed. In real world light is modeled by its spectrum, and each surface by its reflectance spectrum. In computer graphics often an approximation based on RGB triplets is used:

  • Light color is a RGB triplet
  • The reflectance spectrum is a triple of percentage

So each component of the following equations will be described by a triplet of number (RGB or percentage).
The light that you see on a surface could be decomposed into four main components. Let’s see them in detail and how they are composed together to calculate the illumination of a surface point: Phong model.

Emissive component

This is the light component emitted by the surface material. Is a purely additive component. As you can image, few surface material in nature are emissive (e.g.: light :laughing:). So the emissive illumination $I_{\text{emissive}}$ of a surface point is obtained by multiplying the emissive constant of a surface $k_{\text{e}}$ by the light emissive intensity $I_{\text{LE}}$ (expressed as we said before as a RGB triplet, and this will be valid for all light intensity define in the following formulas of this post). The emissive light intensity could be the intensity of the scene light (or an average if you have multiple scene lights): pratically speaking you can use the scene light RGB color. The emissive constant $k_{\text{e}}$ is a surface property that express its emissive reflectance.

Ambient component

This is light component used to model empirically the contribution of indirect illumination of bouncing lights in the scene on a surface. It depends entirely on the surface material (no geometry influences). So the ambient illumination $I_{\text{ambient}}$ of a surface point is obtained by multiplying the ambient constant of a surface $k_{\text{a}}$ with the light ambient intensity $I_{\text{LA}}$. The ambient constant $k_{\text{a}}$ is a constant related exclusively to the surface material that express empirically its response to indirect illumination. As for the emissive component, the ambient light intensity could be the intensity of the scene light (or an average if you have multiple scene lights).

Diffusive component

This light component represents the amount of reflected light given an incident light. Lambertian surfaces are considered: The incident light is equally reflected (with a given amount) in all directions. The amount of reflected light depends on the incident angle of the light with respect to the surface’s normal. The diffuse illumination $I_{\text{diffuse}}$ of a surface point is obtained by multiplying the diffuse surface constant $k_{\text{d}}$ with the light diffuse intensity $I_{\text{LD}}$ and with the attenuation factor due to incident light given by the cosine of the angle between the light direction and the surface normal $\cos\theta$. This last value is the dot product between the surface normal ${\hat {N}}$ and the light direction ${\hat {L}}$. So the final formula for the diffuse component is:

Specular component

This light componet represents the amount of reflected light in a specific direction (mirror alike reflection). Light is reflected in a different way depending on the incident light direction. Shiny materials are the one with a high specular component. The perceived specular light depends on the position of the observer with respect to the surface. In particular, the specular illumination is influenced by $\cos\alpha$, that is the cosine of the angle between the direction from the surface point towards the view ${\hat {V}}$ and the direction that a perfectly reflected ray of light would take from this point on the surface ${\hat {R}}$. The size of the specular highlights is regulated by a shininess constant , based on the surface material properties. Given all this information the specular component formula obtained by multiplying the specular surface constant $k_{\text{s}}$ with the light specular intensity $I_{\text{LS}}$ and with dot product of the reflection direction ${\hat {R}}$ and the the direction from the surface point towards the view ${\hat {V}}$ squared to the shininess constant :

The above observation are valid also in case we have multiple lights. The only difference is that the diffuse and specular component are calculated for each light and their sum is the final diffuse and specular component. Now we are ready to write the complete Phong reflection lighting equation:

Just a final note: we distinguished different type of light intensity based on the component. In fact most of the time this model is implemented using a single general light intensity triplet for all the component for each light. How can you implement it in a OpenGL ES shader? The following code sample is a simple implementation of this model using RGB colors. It is a fragment shader that could be used to implement per fragment lighting. This basically means that all lighting calculation is done in the fragment shader on each fragment (maybe this is material for a new post :stuck_out_tongue_closed_eyes:). It was written using OpenGL ES 3.0 and GLSL 3.0. It uses a single light for all the component calculation.

The following image is an example of the happy buddha Stanford mesh rendered using my Spectral BRDF explorer iOS renderer. The lighting is (obviously) calculated using the Phong reflection model. The surface simulated is bronze (you can find some of the constant we discussed before here). Nice :smirk:!!!!

phong example - spectral brdf explorer