public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg)
        {
            var springModel = new NestedSpheresStorySpringModel(coroutineService, x => ArrangeAndDecorateInternal(sg.Root, 0, x, sg), sg);

            springModel.Apply();
            return(new BasicStoryLayoutInstance(sg));
        }
        private void ArrangeAndDecorateInternal(int nodeIndex, int level, NestedSpheresStorySpringModel springModel, IStoryGraph sg)
        {
            var node   = sg.NodeObjects[nodeIndex];
            var aspect = sg.Aspects[nodeIndex];
            var index  = node.Id;

            var dynamicParts = new StoryNodeDynamicParts();
            var scale        = springModel.GetVisualRadius(index);
            var visualElems  = new List <IVisualElement>
            {
                new ModelVisualElement <IStoryComponent>(sg.Aspects[sg.Root])
                .SetModel(mainModel)
                .SetMaterial(sphereMaterials[level % sphereMaterials.Length])
                .SetRenderState(sphereRenderState)
                .SetTransform(new Transform(scale, Quaternion.Identity, Vector3.Zero))
                //.SetTransformSpace(TransformSpace.ScreenAlighned)
                .SetGetDistanceToCameraSq((o, t, c) => ((t.Offset - c.GetEye()).Length() + scale).Sq())
                .SetHide(x => !x.ShowAux1)
            };

            node.Transform = new Transform(1, Quaternion.RotationY(springModel.GetRotation(index)), springModel.GetPosition(index));

            if (level == 0)
            {
                var edgeVisuals = sg.Edges.Select(edge => CreateEdgeVisualElement(node, sg.NodeObjects[edge.First], sg.NodeObjects[edge.Second]));
                visualElems.AddRange(edgeVisuals);
            }

            var visualEffects = new[]
            {
                new FocusVisualEffect()
            };

            if (sg.Children[index].Any())
            {
                foreach (var childIndex in sg.Children[index])
                {
                    ArrangeAndDecorateInternal(childIndex, level + 1, springModel, sg);
                }
                dynamicParts.DefaultViewpointMechanism = new WallDefaultViewpointMechanism(node, new TargetedControlledCameraY.Props
                {
                    Yaw         = MathHelper.PiOver4,
                    Pitch       = MathHelper.PiOver4,
                    ZNear       = 0.01f,
                    ZFar        = 1000f,
                    FieldOfView = MathHelper.PiOver4,
                    Distance    = 2f * scale,
                });
            }
            else
            {
                dynamicParts.DefaultViewpointMechanism = new WallDefaultViewpointMechanism(node, new TargetedControlledCameraY.Props
                {
                    ZNear       = 0.01f,
                    ZFar        = 1000f,
                    FieldOfView = MathHelper.PiOver4,
                    Distance    = GraphicsHelper.FrustumDistance,
                });
                dynamicParts.PlacementSurface2D = new PlanarPlacementSurface(node, new Transform(2f, Quaternion.Identity, new Vector3(0, 0, -MathHelper.FrustumDistance)));
                dynamicParts.PlacementSurface3D = new PlanarPlacementSurface(node, Transform.Scaling(0.1f));
            }
            dynamicParts.Hittable       = new SphereHittable <ISceneNode>(node, x => new Common.Numericals.Geometry.Sphere(x.GlobalTransform.Offset, scale), true);
            dynamicParts.VisualElements = visualElems;

            dynamicParts.GetVisualEffects = n => viewServiceLazy.Value.MainView.FocusNode == n
                ? focusVisualEffect.EnumSelf()
                : Enumerable.Empty <IVisualEffect>();

            aspect.SetDynamicParts(dynamicParts);
        }