Este artigo foi traduzido por máquina.

Fronteiras da interface do usuário

Transformações de projeção sem matemática

Charles Petzold

Baixe o código de exemplo

Em praticamente qualquer sistema de elementos gráficos, as transformações constituem o recurso mais importante, na verdade, não desenha tudo.Em vez disso, as transformações alterar a aparência de objetos visuais modificando coordenadas com fórmulas matemáticas geralmente expressas como uma multiplicação de matriz.

A propriedade RenderTransform definida pelo UIElement foi no Silverlight desde seu início e antes disso, no Windows Presentation Foundation (WPF).Como a propriedade é definida pelo UIElement, você pode usá-lo com objetos gráficos, bem como texto, controles e mídia.Basta defina o RenderTransform a um objeto do tipo TranslateTransform, ScaleTransform, RotateTransform, SkewTransform, MatrixTransform (para controle completo sobre a matriz de transformação) ou um TransformGroup uma combinação de várias transformações.

Os tipos de transformações definido com RenderTransform são todos exemplos de bidimensional (2D) afim transformações.Transformações afim são muito bem com comportamento e apenas um pouco opaco: Linhas retas sempre são transformadas em linhas retas, elipses sempre são transformados em elipses e quadrados sempre são transformados em parallelograms.Se duas linhas paralelas antes da transformação, eles são ainda paralelos após a transformação.

Pseudo 3D

Silverlight 3 introduziu uma nova propriedade de UIElement chamada de projeção que permite a configuração de não é afim transformações em objetos gráficos, texto, controles e mídia.Transformações não afim não preservam paralelismo.

O tipo de transformação não afim permitido no Silverlight 3 ainda é representado por uma multiplicação de matriz e ainda tem restrições sobre o que pode fazer.Linhas retas sempre são transformadas em linhas retas e um quadrado sempre é transformado em uma simples quadrilateral convexo.Por “ quadrilateral ”, quero dizer uma figura de quatro lados (também chamada de tetragon ou quadrangle); por “ simples ”, quero dizer que os lados Don se cruzam, exceto em seus vértices; por “ convexo ”, quero dizer que os ângulos internos em cada vértice são menos de 180 graus.

Esse tipo de transformação não afim é útil para criar transformações cônico, onde oposta lados de um quadrado ou retângulo cônico um pouco em uma direção.Figura 1 mostra um texto com uma transformação cônico realizada por meio de uma configuração de propriedade de projeção muito simples.

Figure 1 Text with a Taper Transform
Figura 1 Texto com uma transformação cônico

O texto parece ser um pouco três tridimensional (3D) porque o final de cauda parece mais distante do nossos olhos — um efeito denominado uma projeção em perspectiva.

De certa forma, a propriedade de projeção proporciona Silverlight um pouco de “ pseudo 3D. ” Ele não é um sistema real 3D, porque não há nenhuma maneira de definir objetos no espaço 3D, não há conceito de câmeras, luzes ou sombreamento e — talvez mais importante — sem corte de objetos com base na sua organização no espaço 3D.

No entanto, trabalhar com a transformação de projeção requer o programador começar a pensar sobre três dimensões e rotação 3D especialmente sobre.Felizmente, os desenvolvedores do Silverlight fez alguns simples e comum o uso da propriedade projeção bastante fácil.

A abordagem mais fácil

Você pode definir a propriedade de projeção para um objeto Matrix3DProjection ou um objeto PlaneProjection.A propriedade Matrix3DProjection define apenas um objeto, mas é uma estrutura de Matrix3D 4 x 4, que requer muita matemática.(Para algumas abordagens para essa estrutura, consulte as entradas de blog no meu site — charlespetzold.com — com a data de 23 de julho de 2009 e 31 de julho de 2009.)

Mas, neste artigo, eu já prometidos por mim para evitar a matemática em sua maioria, que significa adequar-que vai ser se com a classe PlaneProjection.Embora a classe define as propriedades definíveis 12, vai ser concentrando em apenas seis deles.

