Naar inhoud

Grafiekindeling ​

Pivotick ondersteunt meerdere lay-outstrategieën voor het positioneren van knooppunten. U kunt de lay-out configureren via de optie layout, of via simulation.layout; als beide zijn ingesteld, wint layout.

OptieTypStandaardBeschrijving
type'force' | 'tree' | 'structured''force'Het lay-outalgoritme dat moet worden gebruikt. 'force' past een op fysica gebaseerde krachtindeling toe. 'tree' rangschikt knooppunten in een boomstructuur. 'structured' tekent een kleine grafiek zoals een persoon dat zou doen, zie Gestructureerde lay-out.

Opties voor boomindeling ​

Wanneer type: 'tree' is geselecteerd, zijn de volgende extra opties beschikbaar:

OptieTypStandaardBeschrijving
rootIdstringundefinedGeef de ID op van het knooppunt dat als root moet worden gebruikt. Indien niet opgegeven, zal Pivotick automatisch een wortel selecteren op basis van rootIdAlgorithmFinder.
rootIdAlgorithmFinder-MaxReachabilityAlgoritme dat wordt gebruikt om automatisch het hoofdknooppunt te vinden.
parentKeystringundefinedNaam van de node.data-sleutel met de id van het bovenliggende knooppunt van het knooppunt. Onbewust komt ouderschap van de randen. Zie De hiërarchie declareren.
depthKeystringundefinedNaam van de node.data-sleutel die de rij bevat waarop het knooppunt zich bevindt, geteld vanaf 0. Niet ingesteld: een knooppunt bevindt zich één rij onder het bovenliggende knooppunt.
strengthnumber0.1De krachtkracht om de boomstructuur te behouden.
horizontalbooleanfalseRangschik de knooppunten horizontaal in plaats van verticaal.
radialbooleanfalsePlaats knooppunten in een radiale lay-out in plaats van verticaal.
radialGapnumber750Ruimte tussen lagen in radiale lay-out.
spacing'auto' | 'manual''auto'Of de twee onderstaande vermenigvuldigers zich afstemmen op de grootte van de knooppunten en de vorm van de boom. Het instellen van een van beide impliceert expliciet 'manual'.
levelSpacingnumber1Vermenigvuldigt de afstand tussen opeenvolgende niveaus (tussen ringen, wanneer radial). 1 past de boom op het canvas, zoals voorheen.
siblingSpacingnumber1Vermenigvuldigt de afstand tussen knooppunten binnen een niveau. Genegeerd bij radial, waarbij een niveau altijd de volledige cirkel beslaat.

Cycli en losgekoppelde grafieken ​

De hiërarchie is opgebouwd uit een breedte-eerst omspannende boom geworteld in rootId, niet uit de ruwe randen, dus geen van deze stopt een boomindeling:

  • Cycles. De eerste rand die een knooppunt bereikt, maakt het tot de ouder van dat knooppunt; een rand die aankomt bij een Een reeds geplaatst knooppunt maakt eenvoudigweg geen deel uit van de hiërarchie en wordt net als elk ander niveau overschrijding getekend andere rand. Een knooppunt met twee ouders wordt eveneens door een van hen geclaimd.
  • Verschillende componenten. Elke component die de root niet kan bereiken, krijgt zijn eigen root – waarbij de voorkeur wordt gegeven aan een knooppunt zonder inkomende randen, dus een component die is een hiërarchie is, wordt als één getekend — en de componenten liggen naast elkaar.
  • Knooppunten zonder randen. Deze hebben geen plaats in een hiërarchie, dus zijn ze geparkeerd dan er één gegeven: ze vullen de dode ruimte naast de ondiepe niveaus, aan de achterrand van de lay-out, die een boom leeg laat omdat hij breder wordt naarmate hij naar beneden gaat. In de radiale lay-out nemen ze een eigen ring buiten de laatste. Alleen knooppunten waar niets naar * en* wijst, die naar niets wijzen geparkeerd, zodat er nooit een rand overblijft die zich uitstrekt van de boom tot aan de parkeerplaats.

