Zum Inhalt springen

Randschichten ​

Ein Graph, dessen Beziehungen alle gleich aussehen, ist nicht mehr lesbar, sobald er mehr als eine Art Beziehung enthält. Randschichten sind die drei Dinge, die Sie benötigen, um das zu beheben, und sie sollen zusammen verwendet werden:

  1. Nennen Sie die Art – render.edgeTypeAccessor.
  2. Style es auseinander – render.edgeStyleMap.
  3. Ausschalten – UI.filter.edgeFacets, angezeigt als Live-Schalter im Filterfeld und optional als Legendenabschnitt.
ts
const options = {
    render: {
        edgeTypeAccessor: (edge) => edge.getData()?.kind,
        edgeStyleMap: {
            reference:   { strokeColor: '#428bca' },
            correlation: { strokeColor: '#888888', dashed: true, animateDash: false },
            sighting:    { strokeColor: '#f39a1f', dashed: true, markerEnd: 'circle' },
        },
    },
    UI: {
        filter: { edgeFacets: [{ key: 'kind', label: 'Relationship' }] },
    },
}

Das Ausschalten einer Ebene verschiebt niemals das Diagramm

Dies ist die Eigenschaft, auf der die gesamte Funktion basiert – siehe Umschalten ist eine Linse. Layout, Auswahl und Kamera bleiben Stück für Stück identisch, es sei denn, Sie entscheiden sich dafür, unverbundene Knoten auszublenden.

Benennen und Stylen einer Art ​

edgeTypeAccessor und edgeStyleMap spiegeln nodeTypeAccessor / nodeStyleMap genau wider: Der Accessor gibt eine Zeichenfolge (oder undefined) zurück, und die Map wandelt diese Zeichenfolge in eine Partial<EdgeStyle> um, die über dem Standardkantenstil zusammengeführt wird.

ts
render: {
    edgeTypeAccessor: (edge) => edge.getData()?.kind,
    edgeStyleMap: {
        reference: { strokeColor: '#428bca', strokeWidth: 2 },
        contains:  { strokeColor: '#5cb85c', markerEnd: 'diamond' },
    },
}

Jede EdgeStyle-Eigenschaft ist Freiwild – strokeColor, strokeWidth, opacity, curveStyle, dashed, animateDash, markerEnd, markerStart, rotateLabel. Eine dashed-Kante animiert standardmäßig ihren Strich; Stellen Sie animateDash: false für einen Typ ein, der als ruhiger Hintergrundkontext gelesen werden soll.

Eine Kante ohne Art oder eine Art, die in der Karte nicht erwähnt wird, behält den Standardkantenstil bei. Nichts warnt: Ein ungestylter Stil ist eine legitime Wahl.

Vorrang ​

Die gleiche Reihenfolge wie bei den Knoten, die engsten gewinnen:

PrioritätWo
1 (gewinnt)das Eigene der Kante style.styleCb — siehe die Warnung unten
2die eigenen statischen style-Eigenschaften der Kante
3render.edgeStyleMap[kind]
4render.defaultEdgeStyle.styleCb
5render.defaultEdgeStyle Literale

Die beiden styleCb-Rollen sind nicht dasselbe, und es lohnt sich, sie klar zu betrachten. Beim own-Stil einer Kante ist es ein Override, der sofort gewinnt; auf render.defaultEdgeStyle ist es die berechnete Form des Standardschlitzes und füllt nur die nichts engere Menge aus – einschließlich dessen, was pro Kante styleCb weggelassen wurde. Spezifitätsreihenfolge, nicht Callback-Beats-Static.

Die eigene styleCb einer Kante ersetzt ihre gesamte Stufe

Wenn der eigene Stil einer Kante eine styleCb deklariert, ist ihr Rückgabewert die nur Quelle auf dieser Ebene: Die eigenen statischen Eigenschaften * und* edgeStyleMap der Kante werden beide übersprungen und nur defaultEdgeStyle füllt die Lücken. Eine styleCb, die { strokeWidth: 4 } an einer Kante zurückgibt, die auch strokeColor setzt, verliert also diese Farbe und auch die Farbe der Art. Wenn Sie einen Callback und den Stil der Art wünschen, führen Sie die Suche innerhalb des Callbacks durch oder setzen Sie den Callback auf render.defaultEdgeStyle, wo er stattdessen der Karte nachgibt.