Da fonte para download de código deste artigo é PlaneProjectionExperimenter, que permite que você experimentar interativamente com esses seis propriedades.Figura 2 mostra o programa em ação.Você pode executá-lo Compilando o programa para download ou executá-lo no meu site na Web em charlespetzold.com/Silverlight/PlaneProjectionDemos, juntamente com os outros programas neste artigo.Por enquanto, ignore o ponto azul no meio.


Figura 2 O programa PlaneProjectionExperimenter

As três propriedades cruciais PlaneProjection são RotationX, RotationY e RotationZ, que pode ser alterado usando três ScrollBars.Essas propriedades assumem um sistema de coordenadas 3D onde valores de X aumentam para a direita e valores de Y aumentam diminuindo a tela.(Essa opção é consistente com o sistema de coordenadas normal do Silverlight, mas não com típicos sistemas 3D, onde os valores de Y geralmente aumentam indo.) Valores cada vez maiores de Z parecem vir fora da tela para o visualizador.Essas três propriedades causar rotação em torno de três eixos.

As barras de rolagem para X e Y são posicionados perpendicularly para o eixo de rotação.Por exemplo, o ScrollBar vertical à esquerda gira a figura ao redor do eixo X, que é executado horizontalmente através do centro da carta.As partes superior e inferior da carta parecem Inverter na direção ou distante do visualizador.

Primeiro, é melhor tentar cada eixo de rotação independentemente dos outros e atualize o navegador entre experimentations.Você pode antecipar a direção de rotação usando a regra do lado direita.Aponta o polegar a direção do eixo positivo.(X, à direita; Y é pressionada; para Z, é na sua direção.) A curva que tornam os dedos outros indica a direção de rotação para ângulos de rotação positiva.Ângulos negativos giram na direção oposta.

Uma rotação composta depende a ordem na qual as rotações individuais são aplicadas.Usar PlaneProjection sacrifica alguma flexibilidade nesses rotações.PlaneProjection sempre aplica RotationX primeiro, em seguida, RotationY e finalmente RotationZ.Como podemos pode saber?Tente deixar RotationZ em 0 e manipular RotationX e RotationY.Você verá que RotationX sempre gira a carta ao redor do eixo horizontal da carta, enquanto RotationY gira a letra em torno do eixo vertical do janela, significando que RotationY é aplicado à letra já girada no ângulo RotationX.Agora, com RotationX e RotationY definido como qualquer coisa, manipular RotationZ.Esta rotação também é relativa à janela e não altera a aparência da letra todo.

Na vida real, você pode simplesmente definir uma propriedade de projeção e obter um resultado razoável.O texto em Figura 1 faz parte do projeto TaperText e exibida usando o seguinte XAML:

<TextBlock Text="TAPER"
           FontFamily="Arial Black"
           FontSize="144"
           HorizontalAlignment="Center"
           VerticalAlignment="Center">
    <TextBlock.Projection>
        <PlaneProjection RotationY="-60" />
    </TextBlock.Projection>
</TextBlock>

Como com RenderTransform, projeção não afeta o layout. O sistema de layout vê um elemento un-transformed e un-projected.

Obviamente, as propriedades RotationX, RotationY e RotationZ são todas apoiadas por propriedades de dependência, para que eles também podem se tornar destinos de animação.

O FlipPanel

Com apenas essa quantidade conhecer PlaneProjection, é possível codificar um “ flip painel, ” que é uma técnica para minimizar a superfície um aplicativo na tela, organizando controles na parte frontal e traseira de um painel (ou para que pareça). No WPF, um controle FlipPanel requer alternar entre 2D e 3D. No Silverlight, o FlipPanel se torna muito simples.

Projeto FlipPanelDemo inclui um controle FlipPanel derivado de UserControl. A parte de código mostrada na Figura 3 define duas novas propriedades chamadas Child1 e Filho2 do tipo UIElement e métodos públicos chamados Flip e FlipBack.

Figura 3 O arquivo FlipPanel.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;

namespace FlipPanelDemo
{
    public partial class FlipPanel : UserControl
    {
        public static readonly DependencyProperty Child1Property =
            DependencyProperty.Register("Child1",
                typeof(UIElement),
                typeof(FlipPanel),
                new PropertyMetadata(null, OnChild1Changed));

