Showing posts with label ui. Show all posts
Showing posts with label ui. Show all posts

Sunday, 10 August 2014

Implicit Converters

I already spoke a bit about converting types.

Since in FlareTic all the "dataflow patch types" use some form of generic holder for pins, link is done simply as:

Check if input type is assignable from output type, if yes allow link.
Now this creates some issues as for example float is not assignable to bool.

So I don't want to create AsBool (Float) , AsFloat(Bool) and so on for all types, that makes a patch bloated for nothing, I need another solution.

Enters converters

Code Snippet
  1. public interface ITypeConverterFactory
  2. {
  3.     bool CanConvert(Type from, Type to);
  4.     ITypeConverter CreateConverter();
  5. }
  6.  
  7. public interface ITypeConverter
  8. {
  9.     object Convert(object from);
  10.     bool Warning { get; }
  11.     string WarnMessage { get; }
  12. }

The first interface allows to check if we can convert types, and when we connect creates the converter that does the job.

Now when we check for connection, we have a list of factories, so if we find a converter, we can link :)

For raw .NET primitive types, you have to build by yourself, so to speed this up, few generic builders:

Code Snippet
  1. public class FuncConverter<TFrom, TTo> : ITypeConverter
  2. {
  3.     private readonly Func<TFrom, TTo> convertFunc;
  4.     private readonly bool warning;
  5.     private readonly string message;
  6.  
  7.     public FuncConverter(Func<TFrom, TTo> func, bool warning, string message)
  8.     {
  9.         this.convertFunc = func;
  10.         this.warning = warning;
  11.         this.message = message;
  12.     }
  13.  
  14.     public object Convert(object from)
  15.     {
  16.         return this.convertFunc((TFrom)from);
  17.     }
  18.  
  19.     public bool Warning
  20.     {
  21.         get { return this.warning; }
  22.     }
  23.  
  24.     public string WarnMessage
  25.     {
  26.         get { return this.message; }
  27.     }
  28. }

And the factory :

Code Snippet
  1. public abstract class FuncConverterFactory<TFrom, TTo> : ITypeConverterFactory
  2. {
  3.     private readonly Func<TFrom, TTo> convertFunc;
  4.     private readonly bool warn;
  5.     private readonly string msg;
  6.  
  7.     public FuncConverterFactory(Func<TFrom, TTo> func, bool warning = false, string message = "")
  8.     {
  9.         this.convertFunc = func;
  10.         this.warn = warning;
  11.         this.msg = message;
  12.     }
  13.  
  14.     public bool CanConvert(Type from, Type to)
  15.     {
  16.         return from == typeof(TFrom) && to == typeof(TTo);
  17.     }
  18.  
  19.     public ITypeConverter CreateConverter()
  20.     {
  21.         return new FuncConverter<TFrom, TTo>(this.convertFunc, this.warn, this.msg);
  22.     }
  23. }


Next bit of boilerplate, could have done several ways, but you only do it once ;)

Code Snippet
  1. public class IntToFloatConverter : FuncConverterFactory<int, float>
  2. {
  3.     public IntToFloatConverter() : base(i => i) { }
  4. }
  5.  
  6. public class FloatToIntConverter : FuncConverterFactory<float, int>
  7. {
  8.     public FloatToIntConverter() : base(f => (int)f, true, "Conversion from float to int is explicit, some data can be lost" ) { }
  9. }

You can notice that explicit casting also induce a message, so we can warn user in some cases.

Now for all other types, we go reflect implicit/explicit table for other types:

Code Snippet
  1. public class ImplicitReflectionConverterFactory : ITypeConverterFactory
  2. {
  3.     public bool CanConvert(Type from, Type to)
  4.     {
  5.         var f = from.GetMethods().Where(
  6.             m => m.Name == "op_Implicit"
  7.             && m.ReturnType == to
  8.             && m.GetParameters().Count() == 1
  9.             && m.GetParameters()[0].ParameterType == from).FirstOrDefault() != null;
  10.  
  11.         if (f) { return true; }
  12.  
  13.         f = to.GetMethods().Where(
  14.             m => m.Name == "op_Implicit"
  15.             && m.ReturnType == to
  16.             && m.GetParameters().Count() == 1
  17.             && m.GetParameters()[0].ParameterType == from).FirstOrDefault() != null;
  18.  
  19.         return f;
  20.     }
  21.  
  22.     public ITypeConverter CreateConverter()
  23.     {
  24.         return new ImplicitReflectionConverter();
  25.     }
  26. }

Now let's go a bit forward, why not also being able to auto convert opencv image to dx11 texture, that's why you use a converter factory, since now converter will also hold a resource, so we need one instance per link.

Converter code is just boilerplate, create texture for the same format as opencv image, and copy content in gpu when possible.


Here we go, of course user interface must reflect that we are using a converter, so blue mean converter, yellow means converter with warning.

Now opencv image can be directly linked to any node that wants a texture, and you can notice sharpdx types can be auto translated (Color4 has a Vector3 explicit operator, so it's automatically scanned by the reflector).

I love clean patches ;)

Now one common issue, we might want some form of control over the conversion, for example, float to string, we'd like to specify a format. So let's push the concept forward and make our link configurable via inspector.

Easy then, let's add a small additional interface:

