Main Content

Gestión de propiedades de clases de gráficas

Al desarrollar una gráfica personalizada como una subclase de la clase básica ChartContainer, se pueden utilizar determinadas técnicas para hacer que el código sea más sólido y eficiente, y se ajuste mejor a las necesidades de los usuarios. Estas técnicas se centran en cómo definir y gestionar las propiedades de una clase. Utilice cualquiera que sea útil para el tipo de visualización que desee crear y la experiencia de usuario que desee ofrecer.

Inicializar los valores de las propiedades

Asigne valores predeterminados a todas las propiedades públicas de la clase. Al hacerlo, se configura una gráfica válida si el usuario omite algunos de los argumentos de par nombre-valor cuando llama al método constructor.

En cuanto a las propiedades que almacenan datos de coordenadas, defina los valores iniciales en valores NaN o arreglos vacíos, de manera que la gráfica predeterminada esté vacía cuando el usuario no especifique las coordenadas. Seleccione las coordenadas predeterminadas según los requisitos de las funciones de representación a las que tenga previsto llamar en los métodos de la clase. Para obtener información sobre los requisitos, consulte la documentación de las funciones de representación que tenga previsto utilizar.

Validar los valores de las propiedades

Se recomienda comprobar los valores de las propiedades de la clase antes de que el código utilice dichos valores. Una manera cómoda de hacerlo es validar el tamaño y la clase de las propiedades según las vaya definiendo. Por ejemplo, este bloque de propiedades valida el tamaño y la clase de cuatro propiedades.

properties
    IsoValue (1,1) double = 0.5
    Enclose {mustBeMember(Enclose,{'above','below'})} = 'below'
    CapVisible (1,1) matlab.lang.OnOffSwitchState = 'on'
    Color (1,3) double {mustBeGreaterThanOrEqual(Color,0),...
        mustBeLessThanOrEqual(Color,1)} = [.2 .5 .8]
end

  • IsoValue debe ser un arreglo 1 por 1 de clase double.

  • Enclose debe contar con un valor de 'above' o 'below'.

  • CapVisible debe ser un arreglo 1 por 1 de clase matlab.lang.OnOffSwitchState.

  • Color debe ser un arreglo 1 por 3 de clase double, en el que cada valor se encuentre en el intervalo [0,1].

También se pueden validar las propiedades que almacenan los objetos de gráficas subyacentes de la gráfica. Para determinar el nombre de clase de un objeto, llame a la función de representación correspondiente en la línea de comandos y, a continuación, llame a la función class para obtener el nombre de clase. Por ejemplo, si prevé llamar a la función patch en el método setup, llame a la función patch en la línea de comandos con un argumento de salida (los argumentos de entrada no importan). A continuación, pase el resultado a la función class para obtener su nombre de clase.

x = patch(NaN,NaN,NaN);
class(x)
ans =

    'matlab.graphics.primitive.Patch'

Utilice el resultado de la función class para validar la clase para la propiedad correspondiente en la clase. Por ejemplo, cada una de las siguientes propiedades almacena un objeto Patch.

properties (Access = private,Transient,NonCopyable)
    IsoPatch (1,1) matlab.graphics.primitive.Patch
     CapPatch (1,1) matlab.graphics.primitive.Patch
end

De manera ocasional, podría querer definir una propiedad que pueda almacenar distintas formas y clases de valores. Por ejemplo, si define una propiedad que pueda almacenar un vectores de caracteres, un arreglo de celdas de vectores de caracteres o un arreglo de cadenas, omita la validación del tamaño y la clase o utilice un método de validación de propiedad personalizado.

Para obtener más información acerca de cómo validar propiedades, consulte Validate Property Values.

Personalizar la visualización de propiedades

Una de las ventajas de definir la gráfica como una subclase de la clase básica ChartContainer es que también hereda de la clase matlab.mixin.CustomDisplay. De esta manera, se puede personalizar la lista de propiedades que MATLAB® muestra en la ventana de comandos cuando se hace referencia a la gráfica sin un punto y coma. Para personalizar la visualización de propiedades, sobrecargue el método getPropertyGroups. En dicho método, se pueden personalizar las propiedades que se enumeran y el orden de la lista. Por ejemplo, considere una clase IsoSurfCapChart que cuente con las siguientes propiedades públicas.

properties
    IsoValue (1,1) double = 0.5
    Enclose {mustBeMember(Enclose,{'above','below'})} = 'below'
    CapVisible (1,1) matlab.lang.OnOffSwitchState = 'on'
    Color (1,3) double {mustBeGreaterThanOrEqual(Color,0),...
        mustBeLessThanOrEqual(Color,1)} = [.2 .5 .8]
end

El siguiente método getPropertyGroups especifica la lista de propiedades de objetos escalares como Color, IsoValue, Enclose y CapVisible.