        public static readonly DependencyProperty Child2Property =
            DependencyProperty.Register("Child2",
                typeof(UIElement),
                typeof(FlipPanel),
                new PropertyMetadata(null, OnChild2Changed));

        public FlipPanel()
        {
            InitializeComponent();
        }

        public UIElement Child1
        {
            set { SetValue(Child1Property, value); }
            get { return (UIElement)GetValue(Child1Property); }
        }

        public UIElement Child2
        {
            set { SetValue(Child2Property, value); }
            get { return (UIElement)GetValue(Child2Property); }
        }

        public void Flip()
        {
            flipStoryboard.Begin();
        }

        public void FlipBack()
        {
            flipBackStoryboard.Begin();
        }

        static void OnChild1Changed(DependencyObject obj,
                        DependencyPropertyChangedEventArgs args)
        {
            (obj as FlipPanel).child1Container.Content = args.NewValue;
        }

        static void OnChild2Changed(DependencyObject obj,
                        DependencyPropertyChangedEventArgs args)
        {
            (obj as FlipPanel).child2Container.Content = args.NewValue;
        }
    }
}

A parte de XAML no Figura 4 mostra como Child1 e Filho2 ocupam transformações mesmas do projeção do espaço e ter aplicadas. A posição inicial, a transformação PlaneProjection para Filho2 tem RotationX definido como-90 graus, que significa que ele é em ângulo reto para o visualizador e é efetivamente invisível. As animações “ Inverter ” simplesmente swing Child1 fora do modo de exibição por Animando RotationX a 90; swing Filho2 em modo de exibição por Animando RotationX como 0. As animações “ Inverter volta ” reverter essas ações.

Figura 4 O arquivo FlipPanel.xaml

<UserControl x:Class="FlipPanelDemo.FlipPanel"
             xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Storyboard x:Name="flipStoryboard">
            <DoubleAnimation Storyboard.TargetName="planeProjection1"
                             Storyboard.TargetProperty="RotationX"
                             To="90"
                             Duration="0:0:0.5" />
            
            <DoubleAnimation Storyboard.TargetName="planeProjection2"
                             Storyboard.TargetProperty="RotationX"
                             To="0"
                             BeginTime="0:0:0.5"
                             Duration="0:0:0.5" />
        </Storyboard>

        <Storyboard x:Name="flipBackStoryboard">
            <DoubleAnimation Storyboard.TargetName="planeProjection1"
                             Storyboard.TargetProperty="RotationX"
                             To="0"
                             BeginTime="0:0:0.5"
                             Duration="0:0:0.5" />
            <DoubleAnimation Storyboard.TargetName="planeProjection2"
                             Storyboard.TargetProperty="RotationX"
                             To="-90"
                             Duration="0:0:0.5" />
        </Storyboard>

    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" Background="White">
        <ContentControl Name="child1Container">
            <ContentControl.Projection>
                <PlaneProjection x:Name="planeProjection1" />
            </ContentControl.Projection>
        </ContentControl>
        <ContentControl Name="child2Container">
            <ContentControl.Projection>
                <PlaneProjection x:Name="planeProjection2" 
                                 RotationX="-90" />
            </ContentControl.Projection>
        </ContentControl>
    </Grid>
</UserControl>

Normalmente, Child1 e Filho2 seriam definida como painéis de algum tipo cobertos com controles. No programa FlipPanelDemo, Child1 e Filho2 simplesmente têm diferentes cores e um único botão para disparar a inversão. A ação de inversão parece ser mais bom para o usuário de navegar para uma nova página, porque isso implica que um conjunto de controles não estão desaparecendo irremediavelmente, mas pode facilmente ser recuperado.

Centro de rotação

Rotação de todas as é relativo a um centro. Rotação em duas dimensões é relativo a um ponto. Rotação em três dimensões é relativo a uma linha no espaço 3D — conhecida como um “ eixo de rotação. ” Para conveniência — e talvez para evitar a introdução de linhas 3D e vetores 3D em Silverlight — a transformação de projeção é considerada em relação a um ponto 3D.