Der Canvas-Renderer löst weniger auf

renderers/canvas berücksichtigt edgeStyleMap, liest aber nur vier der neun EdgeStyle-Eigenschaften. Der SVG-Renderer – die Standardeinstellung – löst sie alle auf.

Eine Art zu einer Schicht machen ​

Abgesehen von den verschiedenen Styling-Arten ist das nur die Hälfte davon. UI.filter.edgeFacets gibt an, welche Kantenmaße umschaltbar sind:

ts
UI: {
    filter: {
        edgeFacets: [{ key: 'kind', label: 'Relationship' }],
    },
}

{ key: 'kind' } ist eine vollständige Deklaration. Eine Kantenfacette hat kantenförmige Standardeinstellungen: Eine Ebene ist eine multiselect, und ihre Optionen werden von den unterschiedlichen Werten abgeleitet, die Ihre echten Kanten tatsächlich tragen, sodass sie niemals eine Auswahl bieten kann, die leer zurückbleiben würde.

Hier wird nichts jemals automatisch abgeleitet. Im Gegensatz zu Knotenfacetten, die auf das Scannen Ihrer Daten zurückgreifen, verhält sich ein Diagramm, das kein edgeFacets deklariert, genau wie eines, das noch nie von Ebenen gehört hat. Die Randsichtbarkeit ist zu folgenreich, als dass man sie erraten könnte.

Das Filterfeld erweitert einen Relationships-Abschnitt: eine Zeile pro Art, jede mit einem Linienmuster und einer Anzahl, wobei auf einmal angewendet wird – wie die Attributfelder darüber und die Legende daneben. Nichts in diesem Bereich wartet hinter einer Schaltfläche „Übernehmen“.

Facetten, die keine Ebenen sind ​

type akzeptiert das vollständige Facettenvokabular – text, regex, select, multiselect, numberRange, boolean – plus accessor und predicate für Dimensionen, die keine einfachen Daten sind Schlüssel:

ts
edgeFacets: [
    { key: 'kind', label: 'Relationship' },                    // a layer
    { key: 'weight', label: 'Confidence', type: 'numberRange' }, // a batch control
    {
        key: 'recent',
        label: 'Seen this week',
        type: 'boolean',
        accessor: (edge) => Date.now() - edge.getData().seenAt < 7 * 864e5,
    },
]

Nur die Multiselect-Art ist eine Live-Umschaltung. Alles andere ist eine Stapelsteuerung, daher wird es im Attributformular des Panels neben den Knotenfacetten gerendert, wo eine Schaltfläche „Übernehmen“ die richtige Option ist.

Optionen ​

OptionTypStandardwertWas es bewirkt
keystring—Identität filtern. setEdgeFilter(key, …) nimmt dies.
labelstringverschönert keyWird wörtlich verwendet, kann also aus einer Übersetzungsebene stammen.
typeFilterFacetType'multiselect'Eine Ebene besteht aus einer Reihe von Arten, jeweils ein- oder ausgeschaltet.
optionsarray | (graph) => arrayabgeleitetWird bei jedem Neuaufbau anhand des Live-Diagramms behoben.
matchModeFilterMatchMode'exact'
accessor(edge) => unknownedge.getData()[key]Was ermöglicht berechnete Facetten?
predicate(edge, value) => boolean—Volle Kontrolle; siegt über accessor / matchMode. Läuft pro Kante und Anwendung, also halten Sie es günstig.
ordernumberDeklarationsanordnungAnzeigereihenfolge im Panel.

Das Umschalten ist eine Linse, keine Layoutänderung ​

Das Ausschalten einer Ebene ändert welche Linien gezeichnet werden und sonst nichts. Knotenpositionen, die Auswahl und die Kamera bleiben Bit für Bit unverändert und die Simulation wird nicht neu gestartet.