Merk op dat rootIdAlgorithmFinder: 'MinMaxDistance' en 'MinHeight' dezelfde zoekopdracht zijn: het knooppunt waarvan het langste pad naar beneden het kortst is - en beide zijn gebaseerd op een topologische sortering, dus op een grafiek met een cyclus ze waarschuwen en vallen terug naar het eerste knooppunt. De standaard 'MaxReachability' kent een dergelijke beperking niet.

Boomwortelzoeker ​

In een boomgebaseerde lay-out moet één knooppunt als root worden aangewezen. U kunt het hoofdknooppunt opgeven met de optie rootId.

Als u niet zeker weet welk knooppunt de root moet zijn, hoeft u zich geen zorgen te maken! Pivotick selecteert er automatisch een met behulp van het algoritme dat is gedefinieerd in rootIdAlgorithmFinder.

Een genaamd rootId wordt bewandeld en negeert de randrichting, zodat elk knooppunt een hele boom kan rooten — kies een blad en de grafiek hangt er weer onder, met de voormalige ouder een niveau lager. (Volg de pijlen van een blad zou niets bereiken, en de rest van de grafiek zou er als een seconde naast worden gelegd component.) De randen zelf worden nog steeds getekend zoals ze zijn, dus er wordt een link andersom gebruikt wordt weergegeven als een pijl die een niveau omhoog wijst. Een wortel die rootIdAlgorithmFinder heeft gekozen, blijft doorlopen de pijlen: het werd in de eerste plaats van hen afgelezen.

Tenzij de pijlen geen hiërarchie vormen. Op gegevens die convergeren — elk blad een bron, allemaal wijzend naar een paar hubs, wat de vorm is van de meeste herkomst- en 'gezien-met'-gegevens - geen enkel knooppunt bereikt de grafiek langs de pijlen, welke vinder er ook wordt gevraagd. Een gerichte wandeling verlaat dan bijna elk knooppunt als een eigen wortel, en de boom komt eruit als een kam van stompen waarvan de meeste randen er overheen vliegen indeling. Dus als helemaal geen knooppunt de helft van zijn eigen component kan bestrijken door de pijlen te volgen, de lay-out stopt met het lezen van de richting: hij loopt langs elke rand in beide richtingen en wortelt opnieuw in het midden van de grafiek. De randen zijn nog steeds getekend precies zoals ze zijn, wat betekent dat hun pijlen omhoog wijzen in de richting van de boom. root – de eerlijke weergave van gegevens die die kant op wijzen.

Dit is een terugval, niet de regel, en de test gaat over de grafiek in plaats van over de wortel die dat was geplukt:

  • Waar de pijlen een hiërarchie vormen, kunnen ze het beste worden ingedeeld, en elke vinder houdt zich daaraan zijn eigen antwoord — inclusief 'MinHeight', waarvan het antwoord op een boom altijd een blad is, reikend tot niets met opzet. Een wortel die een klein deel van de grafiek beslaat, wordt alleen overruled als er niets anders is had meer kunnen dekken.
  • Een grafiek van verschillende afzonderlijke hiërarchieën komt er nog steeds uit als een bos: de dekking wordt gemeten de eigen component van de root, dus een perfect goede hiërarchie met meerdere componenten wordt niet aangezien voor a mislukking.
  • Een vastgezette rootId slaat de test volledig over; er wordt al gelopen zonder de richting te negeren.

Een rootId die een knooppunt noemt dat niet in de grafiek staat - uitgefilterd, verwijderd, in een samengevouwen cluster — wordt genegeerd zolang dat waar is, en de vinder kiest in plaats daarvan de wortel. De optie blijft behouden, dus het knooppunt dat terugkomt, wortelt de boom opnieuw.

De root kan ook tijdens runtime worden gewijzigd; de vervolgkeuze Natuurkunde biedt deze aan als een menu in de rij Root wanneer een boomstructuur actief is. Het menu geeft elke vinder een naam en zegt wat deze doet; zijn eerste binnenkomst is "het knooppunt dat ik heb geselecteerd", dat grijs wordt weergegeven totdat precies één knooppunt is geselecteerd. Zodra een knooppunt is vastgezet, toont de rij het label van dat knooppunt in plaats van de naam van een vinder:

ts
graph.simulation.setTreeRoot({ rootId: 'node-42' })          // pin the tree to a node
graph.simulation.setTreeRoot({ algorithm: 'MinHeight' })     // drop the pin, let the finder choose
graph.simulation.getTreeRoot() // { rootId: undefined, algorithm: 'MinHeight' }

Het verklaren van de hiërarchie ​

Normaal gesproken werkt Pivotick de hiërarchie voor zichzelf uit: ouderschap vanuit een opspannende boom over de randen, en elk knooppunt één rij onder zijn ouder. Twee opties laten de gegevens het zeggen.

  • parentKey noemt de sleutel node.data die de bovenliggende ID van een knooppunt bevat.
  • depthKey noemt de node.data-toets die de rij vasthoudt waarop deze staat, geteld vanaf 0 op het ondiepste wortel.

Beide zijn standaard uitgeschakeld en er wordt helemaal niets gelezen totdat je een sleutel een naam geeft, dus een grafiek waarvan de gegevens dat toevallig een veld depth of parent bevat, blijft onaangetast totdat u erom vraagt.

Het kan zijn dat een van beide sleutels op een afzonderlijk knooppunt ontbreekt. Een knooppunt zonder aangegeven ouder krijgt er een van de randen zoals gewoonlijk; een knooppunt zonder gedeclareerde rij bevindt zich één onder zijn ouder. Een grafiek die geen van beide verklaart is precies zo ingedeeld als altijd.

Een boom verder naar beneden starten ​

Dit is waar depthKey voornamelijk voor bedoeld is. Een grafiek van meerdere afzonderlijke bomen is een bos, en zijn wortels ze delen allemaal de ondiepste rij – voorheen was er geen manier om te zeggen dat een van hen verder thuishoort naar beneden. Geef die wortel een rij:

ts
const data = {
    nodes: [
        { id: 'a' }, { id: 'a1' }, { id: 'a2' },
        { id: 'b', data: { level: 2 } }, { id: 'b1' }, { id: 'b2' },
    ],
    edges: [
        { from: 'a', to: 'a1' }, { from: 'a', to: 'a2' },
        { from: 'b', to: 'b1' }, { from: 'b', to: 'b2' },
    ],
}

const graph = new Pivotick(container, data, {
    layout: { type: 'tree', depthKey: 'level' },
})
row 0     a
row 1   a1  a2
row 2                b          <- b asked for row 2
row 3              b1  b2

De hele boom beweegt met zijn wortel mee: de kinderen van b volgen hem naar beneden. In de lay-out radial is een rij a ring, dus dezelfde declaratie begint met twee ringen.

Wat de rijen betekenen ​

Een rij kan een knooppunt alleen verder naar beneden duwen. Een opgeruimde boom plaatst een kind precies één rij lager de ouder, dus een rij die niet onder de ouder ligt, kan niet worden getekend: deze wordt vastgeklemd op parent + 1 en een waarschuwing geeft aan hoeveel er waren. Het alternatief, de rij eren door het knooppunt los te maken van de rij ouder, zou één verkeerd getal een boom in extra stukken laten breken.

Rijen die de gegevens overslaan, blijven leeg en nemen echte ruimte in beslag — dat is wat een gedeclareerde rij betekent alles. Een boom wordt geschaald zodat hij op het canvas past, dus rij 50 wordt aangegeven op een verder ondiepe plek De grafiek maakt het eenenvijftig rijen hoog en perst elke echte rij tot niets. levelSpacing en automatische spatiëring is de weg terug naar buiten.

Wat een verklaarde ouder doet ​

Een aangegeven ouder wordt gehonoreerd ongeacht of er wel of niet een voorsprong bij het paar komt. Dat maakt het mogelijk om te stellen een hiërarchie die de randen niet bevatten – maar het betekent ook dat zo’n tak zonder lijn wordt getekend omdat er geen rand is om te tekenen. De verklaring is waar de hiërarchie vandaan komt; de randen worden alleen getekend zoals ze zijn.