function propgrp = getPropertyGroups(obj)
    if ~isscalar(obj)
        % List for array of objects
        propgrp = getPropertyGroups@matlab.mixin.CustomDisplay(obj);    
    else
        % List for scalar object
        propList = {'Color','IsoValue','Enclose','CapVisible'};
        propgrp = matlab.mixin.util.PropertyGroup(propList);
    end
end

Cuando el usuario hace referencia a una instancia de esta gráfica sin un punto y coma, MATLAB muestra la lista personalizada.

c = IsoSurfCapChart
c = 

  IsoSurfCapChart with properties:

            Color: [0.2000 0.5000 0.8000]
         IsoValue: 0.5000
          Enclose: 'below'
       CapVisible: on

Para obtener más información acerca de cómo personalizar la visualización de propiedades, consulte Customize Property Display.

Optimizar el método update

En la mayoría de los casos, el método update de la clase reconfigura todos los aspectos relevantes de la gráfica que dependen de las propiedades públicas. A veces, la reconfiguración incluye un cálculo costoso que requiere mucho tiempo. Si el cálculo incluye solo un subconjunto de las propiedades, se puede diseñar la clase para que ejecute dicho código solo cuando sea necesario.

Una manera de optimizar el método update es añadir estos componentes a la clase:

  • Defina una propiedad privada denominada ExpensivePropChanged que acepte un valor logical. Esta propiedad indica si ha cambiado alguna de las propiedades utilizadas en el cálculo costoso.

  • Escriba un método set para cada propiedad implicada en el cálculo costoso. En cada método set, establezca la propiedad ExpensivePropChanged en true.

  • Escriba un método protegido que realice el cálculo costoso.

  • Escriba una instrucción condicional en el método update que compruebe el valor de ExpensivePropChanged. Si el valor es true, ejecute el método que realice el cálculo costoso.

El siguiente código ofrece una implementación simplificada de este diseño.

classdef OptimizedChart < matlab.graphics.chartcontainer.ChartContainer
    
    properties
        Prop1
        Prop2
    end
    properties(Access=private,Transient,NonCopyable)
        ExpensivePropChanged (1,1) logical = true
    end
    
    methods(Access = protected)
        function setup(obj)
            % Configure chart
            % ...
        end
        function update( obj )
            % Perform expensive computation if needed
            if obj.ExpensivePropChanged
                doExpensiveCalculation(obj);
                obj.ExpensivePropChanged = false;
            end
            
            % Update other aspects of chart
            % ...
        end
        function doExpensiveCalculation(obj)
            % Expensive code
            % ...
        end
    end
    
    methods
        function set.Prop2(obj,val)
            obj.Prop2 = val;
            obj.ExpensivePropChanged = true;
        end
    end
end
En este caso, Prop2 está implicado en el cálculo costoso. El método set.Prop2 establece el valor de Prop2 y, a continuación, establece ExpensivePropChanged en true. Por lo tanto, la próxima vez que el método update se ejecute, llama a doExpensiveCalculation únicamente si ExpensivePropChanged es true. A continuación, el método update sigue actualizando otros aspectos de la gráfica.

Ejemplo: Gráfica de isosuperficie optimizada con visualización de propiedades personalizada

Defina una clase de IsoSurfCapChart para mostrar una isosurface con sus correspondientes isocaps. Incluye las siguientes funcionalidades:

  • Propiedades que utilizan validación de clase y tamaño

  • Una visualización de propiedades personalizada

  • Un método update optimizado que recalcula isosurface e isocaps únicamente si una o varias de las propiedades relevantes han cambiado

Para definir esta clase, cree un archivo de programa denominado IsoSurfCapChart.m en una carpeta que se encuentre en la ruta de MATLAB. A continuación, implemente la clase siguiendo los pasos que aparecen en la tabla.

PasoImplementación

Derive la clase a partir de la clase básica ChartContainer.

classdef IsoSurfCapChart < matlab.graphics.chartcontainer.ChartContainer

Defina las propiedades públicas mediante la validación de tamaño y clase.

  • VolumeData, IsoValue y Color son parámetros de isosurface.

  • Enclose, WhichCapPlane y CapVisible son parámetros de isocaps.

    properties
        VolumeData double = rand(25,25,25)
        IsoValue (1,1) double = 0.5
        Enclose {mustBeMember(Enclose,{'above','below'})} = 'below'
        WhichCapPlane {mustBeMember(WhichCapPlane,{'all','xmin',...
            'xmax','ymin','ymax','zmin','zmax'})} = 'all'
        CapVisible (1,1) matlab.lang.OnOffSwitchState = 'on'
        Color (1,3) double {mustBeGreaterThanOrEqual(Color,0),...
            mustBeLessThanOrEqual(Color,1)} = [.2 .5 .8]
    end