A classe PlaneProjection suporta alterando o Centro de rotação usando três propriedades:

  • CenterOfRotationX (coordenada relativa; padrão é 0,5)
  • CenterOfRotationY (coordenada relativa; padrão é 0,5)
  • CenterOfRotationZ (coordenadas absolutas; padrão é 0)

Let’s Observe os dois primeiros pela primeira vez. Como a propriedade RenderTransformOrigin, esses valores são relativas ao canto superior esquerdo do elemento que está sendo transformado. Diferem RenderTransformOrigin o valor padrão é o ponto (0, 0), enquanto os valores padrão para PlaneProjection causam rotações ser centralizado no elemento.

PlanProjectionExperimenter, você pode alterar CenterOfRotationX e CenterOfRotationY usando ponto azul. (Uma dica de ferramenta indica os valores atuais.) Você provavelmente perceberá imediatamente que CenterOfRotationX não afeta a rotação ao redor do eixo X e CenterOfRotationY não afeta a rotação ao redor do eixo Y.

Se você fizer CenterOfRotationX igual a 0, rotação ao redor do eixo Y é relativo para o lado esquerdo do elemento; da mesma forma, se você defini-la como 1, rotação é em torno do lado direito. Você pode tornar o valor menor que 0 ou maior que 1 para algumas variações muito grande. O elemento parece chegar muito perto do visualizador e ir muito longe.

Experimente isto: Verifique CenterOfRotationY aproximadamente igual a 0. Agora, defina RotationX igual a 90 graus ou thereabouts. Você observará que o elemento ainda está visível. FlipPanel, uma rotação de 90 graus ou -90 graus faz com que o elemento a ser invisível porque ele é visualizado em sua borda. Os cálculos matemáticos acontecendo dentro da classe PlaneProjection assumem olho do visualizador (ou a câmera metaphorical) sempre é alinhada no centro do elemento diretamente novamente procurando em uma direção Z negativo. Se algo está fora do centro e girado 90 graus, ainda será visível do centro do elemento.

O eixo CenterOfRotationZ é tratado diferentemente de X e Y. É em coordenadas absolutas do Silverlight em vez de coordenadas relativas e o padrão é 0. Em PlaneProjectionExperimenter, você pode alterar esse valor usando a roda do mouse. Ponto azul cresce e é reduzido para representar a alteração da propriedade.

Rotação com CenterOfRotationZ um não-padrão é provavelmente o mais difícil visualizar mentalmente, portanto, vale a pena o tempo para testar um pouco.

Presume-se que o elemento que está sendo projetado ficam no plano XY — ou seja, o plano no espaço 3D em que Z é igual a 0. Se você deixar a propriedade CenterOfRotationZ com seu valor padrão 0 e manipula RotationX ou RotationY, partes da carta obtém maiores como eles serão movidos ao espaço de Z positivos e partes ficam menores à medida que eles movem no espaço de Z negativo. Agora aumente CenterOfRotationZ para um valor de aproximadamente 200 e manipular RotationY. A letra obterá maior porque está girando ao redor do centro em que Z é igual a 200 de uma área de espaço em que Z é igual a 0 para a área em que Z é igual a 400. Quando você define CenterOfRotationZ para 500 ou superior, a solução inteira pára de funcionar bem, porque a matemática interna do PlaneProjection presumir que o visualizador (ou a câmera) está localizadas 1.000 unidades do plano XY. Com um centro de rotação de 500, o elemento é realmente 
being projetada atrás da câmera.

Painel de três carrossel

Nós pode tornar algo semelhante a um FlipPanel com três lados em vez de apenas dois? Sim, mas exigem um pouco pequeno de trigonometria e muito mexam em torno de índices Z.

Figura 5 mostra uma visão superior do imagina. Ele é um modo de exibição de uma posição negativa no eixo Y — acima do monitor, portanto, para falar — olhando para baixo. Linhas grossas representam três painéis. Na verdade, todos esses painéis são posicionados no mesmo ponto. Somente a transformação de projeção faz com que eles apareçam em locais diferentes. Aquele visível na frente não tem nenhum rotações aplicadas a ele. Os outros dois têm sido sujeitos a rotações onde RotationY é definida para 120 (para aquela à direita) e -120 graus (à esquerda). As duas rotações são centralizadas em um valor CenterOfRotationZ negativo, que corresponde ao ponto preto. O que é esse valor?

