1
0
mirror of https://github.com/robonen/eulerian-cycle.git synced 2026-03-20 10:54:46 +00:00

Added animation and animation track

This commit is contained in:
2021-12-21 09:18:50 +07:00
parent 0aabb2bac9
commit 2356593d39
4 changed files with 111 additions and 75 deletions

View File

@@ -1,14 +1,5 @@
@charset "UTF-8"; @charset "UTF-8";
:root {
--main-color: rgb(97 196 189);
--complement-color: rgb(46 105 120);
--hover-main-color: rgb(224, 241, 240);
--hover-complement-color: rgb(214 231 234);
--body-color: #f3f3f3;
--text-color: #3d3d3d;
}
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; width: 6px;
height: 6px; height: 6px;

View File

@@ -1,3 +1,5 @@
@charset "UTF-8";
:root { :root {
--main-color: rgb(97 196 189); --main-color: rgb(97 196 189);
--complement-color: rgb(46 105 120); --complement-color: rgb(46 105 120);
@@ -8,8 +10,8 @@
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0px; width: 6px;
height: 0px; height: 6px;
background-color: var(--body-color); background-color: var(--body-color);
} }

View File

@@ -1,25 +1,36 @@
<template> <template>
<svg <svg
class="render-area"
ref="renderer" ref="renderer"
@dblclick="createNode" @dblclick="createNode"
@click.left.stop="unselectAllNodes" @click.left.stop="unselectAllNodes"
@contextmenu.prevent @contextmenu.prevent
> >
<g> <g>
<component <g v-for="(link, idx) in links" :key="idx">
<path
v-if="link.source === link.target"
class="line-default" class="line-default"
v-for="(link, idx) in links" :class="{
:key="idx" 'line-active': link.selected === 1,
:is="link.source === link.target ? 'path' : 'line'" 'line-was-active': link.selected === 2,
}"
:d="loopPosition(link.source)"
@click.right.stop.prevent="removeLink(idx)"
></path>
<line
v-else
class="line-default"
:class="{
'line-active': link.selected === 1,
'line-was-active': link.selected === 2,
}"
:x1="nodes[link.source].x" :x1="nodes[link.source].x"
:y1="nodes[link.source].y" :y1="nodes[link.source].y"
:x2="nodes[link.target].x" :x2="nodes[link.target].x"
:y2="nodes[link.target].y" :y2="nodes[link.target].y"
:d="loopPosition(link.source)"
:class="{ 'line-active': link.selected }"
@click.right.stop.prevent="removeLink(idx)" @click.right.stop.prevent="removeLink(idx)"
></component> ></line>
</g>
</g> </g>
<g> <g>
<circle <circle
@@ -29,7 +40,10 @@
:key="idx" :key="idx"
:cx="node.x" :cx="node.x"
:cy="node.y" :cy="node.y"
:class="{ 'node-active': node.selected }" :class="{
'node-active': node.selected === 1,
'node-was-active': node.selected === 2,
}"
@click.right.stop.prevent="removeNode(idx)" @click.right.stop.prevent="removeNode(idx)"
@click.left.stop="selectNode(idx)" @click.left.stop="selectNode(idx)"
@mousedown="dragNode($event, idx)" @mousedown="dragNode($event, idx)"
@@ -51,16 +65,16 @@
</template> </template>
<script> <script>
import { ref } from "vue"; import { ref, watch } from "vue";
import Linker from "./core/linker"; import Linker from "./core/linker";
import EulerCycle from "./core/euler"; import EulerCycle from "./core/euler";
export default { export default {
name: "Graph", name: "Graph",
props: { props: {
stepData: { currentStep: {
type: Object, type: Number,
default: () => ({}), default: 0,
}, },
}, },
setup(props, { emit }) { setup(props, { emit }) {
@@ -69,6 +83,7 @@ export default {
// Vars // Vars
let draggableNode = false; let draggableNode = false;
let loop = null;
// Reactive // Reactive
const renderer = ref(null); const renderer = ref(null);
@@ -86,27 +101,31 @@ export default {
const euler = new EulerCycle(); const euler = new EulerCycle();
// Watchers // Watchers
// watch( watch(
// () => props.stepData, () => props.currentStep,
// (sd) => { (stepId) => {
// nodes.value.forEach((e) => { if (loop === null) return;
// e.selected = false;
// });
// links.value.forEach((e) => { // Invalidate all
// e.selected = false; deactivateAll();
// });
// if (Object.keys(sd).length !== 0) { // Select passed
// nodes.value[sd.source].selected = true; for (let i = 0; i < stepId; i++) {
// nodes.value[sd.target].selected = true; const pass = loop[i];
// links.value.forEach((e) => { const pass_id = findLink(pass);
// if (e.source === sd.source && e.target === sd.target)
// e.selected = true; links.value[pass_id].selected = 2;
// }); wasActivatedNodes([pass.source, pass.target]);
// } }
// }
// ); // Select current
const current = loop[stepId];
const current_id = findLink(current);
links.value[current_id].selected = 1;
activateNodes([current.source, current.target]);
}
);
// Methods // Methods
const loopPosition = (coords) => { const loopPosition = (coords) => {
@@ -132,21 +151,37 @@ export default {
}; };
const checkGraph = () => { const checkGraph = () => {
if (loop !== null) {
deactivateAll();
if (!linker.sourceEmpty()) activateNodes([linker.getSource()]);
}
euler.loadLinks(Object.values(links.value)); euler.loadLinks(Object.values(links.value));
if (links.value.length > 0 && euler.check()) { if (links.value.length > 0 && euler.check()) {
emit("hasEuler", euler.find()); loop = euler.find();
} else { } else {
emit("hasEuler", null); loop = null;
} }
emit("hasEuler", loop);
}; };
// Nodes // Nodes
const activateNodes = (ids) => const activateNodes = (ids) =>
ids.forEach((e) => (nodes.value[e].selected = true)); ids.forEach((e) => (nodes.value[e].selected = 1));
const deactivateNodes = (ids) => const deactivateNodes = (ids) =>
ids.forEach((e) => (nodes.value[e].selected = false)); ids.forEach((e) => (nodes.value[e].selected = 0));
const wasActivatedNodes = (ids) =>
ids.forEach((e) => (nodes.value[e].selected = 2));
const deactivateAll = () => {
nodes.value.forEach((node) => (node.selected = false));
links.value.forEach((link) => (link.selected = 0));
};
const createNode = ({ offsetX, offsetY }) => { const createNode = ({ offsetX, offsetY }) => {
if (nodes.value.length >= 99) return; if (nodes.value.length >= 99) return;
@@ -154,7 +189,7 @@ export default {
const newNode = { const newNode = {
x: offsetX, x: offsetX,
y: offsetY, y: offsetY,
selected: false, selected: 0,
}; };
if (hasntIntersections(newNode)) { if (hasntIntersections(newNode)) {
@@ -235,24 +270,32 @@ export default {
}; };
// Links // Links
const hasLink = (obj1, obj2) => {
return (
(obj1.source === obj2.source && obj1.target === obj2.target) ||
(obj1.source === obj2.target && obj1.target === obj2.source)
);
};
const findLink = (vertex) => {
return links.value.findIndex((link) => hasLink(vertex, link));
};
linker.onLink((source, target) => { linker.onLink((source, target) => {
let duplicateLink = null; let duplicateLink = null;
links.value.forEach((e, idx) => { links.value.forEach((e, idx) => {
if ( if (hasLink(e, { source, target })) duplicateLink = idx;
(e.source === source && e.target === target) ||
(e.source === target && e.target === source)
)
duplicateLink = idx;
}); });
if (duplicateLink !== null) { if (duplicateLink !== null) {
links.value.splice(duplicateLink, 1); links.value.splice(duplicateLink, 1);
checkGraph();
return; return;
} }
links.value.push({ links.value.push({
selected: false, selected: 0,
source, source,
target, target,
}); });
@@ -321,11 +364,19 @@ path {
fill: #ec407a; fill: #ec407a;
} }
.node-was-active {
fill: #8d3956;
}
.line-default { .line-default {
stroke: #3d3d3d; stroke: #3d3d3d;
} }
.line-active { .line-active {
stroke: #a81043; stroke: #ff93b8;
}
.line-was-active {
stroke: #8d3956;
} }
</style> </style>

View File

@@ -56,12 +56,12 @@
Нажмите ЛКМ дважды, чтобы добавить вершину Нажмите ЛКМ дважды, чтобы добавить вершину
</div> </div>
<graph <graph
:stepData="currentStepData" :currentStep="currentStepNumber"
@hasEuler="loadSteps" @hasEuler="loadSteps"
@hasVertices="setVertices" @hasVertices="setVertices"
></graph> ></graph>
<div v-if="vertexExists" class="hints"> <div v-if="vertexExists" class="hints">
Чтобы удалить вершину, нажмите ПКМ по ней Чтобы удалить вершину или ребро, нажмите ПКМ
</div> </div>
</div> </div>
<div class="control-cont"> <div class="control-cont">
@@ -91,20 +91,12 @@
</div> </div>
</div> </div>
<div class="addition-cont"> <div class="addition-cont">
<div class="step-cont"> <!-- <div class="step-cont">
<!-- <div <div class="step active-step">2</div>
class="step"
v-for="cs in stepsCount"
:key="cs"
:class="{ 'active-step last-active-step': cs - 1 === currentStep }"
>
{{ cs }}
</div> -->
<!-- <div class="step active-step">2</div>
<div class="step active-step last-active-step">3</div> <div class="step active-step last-active-step">3</div>
<div class="step">1</div> <div class="step">1</div>
<div class="step">4</div> --> <div class="step">4</div>
</div> </div> -->
</div> </div>
</template> </template>