Twee nuttige gevolgen: een knooppunt met meerdere inkomende randen kan zeggen welke de echte ouder is (de spanningsboom zou anders degene pakken die hem het eerst bereikte), en een knooppunt zonder randen waarmee wordt verklaard dat een ouder een juiste plaats in de boom krijgt in plaats van op het geparkeerde gebied.

Verklaringen die niet kunnen worden gebruikt, worden voor dat knooppunt verwijderd, dat vervolgens terugvalt naar de randen, en één waarschuwing komt overeen met allemaal:

  • een ouder die een knooppunt een naam geeft die niet is opgemaakt - uitgefilterd, verwijderd of in een samengevouwen knooppunt cluster — op dezelfde manier waarop rootId terugvalt als het knooppunt dat het noemt ontbreekt;
  • een ouder die een cyclus zou sluiten (de link die deze sluit, is degene die is weggevallen);
  • een rij die geen getal 0 of groter is.
Voorrang ​

rootId overtreft parentKey. Een wortel vastzetten — in de opties, of door er een te kiezen in de Natuurkunde rail — is een verzoek om de boom opnieuw aan dat knooppunt te hangen, zodat het ouderschap opnieuw wordt afgeleid van de randen en verklaarde ouders worden gereserveerd voor die pas. Aangegeven rijen worden niet beïnvloed: ze zeggen hoe diep een knooppunt zit, niet waar het aan hangt.

egoTree negeert beide opties; het bouwt een ster op van de eigen buren van de wortel.

Afstand ​

Een boom wordt zo ingedeeld dat hij past op het canvas, zodat de natuurlijke afstand al de canvasgrootte volgt en hoe diep en breed de boom is. levelSpacing en siblingSpacing schaal die paste bij de geometrie: 2 betekent "tweemaal zo ver uit elkaar als de gepaste lay-out" op respectievelijk de diepte- en breedteas. Beide standaard ingesteld op 1, wat de aangepaste lay-out zelf is, en beide accepteren 0.5 tot 10.

Ze kunnen ook tijdens runtime worden gewijzigd: het vervolgblad Natuurkunde biedt ze als schuifregelaars in elke boom layout actief is (in plaats van de simulatieknoppen, die een boomindeling negeert):

ts
graph.simulation.setTreeSpacing({ levelSpacing: 2 })
graph.simulation.getTreeSpacing() // { levelSpacing: 2, siblingSpacing: 1 }

Automatische afstand ​

Standaard werkt de boom zijn eigen spatiëring uit. Een boomindeling wordt op maat gemaakt vanaf het canvas en nooit kijkt naar hoe groot de knooppunten zijn, dus wordt een boom met kleine stippen en een boom met grote gelabelde knooppunten gelegd identiek uit – en de tweede overlapt. spacing: 'auto' meet het strakste paar buren op elke as, berekent wat ze nodig hebben voor hun stralen (plus ruimte voor een pijlpunt tussen niveaus) en schaalt de vermenigvuldigers naar wens. Het leidt ze opnieuw af wanneer de grafiek verandert.

Twee dingen die het bewust niet zal doen:

  • Er wordt nooit een boom dichter ingepakt dan de aangepaste lay-out. Auto verhoogt alleen een vermenigvuldiger erboven 1, dus een grafiek die nooit druk was, wordt precies zo opgemaakt als altijd.
  • Het heeft nooit voorrang op een keuze die u hebt gemaakt. Een boom die levelSpacing of siblingSpacing instelt, is aangenomen dat hij een besluit heeft genomen, en als u een van de schuifregelaars met de hand beweegt, verlaat de auto voorgoed. Geef de vermenigvuldigers terug met simulation.enableAutoTreeSpacing().

In de lay-out radial, waar een niveau altijd de volledige cirkel beslaat, kan verdringing binnen een ring alleen U kunt dit verlichten door de ringen verder naar buiten te duwen, zodat beide metingen daar levelSpacing naartoe rijden.

Gestructureerde lay-out ​

