There are bits of the C# language that we don't think about too much when writing websites – and implicit conversions are one of those things. But while I've been messing about with some ray-tracing code in evenings recently, I found a couple of examples they patterns they can be a help with...
						
						url copied!
					So how can I write the code so that it the internals work in the "right" angle units for their work, but I have the option to define things as degrees or as radians to suit my thinking?
						
						url copied!
					public void CalculateSomeAngles(double radians)
{
    ...
}
var radians = Math.ToRadians(45);
CalculateSomeAngles(radians);
					
					That's fine – but it's not really very type safe. Because the method takes a double, you can supply anything. And there's not really anything stopping you accidentally supplying degrees instead of radians.
So what else could you do?
						
						url copied!
					public struct Radians
{
    public double Value { get; private set; }
	
    public Radians(double value)
    {
        Value = value;
    }
}
public void CalculateSomeAngles(Radians radians)
{
    ...
}
CalculateSomeAngles(new Radians(1.1));
					
					With a wrapper type to be explicit about the units, it's harder to make a mistake. And we can also use this pattern to allow you to pass whatever units you prefer. We can define a struct for degrees too:
public struct Degrees
{
    public double Value { get; private set; }
	
    public Degrees(double value)
    {
        Value = value;
    }
}
					
					But the magic here is adding some operators to allow the compiler to automatically do some conversions for us. If we add a "to degrees" conversion to the radians struct and the equivalent degrees struct:
public struct Degrees
{
    public static implicit operator Radians(Degrees d)
    {
        return new Radians(d.Value * Math.PI / 180);
    }
}
public struct Radians
{
    public static implicit operator Degrees(Radians r)
    {
        return new Degrees(r.Value * 180 / Math.PI);
    }
}
					
					Then the compiler no longer cares which you pass – it will just convert to the one it needs in any situation...
So you can write:
CalculateSomeAngles(new Radians(1.1)); CalculateSomeAngles(new Degrees(72));
And now I don't have to worry about my units.
						
						url copied!
					The pattern I ended up with for animation was that it needed a mechanism to set which frame is being rendered, and a mechanism to get the value for this point. Something like:
public interface IAnimateable<T>
{
    T Value { get; }
    void SetFrame(int thisFrame, int frameCount);
}
					
					So if we have a material with "shinyness" that can be amimated, we can replace the normal
						double-typed property with something which exposes this interface, and when the material gets initialised for a specific frame we just pass that data down to the animateable properties:
public class PhongMaterial
{
    ....
    public IAnimateable<double> Shinyness { get; init ; }
    public void SetFrame(int thisFrame, int frameCount)
    {
        ....
        Shinyness.SetFrame(thisFrame, frameCount);
        ....
    }
    ....
}
					
					And we can have a whatever implementations of this animatable double we like. So to move between 1 and 100, we might declare something like:
var someShape = new Sphere() {
    Material = new PhongMaterial() {
        Shinyness = new AnimatedRange(1, 100)
    }
};
					
					But the side effect of doing this is that when we don't need an animated value we still have to pass an object for that value:
var someShape = new Sphere() {
    Material = new PhongMaterial() {
        Shinyness = new StaticValue(25)
    }
};
					
					But the trick above with implicit conversions can save us some typing here. The definition for the static value can help:
public class StaticValue : IAnimateable<double>
{
    public double Value { get; private set; }
    public StaticValue(double value)
    {
        Value = value;
    }
    public void SetFrame(int thisFrame, int frameCount)
    {
    }
    public static implicit operator StaticValue(double value)
    {
        return new StaticValue(value);
    }
}
					
					And now a value which doesn't animate can be declared as:
var someShape = new Sphere() {
    Material = new PhongMaterial() {
        Shinyness = 25
    }
};
					
					This pattern has ended up very useful for both simple scene properties like above, and for more complex Matrix properties for transformations.
↑ Back to top