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="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-CHmI2uuS.js"></script>
|
<script type="module" crossorigin src="/assets/index-i5uFkghh.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DHhyicLg.css">
|
<link rel="stylesheet" crossorigin href="/assets/index--2fz5jZz.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -73,19 +73,19 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
|||||||
// Quadranten-Fokuskoordinaten für sauberes Clustering (Fokuszentren weiter auseinander gezogen)
|
// Quadranten-Fokuskoordinaten für sauberes Clustering (Fokuszentren weiter auseinander gezogen)
|
||||||
const getClusterX = (category: string) => {
|
const getClusterX = (category: string) => {
|
||||||
return {
|
return {
|
||||||
identity: -220,
|
identity: -240,
|
||||||
knowledge: 220,
|
knowledge: 240,
|
||||||
rules: -220,
|
rules: -240,
|
||||||
events: 220,
|
events: 240,
|
||||||
}[category] || 0
|
}[category] || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const getClusterY = (category: string) => {
|
const getClusterY = (category: string) => {
|
||||||
return {
|
return {
|
||||||
identity: -220,
|
identity: -240,
|
||||||
knowledge: -220,
|
knowledge: -240,
|
||||||
rules: 220,
|
rules: 240,
|
||||||
events: 220,
|
events: 240,
|
||||||
}[category] || 0
|
}[category] || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,23 +98,30 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
|||||||
fg.d3Force("center", null)
|
fg.d3Force("center", null)
|
||||||
|
|
||||||
// 2. Clustering-Kräfte (Knoten zu ihren jeweiligen Sektions-Foci ziehen)
|
// 2. Clustering-Kräfte (Knoten zu ihren jeweiligen Sektions-Foci ziehen)
|
||||||
fg.d3Force("x", forceX().x((node: any) => getClusterX(node.category)).strength(0.18))
|
// Reduzierte Stärke (0.07 statt 0.18), damit sie sich nicht zu einem dichten Brei zusammenballen.
|
||||||
fg.d3Force("y", forceY().y((node: any) => getClusterY(node.category)).strength(0.18))
|
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
|
// 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")
|
const charge = fg.d3Force("charge")
|
||||||
if (charge) charge.strength(-240)
|
if (charge) charge.strength(-380)
|
||||||
|
|
||||||
// 4. Kollisionskraft (Fester, sicherer Radius, verhindert Überlappung)
|
// 4. Kollisionskraft (Sicherer Radius, verhindert Überlappung)
|
||||||
fg.d3Force("collide", forceCollide().radius(34).iterations(2))
|
// 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)
|
// 5. Federkraft (Links)
|
||||||
|
// Längere Verbindungsstriche (85 statt 60), damit das Netz übersichtlicher und "verbindungsreicher" wirkt.
|
||||||
const link = fg.d3Force("link")
|
const link = fg.d3Force("link")
|
||||||
if (link) link.distance(60)
|
if (link) link.distance(85)
|
||||||
|
|
||||||
// Simulation neu starten (Reheat)
|
// Simulation neu starten (Reheat)
|
||||||
fg.d3ReheatSimulation()
|
fg.d3ReheatSimulation()
|
||||||
}, [graphData])
|
}, [graphData, hovered, selectedNodeId])
|
||||||
|
|
||||||
const activeNode = hovered || selectedNodeId
|
const activeNode = hovered || selectedNodeId
|
||||||
|
|
||||||
@@ -189,7 +196,7 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
|||||||
nodeCanvasObject={(node: any, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
nodeCanvasObject={(node: any, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
||||||
const x = node.x ?? 0
|
const x = node.x ?? 0
|
||||||
const y = node.y ?? 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 color = CAT_COLOR[node.category] || "#64748b"
|
||||||
const isHovered = node.id === hovered
|
const isHovered = node.id === hovered
|
||||||
const isSelected = node.id === selectedNodeId
|
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 isDimmed = activeNode && node.id !== activeNode && !connectedNodes.has(node.id)
|
||||||
const finalColor = isDimmed ? "#1e293b" : color
|
const finalColor = isDimmed ? "#1e293b" : color
|
||||||
|
|
||||||
|
// 1. Zeichne den farbigen Knotenpunkt (Kreis)
|
||||||
if (!isDimmed) {
|
if (!isDimmed) {
|
||||||
ctx.shadowColor = color
|
ctx.shadowColor = color
|
||||||
ctx.shadowBlur = isHovered || isSelected ? 12 : 6
|
ctx.shadowBlur = isHovered || isSelected ? 15 : 6
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
@@ -210,28 +218,33 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
|||||||
|
|
||||||
if (isHovered || isSelected) {
|
if (isHovered || isSelected) {
|
||||||
ctx.beginPath()
|
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.strokeStyle = isSelected ? "#ffffff" : "rgba(255, 255, 255, 0.7)"
|
||||||
ctx.lineWidth = 1.5
|
ctx.lineWidth = 1.5
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = node.content.length > 30 ? node.content.slice(0, 29) + "…" : node.content
|
// 2. Zeichne das Label nur unter bestimmten Bedingungen (Zoom-abhängig)
|
||||||
const baseFontSize = isHovered || isSelected ? 10 : 9
|
// Verhindert Text-Clutter und macht Knotenpunkte permanent sichtbar.
|
||||||
const fontSize = baseFontSize / Math.max(0.6, globalScale)
|
const showLabel = isHovered || isSelected || (globalScale >= 1.25) || (activeNode && connectedNodes.has(node.id))
|
||||||
|
|
||||||
|
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.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
|
ctx.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
|
||||||
const textWidth = ctx.measureText(label).width
|
const textWidth = ctx.measureText(label).width
|
||||||
const bpad = 4 / Math.max(0.6, globalScale)
|
const bpad = 4.5 / Math.max(0.5, globalScale)
|
||||||
|
|
||||||
ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.85)"
|
ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.88)"
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
|
|
||||||
const rx = x - textWidth / 2 - bpad
|
const rx = x - textWidth / 2 - bpad
|
||||||
const ry = y + size + (5 / Math.max(0.6, globalScale))
|
const ry = y + size + (6 / Math.max(0.5, globalScale))
|
||||||
const rw = textWidth + bpad * 2
|
const rw = textWidth + bpad * 2
|
||||||
const rh = fontSize + bpad * 1.5
|
const rh = fontSize + bpad * 1.6
|
||||||
const rad = 4 / Math.max(0.6, globalScale)
|
const rad = 4 / Math.max(0.5, globalScale)
|
||||||
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(rx + rad, ry)
|
ctx.moveTo(rx + rad, ry)
|
||||||
@@ -246,15 +259,16 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
|||||||
ctx.closePath()
|
ctx.closePath()
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
|
|
||||||
ctx.strokeStyle = isHovered || isSelected ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.05)"
|
ctx.strokeStyle = isHovered || isSelected ? "rgba(255, 255, 255, 0.2)" : "rgba(255, 255, 255, 0.08)"
|
||||||
ctx.lineWidth = 0.5 / Math.max(0.6, globalScale)
|
ctx.lineWidth = 0.6 / Math.max(0.5, globalScale)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
|
|
||||||
ctx.textAlign = "center"
|
ctx.textAlign = "center"
|
||||||
ctx.textBaseline = "top"
|
ctx.textBaseline = "top"
|
||||||
ctx.fillStyle = isDimmed ? "#475569" : (isHovered || isSelected ? "#ffffff" : "#cbd5e1")
|
ctx.fillStyle = isDimmed ? "#475569" : (isHovered || isSelected ? "#ffffff" : "#cbd5e1")
|
||||||
|
|
||||||
ctx.fillText(label, x, y + size + (6 / Math.max(0.6, globalScale)))
|
ctx.fillText(label, x, y + size + (7 / Math.max(0.5, globalScale)))
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user