Politur: Zoom-abhaengiges Rendering fuer Gedaechtnis-Labels und staerkere Physik-Abstossung
This commit is contained in:
+8
-8
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -7,8 +7,8 @@
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>Mission Control 2.0</title>
|
||||
<script type="module" crossorigin src="/assets/index-CHmI2uuS.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DHhyicLg.css">
|
||||
<script type="module" crossorigin src="/assets/index-i5uFkghh.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index--2fz5jZz.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -73,19 +73,19 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
// Quadranten-Fokuskoordinaten für sauberes Clustering (Fokuszentren weiter auseinander gezogen)
|
||||
const getClusterX = (category: string) => {
|
||||
return {
|
||||
identity: -220,
|
||||
knowledge: 220,
|
||||
rules: -220,
|
||||
events: 220,
|
||||
identity: -240,
|
||||
knowledge: 240,
|
||||
rules: -240,
|
||||
events: 240,
|
||||
}[category] || 0
|
||||
}
|
||||
|
||||
const getClusterY = (category: string) => {
|
||||
return {
|
||||
identity: -220,
|
||||
knowledge: -220,
|
||||
rules: 220,
|
||||
events: 220,
|
||||
identity: -240,
|
||||
knowledge: -240,
|
||||
rules: 240,
|
||||
events: 240,
|
||||
}[category] || 0
|
||||
}
|
||||
|
||||
@@ -98,23 +98,30 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
fg.d3Force("center", null)
|
||||
|
||||
// 2. Clustering-Kräfte (Knoten zu ihren jeweiligen Sektions-Foci ziehen)
|
||||
fg.d3Force("x", forceX().x((node: any) => getClusterX(node.category)).strength(0.18))
|
||||
fg.d3Force("y", forceY().y((node: any) => getClusterY(node.category)).strength(0.18))
|
||||
// Reduzierte Stärke (0.07 statt 0.18), damit sie sich nicht zu einem dichten Brei zusammenballen.
|
||||
fg.d3Force("x", forceX().x((node: any) => getClusterX(node.category)).strength(0.07))
|
||||
fg.d3Force("y", forceY().y((node: any) => getClusterY(node.category)).strength(0.07))
|
||||
|
||||
// 3. Starke Abstoßung (Charge) für ein weit gefächertes, luftiges Netz
|
||||
// Erhöht auf -380, damit sich die Knoten kräftiger abstoßen.
|
||||
const charge = fg.d3Force("charge")
|
||||
if (charge) charge.strength(-240)
|
||||
if (charge) charge.strength(-380)
|
||||
|
||||
// 4. Kollisionskraft (Fester, sicherer Radius, verhindert Überlappung)
|
||||
fg.d3Force("collide", forceCollide().radius(34).iterations(2))
|
||||
// 4. Kollisionskraft (Sicherer Radius, verhindert Überlappung)
|
||||
// Wenn der Knoten hovered oder selected ist, geben wir ihm mehr Platz
|
||||
fg.d3Force("collide", forceCollide().radius((node: any) => {
|
||||
const isLarge = node.id === hovered || node.id === selectedNodeId
|
||||
return isLarge ? 50 : 25
|
||||
}).iterations(2))
|
||||
|
||||
// 5. Federkraft (Links)
|
||||
// Längere Verbindungsstriche (85 statt 60), damit das Netz übersichtlicher und "verbindungsreicher" wirkt.
|
||||
const link = fg.d3Force("link")
|
||||
if (link) link.distance(60)
|
||||
if (link) link.distance(85)
|
||||
|
||||
// Simulation neu starten (Reheat)
|
||||
fg.d3ReheatSimulation()
|
||||
}, [graphData])
|
||||
}, [graphData, hovered, selectedNodeId])
|
||||
|
||||
const activeNode = hovered || selectedNodeId
|
||||
|
||||
@@ -189,7 +196,7 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
nodeCanvasObject={(node: any, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
||||
const x = node.x ?? 0
|
||||
const y = node.y ?? 0
|
||||
const size = 5.5 + Math.min(degree[node.id] || 0, 12) * 0.9
|
||||
const size = 6 + Math.min(degree[node.id] || 0, 15) * 0.8
|
||||
const color = CAT_COLOR[node.category] || "#64748b"
|
||||
const isHovered = node.id === hovered
|
||||
const isSelected = node.id === selectedNodeId
|
||||
@@ -197,9 +204,10 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
const isDimmed = activeNode && node.id !== activeNode && !connectedNodes.has(node.id)
|
||||
const finalColor = isDimmed ? "#1e293b" : color
|
||||
|
||||
// 1. Zeichne den farbigen Knotenpunkt (Kreis)
|
||||
if (!isDimmed) {
|
||||
ctx.shadowColor = color
|
||||
ctx.shadowBlur = isHovered || isSelected ? 12 : 6
|
||||
ctx.shadowBlur = isHovered || isSelected ? 15 : 6
|
||||
}
|
||||
|
||||
ctx.beginPath()
|
||||
@@ -210,51 +218,57 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
|
||||
if (isHovered || isSelected) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, size + 2, 0, 2 * Math.PI, false)
|
||||
ctx.arc(x, y, size + 2.5, 0, 2 * Math.PI, false)
|
||||
ctx.strokeStyle = isSelected ? "#ffffff" : "rgba(255, 255, 255, 0.7)"
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
const label = node.content.length > 30 ? node.content.slice(0, 29) + "…" : node.content
|
||||
const baseFontSize = isHovered || isSelected ? 10 : 9
|
||||
const fontSize = baseFontSize / Math.max(0.6, globalScale)
|
||||
// 2. Zeichne das Label nur unter bestimmten Bedingungen (Zoom-abhängig)
|
||||
// Verhindert Text-Clutter und macht Knotenpunkte permanent sichtbar.
|
||||
const showLabel = isHovered || isSelected || (globalScale >= 1.25) || (activeNode && connectedNodes.has(node.id))
|
||||
|
||||
ctx.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
|
||||
const textWidth = ctx.measureText(label).width
|
||||
const bpad = 4 / Math.max(0.6, globalScale)
|
||||
if (showLabel) {
|
||||
const label = node.content.length > 35 ? node.content.slice(0, 34) + "…" : node.content
|
||||
const baseFontSize = isHovered || isSelected ? 10.5 : 9.5
|
||||
const fontSize = baseFontSize / Math.max(0.5, globalScale)
|
||||
|
||||
ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.85)"
|
||||
ctx.beginPath()
|
||||
ctx.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
|
||||
const textWidth = ctx.measureText(label).width
|
||||
const bpad = 4.5 / Math.max(0.5, globalScale)
|
||||
|
||||
const rx = x - textWidth / 2 - bpad
|
||||
const ry = y + size + (5 / Math.max(0.6, globalScale))
|
||||
const rw = textWidth + bpad * 2
|
||||
const rh = fontSize + bpad * 1.5
|
||||
const rad = 4 / Math.max(0.6, globalScale)
|
||||
ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.88)"
|
||||
ctx.beginPath()
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(rx + rad, ry)
|
||||
ctx.lineTo(rx + rw - rad, ry)
|
||||
ctx.quadraticCurveTo(rx + rw, ry, rx + rw, ry + rad)
|
||||
ctx.lineTo(rx + rw, ry + rh - rad)
|
||||
ctx.quadraticCurveTo(rx + rw, ry + rh, rx + rw - rad, ry + rh)
|
||||
ctx.lineTo(rx + rad, ry + rh)
|
||||
ctx.quadraticCurveTo(rx, ry + rh, rx, ry + rh - rad)
|
||||
ctx.lineTo(rx, ry + rad)
|
||||
ctx.quadraticCurveTo(rx, ry, rx + rad, ry)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
const rx = x - textWidth / 2 - bpad
|
||||
const ry = y + size + (6 / Math.max(0.5, globalScale))
|
||||
const rw = textWidth + bpad * 2
|
||||
const rh = fontSize + bpad * 1.6
|
||||
const rad = 4 / Math.max(0.5, globalScale)
|
||||
|
||||
ctx.strokeStyle = isHovered || isSelected ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.05)"
|
||||
ctx.lineWidth = 0.5 / Math.max(0.6, globalScale)
|
||||
ctx.stroke()
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(rx + rad, ry)
|
||||
ctx.lineTo(rx + rw - rad, ry)
|
||||
ctx.quadraticCurveTo(rx + rw, ry, rx + rw, ry + rad)
|
||||
ctx.lineTo(rx + rw, ry + rh - rad)
|
||||
ctx.quadraticCurveTo(rx + rw, ry + rh, rx + rw - rad, ry + rh)
|
||||
ctx.lineTo(rx + rad, ry + rh)
|
||||
ctx.quadraticCurveTo(rx, ry + rh, rx, ry + rh - rad)
|
||||
ctx.lineTo(rx, ry + rad)
|
||||
ctx.quadraticCurveTo(rx, ry, rx + rad, ry)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
|
||||
ctx.textAlign = "center"
|
||||
ctx.textBaseline = "top"
|
||||
ctx.fillStyle = isDimmed ? "#475569" : (isHovered || isSelected ? "#ffffff" : "#cbd5e1")
|
||||
ctx.strokeStyle = isHovered || isSelected ? "rgba(255, 255, 255, 0.2)" : "rgba(255, 255, 255, 0.08)"
|
||||
ctx.lineWidth = 0.6 / Math.max(0.5, globalScale)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.fillText(label, x, y + size + (6 / Math.max(0.6, globalScale)))
|
||||
ctx.textAlign = "center"
|
||||
ctx.textBaseline = "top"
|
||||
ctx.fillStyle = isDimmed ? "#475569" : (isHovered || isSelected ? "#ffffff" : "#cbd5e1")
|
||||
|
||||
ctx.fillText(label, x, y + size + (7 / Math.max(0.5, globalScale)))
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user