Code Snippet
  1. public interface IConfigurableTypeConverter : ITypeConverter
  2. {
  3.     IEnumerable<IParameter> Parameters { get; }
  4. }

And here we go:

Code Snippet
  1. public class StringFormatConverter : IConfigurableTypeConverter
  2. {
  3.     private StringParameter format;
  4.  
  5.     public StringFormatConverter()
  6.     {
  7.         this.format = new StringParameter(new StringParameterConverter());
  8.         this.format.Value = "f";
  9.         this.format.Name = "Format";
  10.     }
  11.  
  12.     public object Convert(object from)
  13.     {          
  14.        return String.Format("{0:" + format.Value + "}", from);
  15.     }
  16.  
  17.     public bool Warning
  18.     {
  19.         get { return false; }
  20.     }
  21.  
  22.     public string WarnMessage
  23.     {
  24.         get { return ""; }
  25.     }
  26.  
  27.     public IEnumerable<IParameter> Parameters
  28.     {
  29.         get
  30.         {
  31.             yield return this.format;
  32.         }
  33.     }
  34. }

Now when we select a link in the editor, if it implements this interface we can manage properties, as per the screenshot:



So far it's great, using the reflection from c# helped to speed up the process a lot.

On next stage, we have the obvious question/problem, what happens if we have multiple converters that allow similar types?

Here we have several choices:
  • Take the first one
  • Show a selector when we complete link
  • Have the selector in the inspector instead
I haven't made a choice, but take first one + selector is the most sensible option for now.






Tuesday, 11 February 2014

Direction and UI design

Since I didn't posted for quite a bit in my blog, I thought maybe it's time for another post, since I've been pretty busy on many fronts.

First thing, I'm (finally) almost done with this China project, and this was a right pain (for a pretty nice result have to admit). There was a one last bug and I hope the last update will finally tick if off. The result of this update will certainly bring another blog post, but since I don't know yet I'll shut up in the mean time ;)

On other parts, I'm working a lot on reworking my demo tool user interface. I don't want to rewrite from scratch, since I believe it's a bad idea in many cases, but want to shift a bit and try something new.

Main thing, is, at the end of the day, people use a software, so the ui design/functionalities comes as a first. As system programmers, we often will write a kick ass runtime, but forget the utter basics of useability (a basic usability  function might come very late in the pipeline because we found this new super fast code generation technique and focused there instead of any ui part).

This is of course even more apparent when you work with small teams (I mean, under 20 people). In my case and my tool I'm one developer, which also have to do projects for a living, so I can't even dedicate 10% of my full time on this.

So instead, I started to think of it in reverse, eg: this is what I need for my tool:
  • Patch interface
  • Code editor
  • Dock panel (since I want to organize my layout)
  • Node lister
  • Informative elements (logger/project explorer/compilation error report...)
So now the concept is to build the main User Interface without any runtime in there.

By then you might argue that building a shell without any features is at best useless, and by trying it now, I would disagree:
  • By using a ui without being distracted by any fancy looking visual you create, you start to really focus on ui. For example, you will immediately notice you'd like a dock, better undo function, that your ui is not smooth (no reference to 4v here ;) .
  • Since I know how to implement decently good graphics (still need to improve on that area for sure as well ;), The way I implement a particle system (as example) does not matter at this stage. And actually by thinking of this particle system I will see how the user interface can support the feature. Already having this particle system hinders that fact.
  • You WILL modify your user interface to correlate with your runtime (and modify you runtime too, softwares are constantly modified as a fact), but at least by working on UI first, you'll make sure that a rather decent amount of usability features are present and you software is not a bulk of unusable functions.
  • People want to use a software, so working on mockup also allows me to show a proper designer/ui specialist the concepts, and get insights (and programmers should never have the last word on ui design, another fact). What is convenient is what the user finds convenient, not your opinion as a programmer. Having well designed mockups also allows me to send a template find to a friend which will help building a nice menu order, shortcut list. While I delegate that, I suddenly have much more available time to build the core runtime.
So now when you build UI, you're overbloated with APIs, and it's hard to make a choice.

Since I don't plan to port my tool to whatever Android/Mac... (and anyway, as above, if you don't have a team of  20+ programmers or open source your program, don't even remotely think about multiplatform), I have the following choices (which can of course being mixed up, but single API streamlines things a tad).

  • Windows forms: The old good one, integrates super easily with Direct3D, simple to learn, can be extreme clunky and an absolute pain to do theming. Code editors/Dockers already available, good graph component for now I rolled my own (in D2D)
  • Direct2d : Great for building graph and other custom widgets,very good text/strokes rendering, quite fast in general, but complex layouts with d2d on it's own will be for sure cumbersome (not speaking of building a code editor with syntax coloring and other nitfy features).
  • Wpf: Most balanced of all, text can be blurry at times still, but good at shapes, code editor+docker also available, Direct3d integration reasonably easy, but such an overdesigned api that it's really daunting (for writing 2 crappy forms it's more or less ok, but decent builders become much more cumbersome). Theming is not too hard but also badly streamlined. 
  • Direct3d : Same as Direct2d, but you have to do all yourself ;) You could really start to build proper gpu UI, and start to rethink data structures but it's a hell of work. If you'd need intermediate texture integration in your canvas, that will rock performances compared to anything else tho.
So here we go for now, some more info soon ))