Das ist Absicht, und genau das tun die professionellen Tools: vis-network trägt pro Kante hidden * und * ein separates pro Kante physics, Sigmas edgeReducer erreicht nie das Layout, Cytoscape und KeyLines re-layout nur, wenn sie dazu aufgefordert werden. Eine versteckte Beziehung ist eine Anzeigeentscheidung. Wenn das Ausblenden einer Korrelationsebene das Diagramm neu fließen ließ, konnten Sie den Schalter nicht zum Lesen des Diagramms verwenden, bei dem es sich um den gesamten Punkt handelt.

Das Einzige, was es verbiegt, ist Opt-in, und auf der Knotenseite: UI.filter.hideDisconnected verbirgt die Knoten einer Schichtstränge. Diese Knoten verlassen die Simulation, sodass sich das Diagramm neu einstellt – weshalb es standardmäßig deaktiviert ist und es sich um einen Knotenfilter handelt und nicht um etwas, das die Ebenensteuerung alleine ausführt.

Mechanisch ließ Edge eine zweite Flagge wachsen:

EigenschaftBedeutung
layerVisibleOb die Ebene dieser Kante aktiviert ist. Ein Veto über visible.
visibleIgnoringLayerWas visible wäre, wenn jede Ebene aktiviert wäre: allein der Endpunkt und die Gründe für das manuelle Ausblenden.
setLayerVisible(on)Wechseln Sie die Ebene und leiten Sie visible neu ab. Gibt zurück, ob sich etwas geändert hat.
representedEdgesAls Stellvertreter: die echten Vorteile, für die es spricht. Undefiniert an einer echten Kante.

Die Verbindung erzwingt Gates auf visibleIgnoringLayer, sodass eine Kante, deren Ebene ausgeschaltet ist, ihre Endpunkte weiterhin zusammenzieht. visible wird auch von der Endpunktfilterung und von hideNode / showNode geschrieben, weshalb der Layer-Status davon getrennt lebt.

Ein Knoten ohne sichtbare Kanten bleibt sichtbar ​

Es zu verbergen wäre eine Knoten-Filterentscheidung, und eine Kantenfacette trifft niemals eine solche. Verwenden Sie das Filterfeld oder queryEngine.setFilter, wenn Sie das möchten.

Ein leerer Pick bedeutet, dass jede Schicht entfernt ist ​

Für einen Knoten mit Mehrfachauswahl ist eine leere Liste so, wie das Panel unset schreibt – keine Einschränkung. Eine Edge-Layer-Steuerung schreibt genau, was eingeschaltet bleibt. Eine geleerte Layer-Liste bedeutet also, dass jeder Layer ausgeschaltet ist. Es gibt nichts anderes, was es bedeuten könnte, und deshalb gibt es für einen Randabschnitt keine Beschränkung, dass der letzte Abschnitt nicht ausgeschaltet werden kann.

Eingeben der Ebenen in der Legende ​

Ein legend-Abschnitt, der scope: 'edge' deklariert, listet die Arten mit einem line-Farbfeld auf – Strichfarbe, Strich und Markierung, wie sie vom Renderer aufgelöst wurden – und befindet sich neben Abschnitten mit Knotenbereich in derselben angedockten Karte:

ts
UI: {
    legend: {
        sections: [
            { key: 'type', title: 'Element' },                     // nodes
            { key: 'kind', title: 'Relationship', scope: 'edge' },  // edges
        ],
    },
}
  • Bei einer passenden edgeFacets-Deklaration steuert der Abschnitt ** diese Facette** – das Panel und die Legende werden zu zwei Ansichten eines Filters, die jeweils aufeinander folgen.
  • Ohne sie behält sie eine eigene Facette und filtert dennoch, sodass eine Legende niemals nur dekorativ ist.
  • Ein Abschnitt ohne key oder entries verwendet render.edgeTypeAccessor, genau wie ein Knotenabschnitt auf nodeTypeAccessor zurückgreift.
  • Die Zählungen umfassen echte Kanten: Die Stellvertreter eines kollabierten Clusters werden ausgeschlossen, sodass eine Zählung niemals eine Beziehung doppelt meldet.

Farbfeldfarben werden abgetastet von dem, was der Renderer gemalt hat, sodass eine Art, die in mehr als einer Farbe gerendert wird, die erste behält und warnt. Deklarieren Sie entries mit Ihrem eigenen color, wenn die Arten nicht dem entsprechen, was die Striche kodieren.