Figure 5 A Proposed Three-Panel Carousel
Figura 5 Um carrossel proposto do painel de 3

Se você se conectar a esse ponto com uma linha pontilhada para o vértice à direita, a linha pontilhada faz um ângulo de 30 graus com o eixo X. A tangente de 30 graus é 0.577. Que é a taxa da distância do ponto ao eixo X, dividido pela metade da largura do painel. Se o painel de 200 unidades de grande, esse ponto está no-57.7. Definida CenterOfRotationZ com esse valor.

Agora, para girar o carrossel de um painel para outro, basta anime RotationY para aumentá-la por 120 graus para todos os três painéis.

Bem, não exatamente. Embora nós estiver picturing esses painéis no espaço 3D, eles realmente ainda existem no espaço 2D e eles realmente estiver empilhados da outra. Como está girando, a propriedade Canvas.ZIndex conectados de cada um dos três painéis deve ser alterada para reorganizar os painéis. Isso é como elementos aparentemente podem ser movidos “ na frente do ” ou “ atrás ” de outros elementos.

Assuma Let’s rotação seja no sentido horário com base na Figura 5 diagrama. Para começar, o painel na frente deve ter um índice Z de 2 (primeiro plano), aquele à direita tem um índice Z de 1 e aquele à esquerda com um índice Z de 0 (plano de fundo). Inicie agora a rotação de 120 graus. Na metade — ou seja, quando cada painel foi giradas 60 graus e dois painéis são igualmente visíveis — índices Z devem ser alterados. O painel movendo para o modo de exibição deve ter seu índice Z definido como 2 e uma transferência para fora da exibição deve ter seu índice Z definida como 1.

Isso é implementado no projeto ThreePanelCarousel. Em uso real, é claro que os três painéis poderiam estar cobertos com controles; neste programa de demonstração, apenas tiverem diferentes cores e um botão. Eu deixou os painéis parcialmente transparente para que você possa ver o que está acontecendo. Figura 6 mostra os painéis em ação.

Figure 6 The ThreePanelCarousel Program as It’s Spinning
Figura 6 O programa ThreePanelCarousel como ele está girando

Você deve estar imaginando se é possível aplicar uma rotação de projeção único para o grupo composto de três painéis. Não, não é. Várias transformações de projeção não podem ser compostas em elementos filho.

Outros efeitos

Ao testar a propriedade de projeção, Don se esqueça sobre outros efeitos de “ aprimorar ” a ishness 3D do seu aplicativo. Uma pequena ajuda sempre a sombra e é fácil simular uma sombra — até mesmo uma sombra movendo — com um pincel em dégradé.

O programa AsTheDaysGoBy usa uma combinação de animações para simular os dias pilotando desativar um calendário de suporte diárias, como se em um filme antigo para sugerir a passagem do tempo. A página calendário tem sua propriedade projeção definida como um objeto PlaneProjection com as propriedades CenterOfRotationX e CenterOfRotationY definidas como 0. RotationX e RotationY são animadas para mover a página para a esquerda e para cima enquanto varre um pincel em dégradé animado a página. Finalmente, a página apenas parece desaparecer como sua propriedade Opacity é animado como 0. Figura 7 mostra o programa em ação.

Figure 7 The AsTheDaysGoBy Program
Figura 7 O programa AsTheDaysGoBy

A animação dura um segundo, para que o programa passa por 60 dias cada minuto, ou cerca de 10 anos por hora. Depois sobre um mês, o programa será alcançando o máximo valor de DateTime e terminará em breve com uma exceção. Até lá, aproveite.

Charles Petzold  é um editor colaborador antigo MSDN Magazine*.*Seu livro mais recente é “ O Turing Annotated: A Guided Tour pelo papel histórico de Alan Turing sobre Computability e a máquina de Turing ” (Wiley, 2008). Blogs Petzold no seu site da charlespetzold.com.