[페이지 원복] 기존 테스트 페이지 원복복
This commit is contained in:
260
pages/[tabId]/test/pathway4.vue
Normal file
260
pages/[tabId]/test/pathway4.vue
Normal file
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<div ref="cyContainer" class="cy-container"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from "vue";
|
||||
import cytoscape from "cytoscape";
|
||||
|
||||
let CytoscapeOverlays = null;
|
||||
|
||||
if (import.meta.client) {
|
||||
const useOverlay = (await import("@/composables/useOverlay")).default;
|
||||
CytoscapeOverlays = await useOverlay();
|
||||
}
|
||||
|
||||
const cyContainer = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
|
||||
cytoscape.use(CytoscapeOverlays.default);
|
||||
|
||||
const res = await fetch("/expanded_pathway21600.xml");
|
||||
const xmlText = await res.text();
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlText, "application/xml");
|
||||
|
||||
const scale = 3;
|
||||
const entryMap = new Map();
|
||||
const entryDataMap = new Map();
|
||||
const parentMap = new Map();
|
||||
|
||||
const entries = Array.from(xmlDoc.getElementsByTagName("entry"));
|
||||
|
||||
for (const entry of entries) {
|
||||
const id = entry.getAttribute("id");
|
||||
entryDataMap.set(id, entry);
|
||||
if (entry.getAttribute("type") === "group") {
|
||||
const components = entry.getElementsByTagName("component");
|
||||
for (const comp of components) {
|
||||
parentMap.set(comp.getAttribute("id"), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nodes = entries.map(entry => {
|
||||
const id = entry.getAttribute("id");
|
||||
const graphics = entry.getElementsByTagName("graphics")[0];
|
||||
const x = parseFloat(graphics?.getAttribute("x") || "0") * scale;
|
||||
const y = parseFloat(graphics?.getAttribute("y") || "0") * scale;
|
||||
const label = graphics?.getAttribute("name") || id;
|
||||
const fgColor = graphics?.getAttribute("fgcolor") || "#000000";
|
||||
const bgColor = graphics?.getAttribute("bgcolor") || "#ffffff";
|
||||
const parent = parentMap.get(id);
|
||||
|
||||
const valueA = Math.floor(Math.random() * 50);
|
||||
const valueB = 100 - valueA;
|
||||
|
||||
const node = {
|
||||
data: {
|
||||
id,
|
||||
label,
|
||||
link: entry.getAttribute("link") || null,
|
||||
reaction: entry.getAttribute("reaction") || null,
|
||||
chartData: [valueA, valueB],
|
||||
},
|
||||
position: { x, y },
|
||||
classes: entry.getAttribute("type"),
|
||||
style: {
|
||||
color: fgColor,
|
||||
"background-color": bgColor,
|
||||
},
|
||||
};
|
||||
|
||||
if (parent) node.data.parent = parent;
|
||||
entryMap.set(id, true);
|
||||
return node;
|
||||
});
|
||||
|
||||
function resolveToRealNode(id) {
|
||||
if (!entryMap.has(id)) return null;
|
||||
const entry = entryDataMap.get(id);
|
||||
if (entry?.getAttribute("type") === "group") {
|
||||
const components = Array.from(entry.getElementsByTagName("component"));
|
||||
if (components.length > 0) return components[0].getAttribute("id");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
const edges = [];
|
||||
const relations = Array.from(xmlDoc.getElementsByTagName("relation"));
|
||||
relations.forEach((rel, i) => {
|
||||
const source = resolveToRealNode(rel.getAttribute("entry1"));
|
||||
const target = resolveToRealNode(rel.getAttribute("entry2"));
|
||||
const type = rel.getAttribute("type");
|
||||
const subtypes = Array.from(rel.getElementsByTagName("subtype"));
|
||||
const compoundSubtype = subtypes.find(
|
||||
s => s.getAttribute("name") === "compound"
|
||||
);
|
||||
|
||||
if (compoundSubtype) {
|
||||
const compoundId = compoundSubtype.getAttribute("value");
|
||||
if (
|
||||
entryMap.has(source) &&
|
||||
entryMap.has(target) &&
|
||||
entryMap.has(compoundId)
|
||||
) {
|
||||
const sourceConst = source;
|
||||
const targetConst = target;
|
||||
edges.push(
|
||||
{
|
||||
data: {
|
||||
id: `edge${i}-1`,
|
||||
source: sourceConst,
|
||||
target: compoundId,
|
||||
label: "via compound",
|
||||
},
|
||||
},
|
||||
{
|
||||
data: {
|
||||
id: `edge${i}-2`,
|
||||
source: compoundId,
|
||||
target: targetConst,
|
||||
label: "via compound",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (entryMap.has(source) && entryMap.has(target)) {
|
||||
const sourceConst = source;
|
||||
const targetConst = target;
|
||||
edges.push({
|
||||
data: {
|
||||
id: `edge${i}`,
|
||||
source: sourceConst,
|
||||
target: targetConst,
|
||||
label: type,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const reactions = Array.from(xmlDoc.getElementsByTagName("reaction"));
|
||||
reactions.forEach((reaction, i) => {
|
||||
const reactionType = reaction.getAttribute("type");
|
||||
const substrates = Array.from(reaction.getElementsByTagName("substrate"));
|
||||
const products = Array.from(reaction.getElementsByTagName("product"));
|
||||
substrates.forEach(substrate => {
|
||||
const sid = resolveToRealNode(substrate.getAttribute("id"));
|
||||
products.forEach(product => {
|
||||
const pid = resolveToRealNode(product.getAttribute("id"));
|
||||
if (entryMap.has(sid) && entryMap.has(pid)) {
|
||||
edges.push({
|
||||
data: {
|
||||
id: `reaction-${i}-${sid}-${pid}`,
|
||||
source: sid,
|
||||
target: pid,
|
||||
label: `${reactionType}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 원형(도넛/파이) 차트 overlay 생성 (흰색 라인 없이)
|
||||
const pieOverlay = CytoscapeOverlays.renderSymbol({
|
||||
symbol: node => {
|
||||
const data = node.data("chartData") || [50, 50];
|
||||
return {
|
||||
draw: (ctx, size) => {
|
||||
const total = data[0] + data[1];
|
||||
const r = Math.sqrt(size / Math.PI);
|
||||
const innerR = r * 0.5;
|
||||
const startAngle = 0;
|
||||
const angleA = (data[0] / total) * 2 * Math.PI;
|
||||
|
||||
// A 영역
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, r, startAngle, startAngle + angleA);
|
||||
ctx.arc(0, 0, innerR, startAngle + angleA, startAngle, true);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = "#36a2eb";
|
||||
ctx.fill();
|
||||
|
||||
// B 영역
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, r, startAngle + angleA, startAngle + 2 * Math.PI);
|
||||
ctx.arc(
|
||||
0,
|
||||
0,
|
||||
innerR,
|
||||
startAngle + 2 * Math.PI,
|
||||
startAngle + angleA,
|
||||
true
|
||||
);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = "#ff6384";
|
||||
ctx.fill();
|
||||
},
|
||||
};
|
||||
},
|
||||
color: "",
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderColor: "#333",
|
||||
});
|
||||
|
||||
const cy = cytoscape({
|
||||
container: cyContainer.value,
|
||||
elements: { nodes, edges },
|
||||
style: [
|
||||
{
|
||||
selector: "node",
|
||||
style: {
|
||||
label: "data(label)",
|
||||
"background-color": "#eee",
|
||||
"text-valign": "center",
|
||||
"text-halign": "center",
|
||||
"border-width": 2,
|
||||
"border-color": "#333",
|
||||
"font-size": 8,
|
||||
padding: "6px",
|
||||
width: 32,
|
||||
height: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "edge",
|
||||
style: {
|
||||
"curve-style": "bezier",
|
||||
"target-arrow-shape": "triangle",
|
||||
"line-color": "#888",
|
||||
},
|
||||
},
|
||||
],
|
||||
layout: { name: "preset", padding: 100 },
|
||||
});
|
||||
|
||||
cy.overlays([{ position: "right", vis: pieOverlay }], {
|
||||
updateOn: "render",
|
||||
backgroundColor: "white",
|
||||
});
|
||||
|
||||
applyCustomZoomHandling(cy);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.cy-container {
|
||||
width: 100vw !important;
|
||||
max-width: 100vw !important;
|
||||
min-width: 0 !important;
|
||||
height: 800px !important;
|
||||
overflow: hidden !important;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user