Defina las propiedades privadas.

  • IsoPatch y CapPatch almacenan los objetos Patch de isosurface e isocaps.

  • SmoothData almacena una versión reducida de los datos volumétricos.

  • ExpensivePropChanged indica si el método de actualización tiene que recalcular isosurface e isocaps.

    properties(Access = private,Transient,NonCopyable)
        IsoPatch (1,1) matlab.graphics.primitive.Patch
        CapPatch (1,1) matlab.graphics.primitive.Patch
        SmoothData double = [];
        ExpensivePropChanged (1,1) logical = true
    end

Implemente el método setup. En este caso, llame a la función patch dos veces para crear los objetos Patch de isosurface e isocaps. Almacene los objetos en las propiedades correspondientes y configure los ejes.

    methods(Access = protected)
        function setup(obj)
            ax = getAxes(obj);
            
            % Create two Patch objects
            obj.IsoPatch = patch(ax,NaN,NaN,NaN, 'EdgeColor', 'none', ...
                'FaceColor',[.2 .5 .8],'FaceAlpha',0.9);
            hold(ax,'on');
            obj.CapPatch = patch(ax,NaN,NaN,NaN,'EdgeColor', 'none', ...
                'FaceColor','interp');
            
            % Configure the axes
            view(ax,3)
            camlight(ax, 'infinite');
            camlight(ax,'left');
            lighting(ax, 'gouraud');
            hold(ax,'off');
        end

Implemente el método update. Decida si va a llamar al método doExpensiveCalculation mediante una prueba del valor de ExpensivePropChanged. A continuación, siga actualizando los otros aspectos (menos costosos) de la gráfica.

        function update(obj)
            % Perform expensive computation if needed
            if obj.ExpensivePropChanged
                doExpensiveCalculation(obj);
                obj.ExpensivePropChanged = false;
            end
            
            % Update visibility of CapPatch and update color
            obj.CapPatch.Visible = obj.CapVisible;
            obj.IsoPatch.FaceColor = obj.Color;
        end

Implemente el método doExpensiveCalculation, que reduce los datos volumétricos y recalcula las caras y los vértices de isosurface e isocaps.

        function doExpensiveCalculation(obj)
            % Update isosurface
            obj.SmoothData = smooth3(obj.VolumeData,'box',7);
            [F,V] = isosurface(obj.SmoothData, obj.IsoValue);
            set(obj.IsoPatch,'Faces',F,'Vertices',V);
            isonormals(obj.SmoothData,obj.IsoPatch);
            
            % Update isocaps
            [m,n,p] = size(obj.SmoothData);
            [Xc,Yc,Zc] = meshgrid(1:n,1:m,1:p);
            [Fc,Vc,Cc] = isocaps(Xc,Yc,Zc,obj.SmoothData,obj.IsoValue,...
                obj.Enclose,obj.WhichCapPlane);
            set(obj.CapPatch,'Faces',Fc,'Vertices',Vc,'CData',Cc);
        end

Implemente el método getPropertyGroups para personalizar la visualización de propiedades.

        function propgrp = getPropertyGroups(obj)
            if ~isscalar(obj)
                % List for array of objects
                propgrp = getPropertyGroups@matlab.mixin.CustomDisplay(obj);
                
            else
                % List for scalar object
                propList = {'Color','IsoValue','Enclose','CapVisible',...
                    'WhichCapPlane','VolumeData'};
                propgrp = matlab.mixin.util.PropertyGroup(propList);
            end
        end
    end

Implemente los métodos set de cada propiedad costosa (VolumeData, IsoValue y Enclose). En cada método, establezca el valor de propiedad correspondiente y, a continuación, establezca ExpensivePropChanged en true.

    methods
        function set.VolumeData(obj,val)
            obj.VolumeData = val;
            obj.ExpensivePropChanged = true;
        end
        function set.IsoValue(obj, val)
            obj.IsoValue = val;
            obj.ExpensivePropChanged = true;
        end
        function set.Enclose(obj, val)
            obj.Enclose = val;
            obj.ExpensivePropChanged = true;
        end
    end
end

A continuación, cree un arreglo de datos volumétricos y una instancia de IsoSurfCapChart.

[X,Y,Z] = meshgrid(-2:0.1:2);
v = (1/9)*X.^2 + (1/16)*Y.^2 + Z.^2;
c = IsoSurfCapChart('VolumeData',v,'IsoValue',0.5)
c = 

  IsoSurfCapChart with properties:

            Color: [0.2000 0.5000 0.8000]
         IsoValue: 0.5000
          Enclose: 'below'
       CapVisible: on
    WhichCapPlane: 'all'
       VolumeData: [41×41×41 double]

Cambie el color de c y oculte isocaps.

c.Color = [1 0.60 0];
c.CapVisible = false;

Consulte también

Clases

Funciones

Temas relacionados