mirror of
https://github.com/robonen/eulerian-cycle.git
synced 2026-03-20 19:04:48 +00:00
Added animation and animation track
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user