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";
: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 {
width: 6px;
height: 6px;

View File

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

View File

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

View File

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