Ir al contenido
Pivotick
Main Navigation Inicio
Primeros pasos
Configuración
Documentación generada de la API
GaleríaProyectos¡Pruébalo!

Español

English
简体中文
Français
Deutsch
Português
Nederlands
日本語
العربية

Español

English
简体中文
Français
Deutsch
Português
Nederlands
日本語
العربية

Apariencia

Sidebar Navigation

Primeros pasos

Proyectos que usan Pivotick

Configuración

Funciones de retorno

Capas de aristas

Simplificar

Distribución

Renderizado

Simulación

Personalizar la interfaz

Barra lateral

Información emergente

Menú contextual

Filtros

Leyenda

Tabla de datos

Estilos de la interfaz

Traducir la interfaz

Complementos

API de Pivotick

Pivotes y enriquecimiento

Guardar resultados de pivotes

Deshacer e historial

Seguridad

Documentación de la API

Galería

Fundamentos

Estilos de nodos

Estilos de aristas

Distribuciones

Eventos y funciones de retorno

Personalización de la interfaz

Edición y creación

Notas y anotaciones

Filtros, búsqueda y jerarquía

Control programático

Temas y rendimiento

Pivotes y enriquecimiento

Ejemplos destacados

Capas de borde ​

Este gráfico lleva cuatro tipos de relación a la vez: servicios call entre sí, un base de datos replicates, un centro de monitoreo monitors la mitad del patrimonio y una tubería registra lo que era deployed-with qué. Dibujados iguales serían una bola de pelo. dibujado aparte, siguen siendo una bola de pelo, porque el gráfico de llamadas que viniste a leer está enterrado bajo dos capas de contexto.

Las capas son los tres pasos que solucionan este problema y deben usarse juntos:

  1. Nombra el tipo — render.edgeTypeAccessor.
  2. Dale estilo — render.edgeStyleMap.
  3. Apágalo — UI.filter.edgeFacets.

Pruébelo: abra Filtrar gráfico y use las filas Relaciones, o haga clic en Relación muestras en la leyenda: son el mismo filtro visto dos veces. girar desactiva monitors y deployed-with y el gráfico de llamadas de repente es legible.

Observa los nodos mientras lo haces. No se mueven. Cambiar una capa cambia qué líneas se dibujan y nada más: el enlace fuerza las puertas en una bandera separada, por lo que un borde cuya capa está apagada continúa juntando sus puntos finales. Diseño, selección y la cámara vuelven bit a bit idénticas. Si al ocultar una capa el gráfico volvía a fluir no podías usar el interruptor para leer el gráfico, que es el punto.

Dos comportamientos que vale la pena provocar:

  • Apagar monitors. Prometheus ahora no tiene ningún borde visible y permanece en el lienzo. Ocultarlo sería una decisión de filtro de nodos y una faceta de borde nunca toma uno.
  • Desactiva cada fila. El gráfico mantiene los diez nodos y no dibuja líneas. para un capa de borde una selección vacía significa cada capa fuera; para un nodo seleccione múltiple un vacío elegir significa sin restricción. El control escribe lo que queda, así que no hay nada. más podría significar.

La leyenda apila una sección de nodo y una sección de borde en una tarjeta: type impulsa el colores de los nodos, kind impulsa los trazos y las muestras de la sección del borde son líneas: color, guión y marcador tomados de lo que realmente pintó el renderizador. Nunca asigna un estilo; informa uno.

Los botones controlan queryEngine.setEdgeFilter directamente: el panel y la leyenda se vuelve a encender para que coincida, porque solo hay un filtro. Las facetas del borde son registrado cualquiera que sea el modo UI, por lo que esto funciona en viewer y static también, donde no hay ningún panel en el que hacer clic.

Consulte Capas de borde para ver el conjunto de opciones completo: el vocabulario de facetas más allá de una capa simple, cómo se filtran los grupos colapsados y la precedencia de estilo edgeStyleMap se sienta.

js
// import { ColorPaletteMapper } from 'pivotick'

// Node colour keys on `type`; edge style keys on `kind`. Two independent
// encodings, which is why the legend below carries a section for each.
const palette = new ColorPaletteMapper('okabe-ito')