Zusammengebrochene Cluster ​

Während ein Cluster geschlossen ist, wird stattdessen eine Kante mit einem Ende darin in beliebiger Tiefe zum Cluster gezeichnet. Kanten, deren Enden auf denselben zwei Knoten auf dem Bildschirm landen, teilen sich eine Linie, eine Ersatz, sodass eine Linie für mehrere reale Beziehungen unterschiedlicher Art sprechen kann. Stellvertreter zeichnen nur: getEdges() und die Registerkarte „Kanten“ der Tabelle listet echte Kanten auf.

Jeder Stellvertreter trägt daher representedEdges und überlebt, während irgendeine von ihnen den Filter passiert. Zwei wissenswerte Konsequenzen:

  • Durch das Ausblenden einer Ebene wird ein Ersatz nur dann ausgeblendet, wenn alle dahinter liegenden Beziehungen ausgeblendet sind. Eine Linie, die sowohl für eine reference als auch für eine correlation steht, bleibt bestehen, solange eine davon aktiviert ist.
  • Jeder Facettentyp reicht innerhalb eines reduzierten Clusters – einer numberRange oder einer predicate, nicht nur einer Liste von Arten –, da die Facette mit den echten Kanten abgeglichen wird und nicht mit den eigenen (leeren) Daten des Stellvertreters.

Ein Ersatz für eine einzelne Kante sieht wie diese gepunktete Kante aus, und wenn Sie darauf klicken, wird die Kante ausgewählt. Ein Ersatz für mehrere ist einfach, und wenn Sie ihn auswählen, wird angezeigt, für wie viele Kanten er steht.

Fahren Sie es aus dem Code ​

Kantenfacetten werden im Konstruktor registriert, sodass die programmatische Filterung in jedem Modus funktioniert – nur das Panel ist im full-Modus Chrome.

ts
graph.queryEngine.setEdgeFilter('kind', { value: ['reference'], matchMode: 'exact' })
graph.queryEngine.getHiddenEdgeCount()      // 12
graph.queryEngine.removeEdgeFilter('kind')  // every layer back on
MethodeWas es bewirkt
setEdgeFilter(key, config)Schreiben Sie einen Kantenfilter.
getEdgeFilters()Die aktiven Kantenfilter ohne Präfix.
removeEdgeFilter(key)Lassen Sie eine weg – der Ebenensatz wird wieder angezeigt.
getHiddenEdgeCount()Wie viele Kanten werden von einer Ebene verdeckt?
getEdgeFacetValues(key)Die unterschiedlichen Werte, jeweils mit einer Anzahl und einer Beispielkante.
setEdgeFacets(facets)Ersetzen Sie den deklarierten Satz.
registerEdgeFacet(facet)Fügen Sie einen additiv hinzu – was ein Legendenabschnitt verwendet, um seinen eigenen zu reservieren.
replaceFilters(ownedKeys, filters)Ersetzen Sie nur die Schlüssel, die Sie besitzen, und lassen Sie andere in Ruhe.

Knoten- und Kantenfilter teilen sich einen GraphFilters-Datensatz, wobei Kantenschlüssel unter einem edge:-Präfix (EDGE_FILTER_PREFIX, exportiert aus GraphQueryEngine) geführt werden, sodass ein Schlüsselname gleichzeitig eine Knotenfacette und eine Kantenfacette sein kann. Jede der oben genannten Methoden verbirgt das Präfix. Sie treffen es nur, wenn Sie den Rohdatensatz lesen.

renderer.getEdgeStyle(edge) ist öffentlich und gibt den Stil zurück, den der Renderer tatsächlich aufgelöst hat – derselbe Aufruf, auf dem die Farbfelder der Legende basieren, und die Möglichkeit, in einem Test sicherzustellen, dass das, was Sie deklariert haben, auch das ist, was gemalt wird.

Siehe auch ​

  • Randebenen-Galeriekarte – alle drei Oberflächen auf einer Arbeitsfläche
  • Renderoptionen – die Knotenhälfte des Accessor/Map-Paares
  • Filter – Knotenfacetten und Wiederverwendung der Vokabularkantenfacetten
  • Legende – Abschnitte und was ein Muster wissen kann und was nicht