/// <summary>
        /// For the given parameter, calculate the actual geometry of the specified label in absolute world coordinates.
        /// </summary>
        /// <remarks>The actual position is calculated from the <see cref="MyNodeLabelModelParameter.Ratio"/> specified in the parameter as
        /// the counterclock-wise angle on the label owner's circumference. Note that we also rotate the label layout itself accordingly.</remarks>
        public IOrientedRectangle GetGeometry(ILabel label, ILabelModelParameter parameter)
        {
            var modelParameter = parameter as MyNodeLabelModelParameter;
            var ownerNode      = label.Owner as INode;

            if (modelParameter != null && ownerNode != null)
            {
                //If we have a matching parameter and a node as owner, calculate the angle for the label position and the matchin rotation of the label layout box itself.
                var               center = ownerNode.Layout.GetCenter();
                var               radius = Math.Max(ownerNode.Layout.Width, ownerNode.Layout.Height) * 0.5d;
                var               ratio  = modelParameter.Ratio;
                double            angle  = ratio * Math.PI * 2;
                double            x      = Math.Sin(angle);
                double            y      = Math.Cos(angle);
                PointD            up     = new PointD(-y, x);
                OrientedRectangle result = new OrientedRectangle();
                result.SetUpVector(up);
                result.Resize(label.PreferredSize);
                result.SetCenter(center + (offset + radius + label.PreferredSize.Height * 0.5d) * up);
                return(result);
            }
            else
            {
                return(OrientedRectangle.Empty);
            }
        }
        public IOrientedRectangle GetGeometry(ILabel label, ILabelModelParameter parameter)
        {
            var   php   = parameter as PoolHeaderParameter;
            INode owner = (INode)label.Owner;

            if (php == null || owner == null)
            {
                return(null);
            }

            ITable  table  = owner.Lookup <ITable>();
            InsetsD insets = table != null && table.Insets != InsetsD.Empty ? table.Insets : new InsetsD(0);

            var orientedRectangle = new OrientedRectangle();

            orientedRectangle.Resize(label.PreferredSize);
            switch (php.Side)
            {
            case 0: // North
                orientedRectangle.SetUpVector(0, -1);
                orientedRectangle.SetCenter(new PointD(owner.Layout.X + owner.Layout.Width / 2, owner.Layout.Y + insets.Top / 2));
                break;

            case 1: // East
                orientedRectangle.SetUpVector(1, 0);
                orientedRectangle.SetCenter(new PointD(owner.Layout.GetMaxX() - insets.Right / 2, owner.Layout.Y + owner.Layout.Height / 2));
                break;

            case 2: // South
                orientedRectangle.SetUpVector(0, -1);
                orientedRectangle.SetCenter(new PointD(owner.Layout.X + owner.Layout.Width / 2, owner.Layout.GetMaxY() - insets.Bottom / 2));
                break;

            case 3: // West
            default:
                orientedRectangle.SetUpVector(-1, 0);
                orientedRectangle.SetCenter(new PointD(owner.Layout.X + insets.Left / 2, owner.Layout.Y + owner.Layout.Height / 2));
                break;
            }

            return(orientedRectangle);
        }