diff --git a/app/src/layout/dock/Graph.ts b/app/src/layout/dock/Graph.ts index cc0942cc2..7cc19d280 100644 --- a/app/src/layout/dock/Graph.ts +++ b/app/src/layout/dock/Graph.ts @@ -574,6 +574,21 @@ export class Graph extends Model { return; } const config = window.siyuan.config.graph[this.type === "global" ? "global" : "local"]; + const timestep = 32 < this.graphData.nodes.length ? 0.1 : 0.5; + let maxVelocity = this.graphData.nodes.length; + if (this.graphData.nodes.length > 1024) { + maxVelocity = 1024; + } + if (this.graphData.nodes.length < 50) { + maxVelocity = 50; + } + let minVelocity = this.graphData.nodes.length; + if (this.graphData.nodes.length > 64) { + minVelocity = 64; + } + if (this.graphData.nodes.length < 16) { + minVelocity = 8; + } const options = { autoResize: true, interaction: { @@ -624,25 +639,34 @@ export class Graph extends Model { damping: 0.4, avoidOverlap: 0.5 }, - maxVelocity: 512, - minVelocity: 64, + maxVelocity: maxVelocity, + minVelocity: minVelocity, solver: "forceAtlas2Based", stabilization: { enabled: true, iterations: 64, updateInterval: 64, onlyDynamicEdges: false, - fit: false + fit: true }, - timestep: 0.5, + timestep: timestep, adaptiveTimestep: true, wind: {x: 0, y: 0} }, }; let i = Math.max(Math.ceil(this.graphData.nodes.length * 0.1), 128); + let j = Math.max(Math.ceil(this.graphData.links.length * 0.1), 128); const nodes = new vis.DataSet(this.graphData.nodes.slice(0, i)); - const edges = new vis.DataSet(); + const edges = new vis.DataSet(this.graphData.links.slice(0, j)); const network = new vis.Network(this.graphElement, {nodes, edges}, options); + const initialScale = Math.max(0.03, 1 - 0.3 * Math.floor(this.graphData.nodes.length / 128)); + if (1 !== initialScale) { + network.moveTo({ + position: {x: 0, y: 0}, + scale: initialScale, + animation: false + }) + } const time = 256; const intervalNodeTime = Math.max(Math.ceil(time / 8), 32); let batch = this.graphData.nodes.length / time / 2; @@ -652,7 +676,6 @@ export class Graph extends Model { if (batch > 256) { batch = 256; } - let count = 0; const intervalNode = setInterval(() => { if (!network.images) { clearInterval(intervalEdge); @@ -665,12 +688,7 @@ export class Graph extends Model { } network.body.data.nodes.add(nodesAdded); i += batch; - count++; - if (0 === count % (batch / 128)) { - network.fit({animation: false}); - } }, intervalNodeTime); - let j = 0; const intervalEdge = setInterval(() => { if (!network.images) { clearInterval(intervalEdge); diff --git a/kernel/model/backlink.go b/kernel/model/backlink.go index 88c16e349..cf36f9344 100644 --- a/kernel/model/backlink.go +++ b/kernel/model/backlink.go @@ -109,6 +109,7 @@ func GetBackmentionDoc(defID, refTreeID, keyword string, containChildren bool) ( if 0 < len(trees) { sortBacklinks(ret, refTree) + ret = mergeNeighborBacklinks(ret) filterBlockPaths(ret) } return @@ -152,6 +153,7 @@ func GetBacklinkDoc(defID, refTreeID, keyword string, containChildren bool) (ret } sortBacklinks(ret, refTree) + ret = mergeNeighborBacklinks(ret) filterBlockPaths(ret) for i := len(ret) - 1; 0 < i; i-- { @@ -165,10 +167,48 @@ func GetBacklinkDoc(defID, refTreeID, keyword string, containChildren bool) (ret return } +func mergeNeighborBacklinks(blockLinks []*Backlink) (ret []*Backlink) { + // 如果反链中的节点是相邻的,则合并 + for i := len(blockLinks) - 1; 0 < i; i-- { + if isPrevious(blockLinks[i].node, blockLinks[i-1].node) { + blockLinks[i-1].DOM += blockLinks[i].DOM + blockLinks[i] = nil + continue + } + } + + for _, b := range blockLinks { + if nil != b { + ret = append(ret, b) + } + } + return +} + +func isPrevious(cur, prev *ast.Node) bool { + if nil == cur || nil == prev { + return false + } + if cur.Previous == prev { + return true + } + for prevParent := prev.Parent; nil != prevParent; prevParent = prevParent.Parent { + if prev.Next == cur { + return true + } + } + for curParent := cur.Parent; nil != curParent; curParent = curParent.Parent { + if prev.Next == curParent { + return true + } + } + return false +} + func filterBlockPaths(blockLinks []*Backlink) { for _, b := range blockLinks { - if 1 == len(b.BlockPaths) && "NodeDocument" == b.BlockPaths[0].Type { - // 如果只有根文档这一层则不显示 + if 2 == len(b.BlockPaths) { + // 根下只有一层则不显示 b.BlockPaths = []*BlockPath{} } } @@ -246,10 +286,7 @@ func buildBacklink(refID string, refTree *parse.Tree, keywords []string, luteEng dom := renderBlockDOMByNodes(renderNodes, luteEngine) var blockPaths []*BlockPath if (nil != n.Parent && ast.NodeDocument != n.Parent.Type) || (ast.NodeHeading != n.Type && 0 < treenode.HeadingLevel(n)) { - // 仅在多于一层时才显示面包屑,这样界面展示更加简洁 - // The backlink panel no longer displays breadcrumbs of the first-level blocks https://github.com/siyuan-note/siyuan/issues/12862 - // Improve the backlink panel breadcrumb and block sorting https://github.com/siyuan-note/siyuan/issues/13008 - blockPaths = buildBlockBreadcrumb(n, nil, false) + blockPaths = buildBlockBreadcrumb(n, nil) } if 1 > len(blockPaths) { blockPaths = []*BlockPath{} diff --git a/kernel/model/block.go b/kernel/model/block.go index 2b9675ac3..dec586803 100644 --- a/kernel/model/block.go +++ b/kernel/model/block.go @@ -887,7 +887,7 @@ func getEmbeddedBlock(trees map[string]*parse.Tree, sqlBlock *sql.Block, heading } if breadcrumb { - blockPaths = buildBlockBreadcrumb(def, nil, true) + blockPaths = buildBlockBreadcrumb(def, nil) } if 1 > len(blockPaths) { blockPaths = []*BlockPath{} diff --git a/kernel/model/blockinfo.go b/kernel/model/blockinfo.go index 635b5c2cb..93f6a0ec9 100644 --- a/kernel/model/blockinfo.go +++ b/kernel/model/blockinfo.go @@ -416,11 +416,11 @@ func BuildBlockBreadcrumb(id string, excludeTypes []string) (ret []*BlockPath, e return } - ret = buildBlockBreadcrumb(node, excludeTypes, true) + ret = buildBlockBreadcrumb(node, excludeTypes) return } -func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string, displayCurrentNode bool) (ret []*BlockPath) { +func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string) (ret []*BlockPath) { ret = []*BlockPath{} if nil == node { return @@ -481,8 +481,8 @@ func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string, displayCurrentN name = strings.ReplaceAll(name, editor.Caret, "") name = util.EscapeHTML(name) - if parent == node && !displayCurrentNode { - add = false + if parent == node { + name = "" } if add {