type: 'structured' toont een kleine grafiek zoals iemand deze zou tekenen, voor een ingebedde grafiek viewer die in één oogopslag leesbaar moet zijn. Het wordt eerder berekend dan gesimuleerd: elk knooppunt is dat geplaatst en vastgezet, zodat dezelfde grafiek altijd hetzelfde beeld geeft, en het slepen van een knooppunt beweegt alleen dat knooppunt.

  • Componenten naast elkaar. Elke verbonden component is afzonderlijk ingedeeld en de componenten zijn in rijen verpakt. De rijbreedte is degene die past bij de hele lay-out canvas op de grootste zoom.
  • Waaiers staan in de rij. Drie of meer bladeren van hetzelfde type die aan dezelfde of twee hangen knooppunten (een blad heeft maximaal twee buren) staan in één gelijkmatig verdeelde rij of kolom, met de knooppunten waaraan ze aan één kant hangen. De groepering is die van Simplify dezelfde buren regel gebruikt, getypt door UI.simplify.typeOf, anders render.nodeTypeAccessor. Niets wordt weggevouwen.
  • Afstanden volgen de randen. Al het andere wordt geplaatst door middel van spanningsmajorisatie: knooppunten één rand uit elkaar zitten één randlengte uit elkaar, twee randen twee keer zo lang uit elkaar, enzovoort. Een hub buren komen er omheen en er komt een ketting recht uit, zonder speciaal geval voor een van beide.
  • Een lade voor losse knooppunten. Knooppunten zonder rand worden in een raster onder de rest of tot aan de juist.
  • Labels aan de vrije zijde. Een zwevend label (textVerticalShift of textHorizontalShift van 1 of meer) wordt getekend op de zijkant van de knoop die de randen vrijlaten, tot aan de rand stijl het gezegd. De labels van een ventilator zijn allemaal afgekeerd van de knooppunten waaraan hij hangt.

De lay-out wordt opnieuw uitgevoerd telkens wanneer de zichtbare grafiek verandert, en nogmaals na de eerste trekking, wanneer de werkelijke afmetingen van de knooppunten bekend zijn. De knooppunten hebben een afstand van getLayoutRadius(), dus een knooppuntstijl dat aangeeft dat layoutSize op dat formaat is opgemaakt, ongeacht wat het tekent. Het is bedoeld voor grafieken van tot een paar honderd knooppunten.

OptieTypStandaardBeschrijving
groupMinnumber3De minste bladeren delen een type en hun buren die als fan in de rij staan. Er worden er minder één voor één geplaatst.
direction'auto' | 'row' | 'column''auto'Hoe een fan zich opstelt. 'auto' plaatst het over de lijn naar de knooppunten waar het aan hangt.
gapnumber30Maak ruimte vrij tussen twee knooppunten. Een rand wordt tweemaal zo lang getekend; componenten en de lade zitten drie keer zoveel uit elkaar.
trayPosition'bottom' | 'right''bottom'Waar de knooppunten zonder rand naartoe gaan.
labelSidesbooleantrueVerplaats elk zwevend label naar de kant waar de randen vrij blijven. false laat labels achter waar de stijl ze plaatst.
ts
new Pivotick(container, data, {
    layout: { type: 'structured', trayPosition: 'right' },
    render: {
        nodeTypeAccessor: (node) => node.getData().type,
        defaultNodeStyle: { text: (node) => node.getData().label, textVerticalShift: -1.4 },
    },
})

graph.simulation.getLabelSide(id) rapporteert de gekozen zijde voor een knooppunt: 'top', 'bottom', 'left' of 'right', of undefined onder een andere lay-out.

De weergave wordt aangepast aan de knooppunten, niet aan hun labels, dus een lang label naast een knooppunt aan de rand van de lay-out kan voorbij het canvas lopen.

gevaar

Voeg een voorbeeld toe met de twee lay-outs!

Verticale Boom ​

js
const options = {
    layout: {
        type: 'tree'
    },
    UI: {
        mode: 'static'
    }
}

Verticale Boom ​

js
const options = {
    layout: {
        type: 'tree',
        radial: true,
        radialGap: 300,
    },
    UI: {
        mode: 'static'
    }
}