Deriving a Domain Specific Language for purely functional 3D Graphics
In this paper, we present a simple yet powerful Domain Specific Language for working with three-dimensional scene data. We start by specifying the domain types (see DDD) of a example problem, and extend the domain model with functionality for rendering and interaction.
Our first example is a little drawing tool which allows users to draw polygons on a vertical plane centered in the 3D scene. Later, we will extend the program with functionality for picking and translating objects by using a Maya-style 3D controller.
Note that this document is written as F# literate script, i.e. this document is both, the paper and the implementation ;)
Domain Driven Design
Let us start with the domain model for polygons:
1: 2: 3: |
|
Thus, a polygon is simple a immutable list of vectors (vertices). Next, we need a way to model polygons which are not yet fully defined. A mouse click extends these, and eventually they are finalized.
1: 2: 3: 4: 5: |
|
The Domain Model can be completed using a list of polygons in the scene, as well as one open polygon the user is currently working on.
1: 2: 3: 4: |
|
Creating a visual representation for our model
The simplest way of specifying a visual representation for data is by specifying a function which maps from data to graphics. Since we aim for rendering our graphics using graphics hardware (OpenGL), we don't directly compute pixels for the data, but we map our domain model to an explicit description of the scene, which we can render efficiently. Aardvark uses the typical description of this sort, a scene graph with geometric primitives at the leaves.
Let us start with the type describing 3D entities in the scene:
1: 2: 3: 4: 5: |
|
This type allows us to specify individual entities in a 3D scene.
Next let us extend the API to support many entities being packed together.
1: 2: 3: 4: 5: |
|
This definition allows us to describe scenes.
An example:
1: 2: 3: 4: 5: |
|
Which represents the following scene:
[todo image]
Thus, a visual representation for our polygon-sketching scene can be implemented as follows:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: |
|
Altough the function is rather verbose F# (many let bindings can be inlined), it precisely captures the semantics of the transformation. Next, we would like to implement interaction techniques. The user usually wants to issue commands to the program and observe the resulting effects.
In order to model interactions cleanly, let us introduce a union type modeling all possible interactions.
1: 2: 3: 4: |
|
Remember, our model is immutable. Therefore, adding a point to our model actually means computing a new model, which is the old model containing the new point.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: |
|
Again, the function precisely captures the semantics of our update commands. Who generates these update commands?
ClosePolygon, for example, could be associated with a mouse click. This means that mouse clicks must produce 3D positions. But a mouse click doesn't usually produce anything, so what can we do? This is exactly the point where our clean API seems to get messy.
One common approach here is to define a picking manager which handles mouse events and translates the events to domain specific data (such as 3D positions). However, the scope and responsibilities of such a manager are often difficult to define, and an implementation using mutable/global state becomes chaotic.
In this work, we take a different approach inspired by the Elm Architecture. The key insight is to perform operations exactly at the points where all necessary information is at hand, or can be computed easily. Since we already defined a DSL for specifying 3D scenes, the obvious choice is to resolve our commands there! This leads to a clean seperation of concerns and encapsulation of state.
Let us take the expression which effectively creates the drawable 3D plane in our scene:
1: 2: 3: |
|
We have the domain model at hand, as well as specfic information such as 3D coordinates. In order to express pick operations here, we need to enrich our Scene representation with pick operations.
Let us first define an API for specifying picks. For our example, we need to handle clicks and mouse moves. Since we want to work with 3D data, our events should be equipped with 3D coordinates:
1: 2: 3: 4: 5: |
|
Let us now define a type which describes the reaction of a mouse event in a generic manner. This means that the produced command message is a type argument for our function type.
1:
|
|
This abstraction here leads the way to specifying message-agnostic 3D scenes. As a consequence, our scene type now carries a type argument representing the type of message which can be produced by the scene itself.
Additionally we equip our render constructor with the capability of producing pick commands.
1: 2: 3: 4: 5: 6: |
|
Let us define functions which provide curried constructions of our message type. This comes in handy soon:
1: 2: 3: 4: 5: 6: |
|
We can check our design by implementing a visual representation of our drawing plane and let us associate a pick operation to it.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: |
|
The above code is a pure expression, i.e. no side effects are involved. The value simply stores first class functions. When provided with a world space pick point, and the description of a mouse event, they produce a command. Although rather nice, the code is still repetitive.
Some helpers will make the function look nicer.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: |
|
Applied to our original drawGroundPlane, the code duplication disappears:
1: 2: 3: 4: 5: 6: 7: |
|
Eta reduced:
1: 2: 3: 4: 5: 6: 7: |
|
Since moving cursors, adding points and closing polygons are the only operations in our example, our implementation is complete.
Our final DSL
Next we define a type which captures two functions and a domain model:
1: 2: 3: 4: 5: 6: |
|
In that context, we always work with views of the type Scene<'msg>. Therefore this type alias makes sense:
1:
|
|
We refactor the app instance for our polygon-sketching example, including the view function, a bit:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: |
|
It's no accident. Our final formulation matches the formulation of Elm applications!
In other words, we just reinvented the core part of the Elm Architecture!
An implementation for our API using the aardvark rendering engine.
Proof of Concept: Rendering values of type Scene
We want to render the scene using the Aardvark rendering engine.
To achieve this, there are several possibilities: Manually compute Render Objects for our scene. Create a Scene Graph which describes our scene in rendering terms.
In this work we chose the latter. Please note that the implementation is far from optimal, but serves as a simple base that we can optimize subsequently.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: |
|
The function toSg is a mapping from our scene representation to the Aardvark.Rendering Scene Graph.
Now we can render scenes. Next, we implement a simple picking scheme which computes command messages out of mouse clicks:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: |
|
Up to now, all presented functions are pure! For running the application, however, we need one single (!) side effecting function.
In Aardvark.Rendering Scene Graphs are compiled into an IRenderTask. This is then assigned to an IRenderControl, which displays the result.
The following code runs the evaluation of Elm-style program logic (the "Elm loop"), and attaches the 3D app to a IRenderControl.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: |
|
This concludes our first implementation. Let us try to run our app:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: |
|
Comparison to Related Work and Analysis of the Approach
In interactive graphics applications, the common approach is to map the domain model into some domain specific representation. Whenever a user input and the associated interactions lead to changes in the model, those changes need to be applied to the application state and the graphics representation (e.g. the scene graph). This approach puts the burden of synchronizing application state and graphics state on the application programmer, as found by Tobler 2011.
Where does the synchronization code go? There are basically two approaches:
- Move all state including domain logic into the scene representation
- Store deep references into the graphics representation and modify them on change
In Tobler's work, a semantic scene graph serves as model, and a rendering scene graph (view), including rendering state, is generated on the fly while traversing the semantic graph. Since the semantic scene graph serves as domain model description language, all domain modelling needs do be done directly in the semantic scene graph.
However versatile, domain logic which is not easily implemented as graph traversal poses difficulties.
In particular, cross cutting concerns (e.g. game objects need to modify other game objects far away in the graph) result in a lot of graph searching for the appropriate nodes. When optimizing away this search effort, the scene representation approach gradually converges toward the reference-keeping approach. Graphics entities, or global state, increasingly keep references on modifiable cells in order to be able to update those immediately.
The main subject of our design pattern is to not use any graphics specific state at all. The graphics representation is entirely recomputed after each domain model modification instead. This completely eliminates the need to track state in the graphics representation, but the trivial implementation is very inefficent.
Can this model be implemented efficiently?
Towards an efficient implementation
Let us summarize how our Elm-style polygon-sketching application performs updates. First, we apply the view function to the initial model, producing a value of type Scene<_>
. Next, we use a subscription on potential mouse events, which, when fired, traverses the scene and collects all potential PickOperations
. Each pick operation potentially yields a message, which is fed into the update function, which recomputes the model.
Feeding back this model to our view function ties the knot. We receive our final interaction loop.
Let us investigate the involved functions and their runtime behavior:
view
is linear in the size of the model (independent of the real change).- in the worst case, each
update
message touches every single entity in the model, resulting in linear runtime. In that case, the change really is of that size, i.e. a mutable update hasO(n)
as well. In practise however, operations often affect a single value deeply nested in the immutable structure, resulting in logarithmic runtime and memory cost. Accordingly, for updating immutable domain models we achievelog(n)
blowup compared to a mutable implementation.
Update cannot be optimized in the purely functional case.
Looking at the view function, however, we identify that the function is repeatedly invoked with an almost identical input.
Incremental Evaluation helps they said
Incremental evaluation, as used in Aardvark, can be used to incrementalize purely functional algorithms.
Conforming to the provided API, all inputs need to be wrapped into ModRef<_> cells. Given a set of modifiable input cells, functions can be implemented by utilizing combinator functions, such as Mod.map or the adaptive
Computation Expression. These functions become agnostic to input value changes:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: |
|
From a high level perspective, our incremental system has the following features:
- Inputs can be defined as modifiable cells
- Purely functional algorithms can be formulated on top of such modifiable cells
- The outpus of the algorithm can be computed by using Mod.force
- The inputs can be changed by using transact. Subsequent calls to Mod.force will return a value consistent with the current input values.
Thus, incremental evaluation does NOT provide us with a solution to our view function efficiency problem. We are working with purely functional input data. The entire domain model is new after a modification, meaning that our inputs alone will never be modified without everything else. This makes incremental evaluation infeasible.
As we discovered in practise, incremental evaluation can greatly speed up rendering performance. However, from an engineering point of view, programs quickly become large and interactions between various value changes complex (comparable to callback hell).
In order to utilize incremental evaluation, we need to map our purely functional model to a modifiable one. This allows us to perform updates as specific, targeted changes, which in turn can be used by the incremental evaluation system.
Mapping immutable values to mutable data sounds plausible. But where should this conversion occur?
While fablish and web frameworks use immutable data until the final rendering phase, we chose to map immutable values to mutable data prior to scene description generation. The rationale behind this is:
- The domain model is relatively small compared to a scene description. Working with a mutable scene description is more efficient.
- Typically, domain models are built on simple datatypes consisting of records and similar things. These might be easier to work with than general purpose scene descriptions.
Unpersisting data structures
Consider the transition of a record from immtuable to modifiable:
1: 2: 3: 4: |
|
ImmutableData1 and MutableData1 are structurally equal, with all leaf fields wrapped into modifiable cells. //what? please rephrase.
How can we implement a procedure that applies immutable data changes into the mutable variant as update?
When immutable changes are detected, updates must be performed onto the according mutable fields. For records containing primitive values, the procedure boils down to traversing the data structure and applying new values to their mutable counterparts.
The problem arises with set data structures. In order to associate old entries in the mutable data with new values in the immutable set, some artificial references need to be introduced.
In this setup we propose to use integer values wrapped in a container class:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Let us now derive immutable and mutable models for our polygon-sketching app:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: |
|
Such a transformation seems to be rather mechanical. It can be formalized into the following procedure:
- Equip each immutable domain type with an identifier (to be used for nested changes in sets)
-
For each domain type, create an associated mutable version. It contains one extra field which points to the immutable structure, and:
- for each field which is a domain type, use the mutable variant of the domain type in the mutable version of the parent structure
- for each primitive field, create a field which wraps the original value in a Mod
- for each field of type set or list, generate a data structure which tracks immutable references and allows to efficiently merge an immutable representation of the set/list into the mutable version
This translation sheme can be implemented as compiler plugin. For the sake of completeness we present the compilation result for our drawing model (We use the DomainType
attribute as a hint for the plugin to handle this type as domain type):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: |
|
The mutable model allows us to implement an incremental variant of the view function.
Our immutable scene representation, as-is, cannot express inner changes efficiently.
As an example, consider the constructor for scene transformations:
val Transform : Trafo3d * seq<Scene1> -> Scene<'msg>
In order to efficiently handle trafo changes, the following signature is required:
val Transform : IMod<Trafo3d> * aset<Scene1> -> Scene<'msg>>
An incremental version of the scene data type can be derived by replacing all immutable values with their modifiable counterparts:
1: 2: 3: 4: 5: |
|
In our implementation, we chose a different representation.
Instead of using a discriminated union, we prefer to use the extensible (regarding data types) OOP approach found in Aardvark.Rendering. We can directly integrate with the Aardvark Scene Graph this way:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: |
|
The Aardvark Scene Graph implementation is based on Attribute Grammars. We can extend the Aardvark Scene Graph by defining additional Ag-rules for our types dynamically. We augment our type ISg<'msg>
with semantic functions such as:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
This translates our ISg<'msg> into Aardvark ISg nodes whenever their RenderObjects semantic is requested.
The complete implementation of the scene graph nodes can be found here.
An efficient version of the drawing application
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: |
|
Discussion
We ended up with an API for submitting changes to a domain model and evaluating a view function on that model. For efficiency reasons, we utilized adaptive functional programming in order to write one single view function which is agnostic to changes. Since the incremental system requires changes at inputs in order to perform necessary recomputations, we, at some point, need to translate changes in immutable data to reference cell mutations. This procedure can be automated and implemented as a compiler extension.
In other words: Previous work focused on feeding changes into a scene representation efficiently. Incremental evaluation is greatly beneficial (even indispensable) to those procedures. Although this is very efficient, keeping track of incremental computations and adaptive dependencies becomes difficult as complexity increases. On the other side, working with immutable updates on domain models is easy, but an efficient implementation is not tangible.
In this work we combine the two, and indeed we get the best of both. The immutable update mechanism can be seen as automatic, convenient input changer, while the incremental evaluation backend takes care of mapping updates efficiently to the underlying graphics hardware.
Refining the Model
External events
So far we only worked with mouse inputs and their interactions with 3D objects. In general, however, we often need to emit messages without user interaction, or due to the occurance of an external event. Elm extends its application pattern with an additional function. It allows subscriptions to be registered between the current model and sources of events.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: |
|
After each update, we purely functionally recompute the subscription set. Then we compute the difference between the current and the old subscription set. Unnecessary subscriptions can be removed safely, while new subscriptions are added where needed.
Subscriptions --- The observer pattern's new clothes?
At first, subscriptions look like a rather imperative pattern.
Traditional subscription-based techniques have severe flaws:
- Subscriptions need to be registered, and they need to be unregistered after expiration. Subscribe in Rx has type: 'val subscribe : ('a -> unit) -> IDisposable' which already hints at the significant effort required for tracking subscriptions in application code.
- Classic subscriptions compose by building up subscription networks, through which input events travel. Observing results from such networks can only be accomplished by registering (mutating) actions. Whenever an input changes, the change propagates through subscription chains. Eventually, actions are triggered to write new values into application state. Since change propagation in such networks is performed eagerly, the program can be assigned with faithful semantics. But understanding such networks statically, or even debugging their runtime behavior, can be extremely tricky.
In our design pattern, the set of subscriptions is recomputed automatically after each update. This means that the problem of "cleaning up" subscriptions completely disappears. Secondly, subscriptions in our model are purely functional. They build up lists of messages representing their effects, which in turn are fed back into the update function. This greatly improves debugging, since the results of external events can be observed at a single point in code instead of various points scattered in the domain logic.
Depth ordering of Pick Events
In our original implementation, we always used the frontmost pick and ignored all further hits along the pick ray. Howewer, it is often necessary to compute all pick occurences along the pick ray. The user might want to move polygon corners around by click-dragging. When hovering over control points of a polygon, the pick must go "through" the control point geometry and hit the ground plane below. This can be accomplished by a simple extension to the pick API:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: |
|
Asynchronous computations
One main design guideline when designing the API for pick operations was to have necessary information available at one point in the code. Considering commands which trigger long-running or computationally intensive tasks, the best place for implementation is in the update function, where the message itself and the immutable domain model are available. However, performing long-running computations in the update function directly stalls the application. Therefore, to express such operations, we introduce the Cmd type and an additional argument to the update function.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: |
|
Further examples
Implementing a transformation controller
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: |
|
Composing translation controller and polygon drawing
Related Work
Conclusions
In this work we used the ELM architecture in the context of 3D graphics. We developed a DSL for specifying 3D scenes and user interaction with graphical elements in a purely functional manner. When rendering UI elements, the resulting scene description can be efficiently rendered using HTML virtual dom diffing. This approach is not feasable for data intensive 3D applications. In this paper we show how to compute changes of immutable data structures in order to use those changes in the incremental rendering engine provided by the aardvark platform.
Full name: Elmish.Polygon
Full name: Microsoft.FSharp.Collections.list<_>
{finishedPoints: obj;
cursor: obj;}
Full name: Elmish.OpenPolygon
from Microsoft.FSharp.Core
{finished: Polygon list;
working: Option<OpenPolygon>;}
Full name: Elmish.DrawingModel
| Sphere of center: obj * radius: float
| Cone of center: obj * dir: obj * height: float * radius: float
| Cylinder of center: obj * dir: obj * height: float * radius: float
| Quad of obj
Full name: Elmish.Primitive
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
| Transform of obj * Scene1 list
| Colored of obj * Scene1 list
| Render of Primitive
| Group of Scene1 list
Full name: Elmish.Scene1
Full name: Elmish.scene
Full name: Elmish.colored1
Full name: Elmish.transformed1
Full name: Elmish.view1
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Full name: Microsoft.FSharp.Collections.List.toSeq
Full name: Microsoft.FSharp.Core.Option.isSome
Full name: Microsoft.FSharp.Collections.List.map
| ClosePolygon
| AddPoint of obj
| MoveCursor of obj
Full name: Elmish.DrawCommand
Full name: Elmish.updateDrawing
Full name: Elmish.groundPlane
| Move of obj
| Down of obj * obj
Full name: Elmish.MouseEvent
Full name: Elmish.PickOperation<_>
| Transform of obj * seq<Scene<'msg>>
| Colored of obj * seq<Scene<'msg>>
| Render of PickOperation<'msg> list * Primitive
| Group of seq<Scene<'msg>>
Full name: Elmish.Scene<_>
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
Full name: Elmish.transform
Full name: Elmish.transform'
Full name: Elmish.translate
Full name: Elmish.colored
Full name: Elmish.render
Full name: Elmish.group
Full name: Elmish.drawGroundPlane
module Event
from Microsoft.FSharp.Control
--------------------
type Event<'T> =
new : unit -> Event<'T>
member Trigger : arg:'T -> unit
member Publish : IEvent<'T>
Full name: Microsoft.FSharp.Control.Event<_>
--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
new : unit -> Event<'Delegate,'Args>
member Trigger : sender:obj * args:'Args -> unit
member Publish : IEvent<'Delegate,'Args>
Full name: Microsoft.FSharp.Control.Event<_,_>
--------------------
new : unit -> Event<'T>
--------------------
new : unit -> Event<'Delegate,'Args>
Full name: Elmish.Event.move
Full name: Microsoft.FSharp.Core.bool
Full name: Elmish.Event.down
Full name: Elmish.Event.down'
Full name: Elmish.Event.leftDown
Full name: Elmish.Event.rightDown
Full name: Elmish.Event.position
Full name: Elmish.on
module Event
from Elmish
--------------------
module Event
from Microsoft.FSharp.Control
--------------------
type Event<'T> =
new : unit -> Event<'T>
member Trigger : arg:'T -> unit
member Publish : IEvent<'T>
Full name: Microsoft.FSharp.Control.Event<_>
--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
new : unit -> Event<'Delegate,'Args>
member Trigger : sender:obj * args:'Args -> unit
member Publish : IEvent<'Delegate,'Args>
Full name: Microsoft.FSharp.Control.Event<_,_>
--------------------
new : unit -> Event<'T>
--------------------
new : unit -> Event<'Delegate,'Args>
Full name: Elmish.drawGroundPlane2
Full name: Elmish.drawGroundPlane3
{initial: 'model;
update: 'model -> 'msg -> 'model;
view: 'model -> 'view;}
Full name: Elmish.App<_,_,_>
Full name: Elmish.ThreeDApp<_,_>
Full name: Elmish.Pick.ignore
Full name: Elmish.viewDrawing
from Elmish
Full name: Elmish.drawingApp
type AutoOpenAttribute =
inherit Attribute
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
member Path : string
Full name: Microsoft.FSharp.Core.AutoOpenAttribute
--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
{trafo: obj;
color: obj;}
Full name: Elmish.ConvertToSceneGraph.State
Full name: Elmish.ConvertToSceneGraph.toSg
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.map
Full name: Microsoft.FSharp.Collections.Seq.toArray
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Full name: System.Array
Full name: Microsoft.FSharp.Collections.Array.replicate
Full name: Elmish.Picking.hitPrimitive
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MinValue : float
static val MaxValue : float
static val Epsilon : float
static val NegativeInfinity : float
static val PositiveInfinity : float
...
end
Full name: System.Double
Full name: Elmish.Picking.pick
Full name: Microsoft.FSharp.Collections.Seq.toList
Full name: Microsoft.FSharp.Collections.List.collect
Full name: Microsoft.FSharp.Collections.List.filter
Full name: Microsoft.FSharp.Core.Operators.not
Full name: Microsoft.FSharp.Collections.List.isEmpty
Full name: Microsoft.FSharp.Core.Operators.snd
Full name: Microsoft.FSharp.Collections.List.sortBy
Full name: Microsoft.FSharp.Core.Operators.fst
Full name: Elmish.createApp
from Elmish
from Elmish
Full name: Elmish.app
Full name: Elmish.win
Full name: Elmish.frustum
Full name: Elmish.cameraView
Full name: Elmish.camera
Full name: Elmish.sg
Full name: Elmish.cursorGeometry
Full name: Elmish.hasCursor
Full name: Elmish.adaptiveScene
Full name: Elmish.sceneWithDisabledCursor
Full name: Elmish.sceneWithCursor
{value: int;}
Full name: Elmish.ImmutableData1
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
{mvalue: obj;}
Full name: Elmish.MutableData1
type AllowNullLiteralAttribute =
inherit Attribute
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
member Value : bool
Full name: Microsoft.FSharp.Core.AllowNullLiteralAttribute
--------------------
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
type Id =
new : unit -> Id
override ToString : unit -> string
static member New : Id
Full name: Elmish.IDs.Id
--------------------
new : unit -> Id
static member Add : location1:int * value:int -> int + 1 overload
static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads
static member Decrement : location:int -> int + 1 overload
static member Exchange : location1:int * value:int -> int + 6 overloads
static member Increment : location:int -> int + 1 overload
static member Read : location:int64 -> int64
Full name: System.Threading.Interlocked
Interlocked.Increment(location: byref<int>) : int
Full name: Elmish.IDs.Id.New
Full name: Elmish.IDs.Id.ToString
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
{finishedPolys: obj;
cursor: obj;}
Full name: Elmish.OpenPolygon2
{id: obj;
finished: obj;
working: Option<OpenPolygon>;}
Full name: Elmish.DrawingModel2
{original: DrawingModel2;
finished: obj;
working: obj;}
Full name: Elmish.MDrawingModel2
Full name: Elmish.unpersist
Full name: Elmish.apply
Full name: Microsoft.FSharp.Collections.Seq.filter
Full name: Microsoft.FSharp.Core.Operators.ignore
Full name: Elmish.SimpleDrawingApp.Polygon
{cursor: obj;
finishedPoints: obj;}
Full name: Elmish.SimpleDrawingApp.OpenPolygon
{finished: obj;
working: Option<OpenPolygon>;}
Full name: Elmish.SimpleDrawingApp.Model
Full name: Elmish.SimpleDrawingAppGenerated.Polygon
{cursor: obj;
finishedPoints: obj;}
Full name: Elmish.SimpleDrawingAppGenerated.OpenPolygon
{mutable _id: obj;
finished: obj;
working: Option<OpenPolygon>;}
interface obj
member ToMod : reuseCache:'a0 -> MModel
override Id : obj
override Id : 'a with set
Full name: Elmish.SimpleDrawingAppGenerated.Model
Full name: Elmish.SimpleDrawingAppGenerated.Model.ToMod
Full name: Elmish.SimpleDrawingAppGenerated.Model.Id
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
{mutable _original: Model;
mfinished: obj;
mworking: obj;}
member Apply : arg0:Model * reuseCache:'a0 -> unit
Full name: Elmish.SimpleDrawingAppGenerated.MModel
Full name: Elmish.SimpleDrawingAppGenerated.MModel.Apply
type Object =
new : unit -> obj
member Equals : obj:obj -> bool
member GetHashCode : unit -> int
member GetType : unit -> Type
member ToString : unit -> string
static member Equals : objA:obj * objB:obj -> bool
static member ReferenceEquals : objA:obj * objB:obj -> bool
Full name: System.Object
--------------------
Object() : unit
| Transform of obj * obj
| Colored of obj * obj
| Render of Primitive
| Group of obj
Full name: Elmish.Scene2
interface
inherit obj
end
Full name: Elmish.AdaptiveScene.ISg<_>
type Group<'msg> =
interface obj
interface ISg<'msg>
new : xs:'a -> Group<'msg>
member Children : 'a
override Children : 'a
Full name: Elmish.AdaptiveScene.Group<_>
--------------------
new : xs:'a -> Group<'msg>
Full name: Elmish.AdaptiveScene.Group`1.Children
Full name: Elmish.AdaptiveScene.Group`1.Children
type Render<'msg> =
interface ISg<'msg>
new : xs:Primitive -> Render<'msg>
member Primitive : Primitive
Full name: Elmish.AdaptiveScene.Render<_>
--------------------
new : xs:Primitive -> Render<'msg>
member Render.Primitive : Primitive
Full name: Elmish.AdaptiveScene.Render`1.Primitive
--------------------
type Primitive =
| Sphere of center: obj * radius: float
| Cone of center: obj * dir: obj * height: float * radius: float
| Cylinder of center: obj * dir: obj * height: float * radius: float
| Quad of obj
Full name: Elmish.Primitive
from Aardvark.Base
from Elmish
module Semantic
from Aardvark.SceneGraph.Semantics.ActiveSemantics
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.AttributeExtensions
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.BoundingBoxes
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.ModeSemantics
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.RenderObjectSemantics
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.SurfaceSemantics
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.TrafoExtensions
--------------------
module Semantic
from Aardvark.SceneGraph.Semantics.UniformSemantics
--------------------
type Semantic =
inherit Attribute
new : unit -> Semantic
Full name: Aardvark.Base.Ag.Semantic
--------------------
new : unit -> Semantic
type LeafSemantics =
new : unit -> LeafSemantics
member RenderObjects : l:Render<'msg> -> aset<IRenderObject>
Full name: Elmish.AgExtension.LeafSemantics
--------------------
new : unit -> LeafSemantics
Full name: Elmish.AgExtension.LeafSemantics.RenderObjects
Full name: Aardvark.SceneGraph.Semantics.RenderObjectSemantics.Semantic.renderObjects
Full name: Microsoft.FSharp.Core.Operators.failwith
from Aardvark.ImmutableSceneGraph.PickStuff
from Elmish
type Action =
| ClosePolygon
| AddPoint of obj
| MoveCursor of obj
Full name: Elmish.FinalDrawingApp.Action
--------------------
type Action<'T> =
delegate of 'T -> unit
Full name: System.Action<_>
--------------------
type Action<'T1,'T2> =
delegate of 'T1 * 'T2 -> unit
Full name: System.Action<_,_>
--------------------
type Action<'T1,'T2,'T3> =
delegate of 'T1 * 'T2 * 'T3 -> unit
Full name: System.Action<_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 -> unit
Full name: System.Action<_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> unit
Full name: System.Action<_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> unit
Full name: System.Action<_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> unit
Full name: System.Action<_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
Full name: Elmish.FinalDrawingApp.update
Full name: Elmish.FinalDrawingApp.viewPolygon
module List
from Aardvark.ImmutableSceneGraph.PickStuff
--------------------
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Full name: Aardvark.ImmutableSceneGraph.PickStuff.Primitives.cylinder
module Scene
from Aardvark.ImmutableSceneGraph
--------------------
type Scene<'msg> =
| Transform of obj * seq<Scene<'msg>>
| Colored of obj * seq<Scene<'msg>>
| Render of PickOperation<'msg> list * Primitive
| Group of seq<Scene<'msg>>
Full name: Elmish.Scene<_>
Full name: Aardvark.ImmutableSceneGraph.Scene.render
module Pick
from Aardvark.ImmutableSceneGraph.PickStuff
--------------------
module Pick
from Aardvark.ImmutableSceneGraph
--------------------
module Pick
from Elmish
val ignore : 'a list
Full name: Aardvark.ImmutableSceneGraph.PickStuff.Pick.ignore
--------------------
val ignore : 'a list
Full name: Elmish.Pick.ignore
Full name: Aardvark.ImmutableSceneGraph.Scene.group
Full name: Elmish.FinalDrawingApp.view
Full name: Aardvark.ImmutableSceneGraph.PickStuff.on
union case Sub.Mouse: (Direction -> Aardvark.Application.MouseButtons -> Aardvark.Base.PixelPosition -> Option<'msg>) -> Sub<'msg>
--------------------
module Mouse
from Aardvark.ImmutableSceneGraph.PickStuff
Full name: Aardvark.ImmutableSceneGraph.PickStuff.Mouse.move
Full name: Aardvark.ImmutableSceneGraph.PickStuff.Mouse.down'
Full name: Aardvark.ImmutableSceneGraph.Scene.colored
Full name: Aardvark.ImmutableSceneGraph.Scene.transform'
Full name: Aardvark.ImmutableSceneGraph.Scene.agroup
Full name: Elmish.FinalDrawingApp.viewScene
Full name: Aardvark.ImmutableSceneGraph.Scene.camera
Full name: Aardvark.ImmutableSceneGraph.Scene.effect
Full name: Elmish.FinalDrawingApp.initial
Full name: Elmish.FinalDrawingApp.app
from Aardvark.Elmish
Full name: Aardvark.Elmish.Subscriptions.none
| TimeSub of TimeSpan * (TimeSpan -> 'msg)
| KeyPress of (obj -> Option<'msg>)
| Many of Sub<'msg> list
Full name: Elmish.Subscriptions1.Sub<_>
type TimeSpan =
struct
new : ticks:int64 -> TimeSpan + 3 overloads
member Add : ts:TimeSpan -> TimeSpan
member CompareTo : value:obj -> int + 1 overload
member Days : int
member Duration : unit -> TimeSpan
member Equals : value:obj -> bool + 1 overload
member GetHashCode : unit -> int
member Hours : int
member Milliseconds : int
member Minutes : int
...
end
Full name: System.TimeSpan
--------------------
TimeSpan()
TimeSpan(ticks: int64) : unit
TimeSpan(hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : unit
Full name: Elmish.Subscriptions1.timeSub
{rotation: float;
_id: obj;}
Full name: Elmish.Animation.Model
{mrotation: obj;}
Full name: Elmish.Animation.MModel
Full name: Elmish.Animation.Msg
Full name: Elmish.Animation.update
Full name: Elmish.Animation.view
Full name: Aardvark.ImmutableSceneGraph.Scene.transform
Full name: Elmish.Animation.subscriptions
from Elmish
Full name: Elmish.Subscriptions1.timeSub
| Solid
| PickThrough
Full name: Elmish.Transparency
Full name: Elmish.PickOperation2<_>
Full name: Elmish.depthTest
from Fablish
{image: obj;}
Full name: Elmish.EnvExample.Model
| LoadImages of string list
| ImagesLoaded of obj
| Progress of float
Full name: Elmish.EnvExample.Msg
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
Full name: Elmish.EnvExample.loadImage
module Env
from Fablish.CommonTypes
--------------------
type Env<'msg> =
{run: Cmd<'msg> -> unit;}
Full name: Fablish.CommonTypes.Env<_>
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
Full name: Microsoft.FSharp.Collections.List.length
Full name: Microsoft.FSharp.Collections.List.mapi
union case Cmd.Cmd: Async<'msg> -> Cmd<'msg>
--------------------
module Cmd
from Fablish.CommonTypes
--------------------
type Cmd<'msg> =
| NoCmd
| Cmd of Async<'msg>
Full name: Fablish.CommonTypes.Cmd<_>
Full name: Elmish.EnvExample.update
from Elmish
from Aardvark.ImmutableSceneGraph
type CompilationRepresentationAttribute =
inherit Attribute
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
member Flags : CompilationRepresentationFlags
Full name: Microsoft.FSharp.Core.CompilationRepresentationAttribute
--------------------
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
| None = 0
| Static = 1
| Instance = 2
| ModuleSuffix = 4
| UseNullAsTrueValue = 8
| Event = 16
Full name: Microsoft.FSharp.Core.CompilationRepresentationFlags
| X
| Y
| Z
Full name: Scratch.DomainTypes.Generated.Axis
Full name: Elmish.TranslateController.AxisModule.dir
Full name: Elmish.TranslateController.AxisModule.moveAxis
type Action =
| Hover of Axis * obj
| NoHit
| MoveRay of obj
| Translate of Axis * obj
| EndTranslation
| ResetTrafo
Full name: Elmish.TranslateController.Action
--------------------
type Action<'T> =
delegate of 'T -> unit
Full name: System.Action<_>
--------------------
type Action<'T1,'T2> =
delegate of 'T1 * 'T2 -> unit
Full name: System.Action<_,_>
--------------------
type Action<'T1,'T2,'T3> =
delegate of 'T1 * 'T2 * 'T3 -> unit
Full name: System.Action<_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 -> unit
Full name: System.Action<_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> unit
Full name: System.Action<_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> unit
Full name: System.Action<_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> unit
Full name: System.Action<_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
--------------------
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16> =
delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> unit
Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
module Axis
from Elmish.TranslateController
--------------------
type Axis =
| X
| Y
| Z
Full name: Scratch.DomainTypes.Generated.Axis
module TranslateController
from Scratch.DomainTypes.Generated
--------------------
module TranslateController
from Scratch.DomainTypes
Full name: Elmish.TranslateController.hasEnded
Full name: Elmish.TranslateController.hover
Full name: Elmish.TranslateController.translate_
Full name: Elmish.TranslateController.initalModel
Full name: Elmish.TranslateController.initial
module Camera
from Scratch.DomainTypes.Generated
--------------------
module Camera
from Scratch.DomainTypes
Full name: Elmish.TranslateController.updateModel
{mutable _id: Id;
hovered: Option<Axis>;
activeTranslation: Option<Axis * Plane3d * V3d>;
trafo: Trafo3d;
editTrafo: Trafo3d;}
interface IUnique
member ToMod : reuseCache:ReuseCache -> MTModel
Full name: Scratch.DomainTypes.Generated.TranslateController.TModel
Full name: Elmish.TranslateController.update
module Scene
from Aardvark.ImmutableSceneGraph
--------------------
type Scene =
{mutable _id: Id;
camera: Camera;
scene: TModel;}
interface IUnique
member ToMod : reuseCache:ReuseCache -> MScene
Full name: Scratch.DomainTypes.Generated.TranslateController.Scene
--------------------
type Scene<'msg> =
| Transform of obj * seq<Scene<'msg>>
| Colored of obj * seq<Scene<'msg>>
| Render of PickOperation<'msg> list * Primitive
| Group of seq<Scene<'msg>>
Full name: Elmish.Scene<_>
Full name: Elmish.TranslateController.viewModel
{mutable _original: TModel;
mhovered: ModRef<Option<Axis>>;
mactiveTranslation: ModRef<Option<Axis * Plane3d * V3d>>;
mtrafo: ModRef<Trafo3d>;
meditTrafo: ModRef<Trafo3d>;}
member Apply : arg0:TModel * reuseCache:ReuseCache -> unit
Full name: Scratch.DomainTypes.Generated.TranslateController.MTModel
Full name: Aardvark.ImmutableSceneGraph.Scene.translate
Full name: Aardvark.ImmutableSceneGraph.PickStuff.Mouse.down
Full name: Aardvark.ImmutableSceneGraph.PickStuff.whenever
Full name: Elmish.TranslateController.viewScene
Full name: Aardvark.ImmutableSceneGraph.Scene.map
Full name: Elmish.TranslateController.ofPickMsgModel
{mouseEvent: MouseEvent;
hits: bool;
keyEvent: KeyEvent;}
Full name: Aardvark.ImmutableSceneGraph.PickStuff.GlobalPick
| Down of MouseButtons
| Move
| Click of MouseButtons
| Up of MouseButtons
| NoEvent
Full name: Aardvark.ImmutableSceneGraph.PickStuff.MouseEvent
union case MouseEvent.Down: Aardvark.Application.MouseButtons -> MouseEvent
--------------------
union case MouseEvent.Down: obj * obj -> MouseEvent
union case MouseEvent.Move: MouseEvent
--------------------
union case MouseEvent.Move: obj -> MouseEvent
Full name: Microsoft.FSharp.Core.Option.isNone
Full name: Elmish.TranslateController.ofPickMsg
Full name: Elmish.TranslateController.app