const options = {
    render: {
        // 1. Name the kind. Mirrors `nodeTypeAccessor` exactly.
        edgeTypeAccessor: (edge) => edge.getData()?.kind,
        // 2. Style it apart. Each value is a partial EdgeStyle merged over
        //    `defaultEdgeStyle`, so a kind only states what makes it different.
        edgeStyleMap: {
            calls: { strokeColor: '#0072B2', strokeWidth: 2.5 },
            replicates: { strokeColor: '#009E73', strokeWidth: 2.5, markerEnd: 'diamond' },
            // `animateDash: false` keeps the two context layers calm. A dashed edge
            // animates its dash by default, which is a lot of motion for something
            // you are meant to read past.
            monitors: { strokeColor: '#999999', strokeWidth: 1.5, dashed: true, animateDash: false },
            'deployed-with': { strokeColor: '#E69F00', strokeWidth: 1.5, dashed: true, animateDash: false }
        },
        defaultNodeStyle: {
            size: 18,
            strokeColor: '#ffffff',
            textColor: '#334155',
            text: (node) => node.getData()?.name,
            // 1.6 addition: draw the whole label instead of middle-ellipsising it.
            textTruncate: false,
            textVerticalShift: -2.2,
            color: (node) => palette.getColor(node.getData()?.type)
        }
    },
    // Not part of the feature — just room. Ten labelled nodes with four relation
    // kinds between them need more space than `Auto` gives a graph this small,
    // and naming any of these knobs opts the graph out of auto tuning.
    simulation: {
        d3LinkDistance: 115,
        d3ManyBodyStrength: -1100,
        d3CollideRadiusMultiplier: 3
    },
    UI: {
        // Layers need the filter panel and the legend, both of which are `full`-mode
        // chrome. The facets themselves are registered whatever the mode, so
        // `queryEngine.setEdgeFilter` works in `viewer` and `static` too.
        mode: 'full',
        // Off: `full` mode also brings a minimap and the data dock, and this card is
        // about neither.
        minimap: false,
        table: false,
        filter: {
            // 3. Make the kind a layer. `{ key: 'kind' }` alone would do — the label
            //    and the options are both derived. Nothing about edge facets is ever
            //    auto-derived, though: declare none and layers don't exist.
            edgeFacets: [{ key: 'kind', label: 'Relationship' }]
        },
        legend: {
            // Top-left: a two-section card is tall enough to reach the mode rail in
            // the bottom-left corner.
            position: 'top-left',
            sections: [
                { key: 'type', title: 'Service' },
                // `scope: 'edge'` keys on edges and draws a *line* swatch — stroke
                // colour, dash and marker as the renderer resolved them. It adopts
                // the `kind` facet declared above, so this section and the panel's
                // Relationships toggles are two views of one filter.
                { key: 'kind', title: 'Relationship', scope: 'edge' }
            ]
        }
    }
}
js
// Layers are filters, so they are drivable from code in any mode. `value` is the
// list of kinds that stay *on*.
function callsOnly(graph) {
    graph.queryEngine.setEdgeFilter('kind', { value: ['calls'], matchMode: 'exact' })
}

// Everything except the two context layers.
function hideContext(graph) {
    graph.queryEngine.setEdgeFilter('kind', { value: ['calls', 'replicates'], matchMode: 'exact' })
}

// Removing the filter is how every layer comes back on. Writing an empty list
// would mean the opposite — every layer off.
function showAllLayers(graph) {
    graph.queryEngine.removeEdgeFilter('kind')
}

// How many edges the active layers are hiding, for a status line of your own.
function reportHidden(graph) {
    console.log(`${graph.queryEngine.getHiddenEdgeCount()} edges hidden by layer`)
}
js
// A service graph carrying four *kinds* of relation at once. `calls` is the one
// people come for; the other three are context that buries it — which is the
// problem layers solve.
const data = {
    nodes: [
        { id: 'web-1', data: { name: 'Web app', type: 'web' } },
        { id: 'web-2', data: { name: 'Mobile web', type: 'web' } },
        { id: 'api-1', data: { name: 'Auth API', type: 'api' } },
        { id: 'api-2', data: { name: 'Orders API', type: 'api' } },
        { id: 'api-3', data: { name: 'Billing API', type: 'api' } },
        { id: 'db-1', data: { name: 'Postgres', type: 'database' } },
        { id: 'db-2', data: { name: 'Replica', type: 'database' } },
        { id: 'cache-1', data: { name: 'Redis', type: 'cache' } },
        { id: 'mon-1', data: { name: 'Metrics', type: 'ops' } },
        { id: 'ci-1', data: { name: 'Deploys', type: 'ops' } }
    ],
    edges: [
        // The call graph — the structure everything else hangs off.
        { from: 'web-1', to: 'api-1', data: { kind: 'calls' } },
        { from: 'web-1', to: 'api-2', data: { kind: 'calls' } },
        { from: 'web-2', to: 'api-1', data: { kind: 'calls' } },
        { from: 'api-2', to: 'api-3', data: { kind: 'calls' } },
        { from: 'api-1', to: 'cache-1', data: { kind: 'calls' } },
        { from: 'api-2', to: 'db-1', data: { kind: 'calls' } },
        { from: 'api-3', to: 'db-1', data: { kind: 'calls' } },
        // Data movement.
        { from: 'db-1', to: 'db-2', data: { kind: 'replicates' } },
        // Observability: one hub wired to everything, which is exactly what makes
        // the call graph hard to read while this layer is on.
        { from: 'mon-1', to: 'api-1', data: { kind: 'monitors' } },
        { from: 'mon-1', to: 'api-2', data: { kind: 'monitors' } },
        { from: 'mon-1', to: 'db-1', data: { kind: 'monitors' } },
        { from: 'mon-1', to: 'cache-1', data: { kind: 'monitors' } },
        // Co-location — inferred, not structural.
        { from: 'ci-1', to: 'api-1', data: { kind: 'deployed-with' } },
        { from: 'ci-1', to: 'api-2', data: { kind: 'deployed-with' } },
        { from: 'ci-1', to: 'api-3', data: { kind: 'deployed-with' } }
    ]
}
Pager
Página siguientePrimeros pasos