mirror of
https://github.com/robonen/eulerian-cycle.git
synced 2026-03-20 02:44:47 +00:00
Fixed indexing error, changed mouse events
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
class="render-area"
|
class="render-area"
|
||||||
ref="renderer"
|
ref="renderer"
|
||||||
@dblclick="createNode"
|
@dblclick="createNode"
|
||||||
@click="deactivateAllNodes"
|
@click.left.stop="unselectAllNodes"
|
||||||
>
|
>
|
||||||
<g>
|
<g>
|
||||||
<line
|
<line
|
||||||
@@ -15,23 +15,23 @@
|
|||||||
:x2="nodes[link.target].x"
|
:x2="nodes[link.target].x"
|
||||||
:y2="nodes[link.target].y"
|
:y2="nodes[link.target].y"
|
||||||
:class="{ 'line-active': link.selected }"
|
:class="{ 'line-active': link.selected }"
|
||||||
/>
|
@click.right.stop.prevent="removeLink(idx)"
|
||||||
|
></line>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<circle
|
<circle
|
||||||
class="node-default"
|
class="node-default"
|
||||||
r="25"
|
:r="RADIUS"
|
||||||
v-for="(node, idx) in nodes"
|
v-for="(node, idx) in nodes"
|
||||||
: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 }"
|
||||||
@dblclick.prevent="createNode"
|
@click.right.stop.prevent="removeNode(idx)"
|
||||||
@click.right.prevent="removeNode(idx)"
|
|
||||||
@click.left.stop="selectNode(idx)"
|
@click.left.stop="selectNode(idx)"
|
||||||
@mousedown="dragNode($event, idx)"
|
@mousedown="dragNode($event, idx)"
|
||||||
@mouseup="dropNode()"
|
@mouseup="dropNode"
|
||||||
/>
|
></circle>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<text
|
<text
|
||||||
@@ -45,10 +45,13 @@
|
|||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
<!-- <path d="M591,451 C291,300 891,300 591,451" stroke="black" fill="transparent" style="
|
||||||
|
stroke-width: 4px;
|
||||||
|
"></path> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, watch } from "vue";
|
import { ref } from "vue";
|
||||||
import Linker from "./core/linker";
|
import Linker from "./core/linker";
|
||||||
import EulerCycle from "./core/euler";
|
import EulerCycle from "./core/euler";
|
||||||
|
|
||||||
@@ -80,27 +83,27 @@ export default {
|
|||||||
const euler = new EulerCycle();
|
const euler = new EulerCycle();
|
||||||
|
|
||||||
// Watchers
|
// Watchers
|
||||||
watch(
|
// watch(
|
||||||
() => props.stepData,
|
// () => props.stepData,
|
||||||
(sd) => {
|
// (sd) => {
|
||||||
nodes.value.forEach((e) => {
|
// nodes.value.forEach((e) => {
|
||||||
e.selected = false;
|
// e.selected = false;
|
||||||
});
|
// });
|
||||||
|
|
||||||
links.value.forEach((e) => {
|
// links.value.forEach((e) => {
|
||||||
e.selected = false;
|
// e.selected = false;
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (Object.keys(sd).length !== 0) {
|
// if (Object.keys(sd).length !== 0) {
|
||||||
nodes.value[sd.source].selected = true;
|
// nodes.value[sd.source].selected = true;
|
||||||
nodes.value[sd.target].selected = true;
|
// nodes.value[sd.target].selected = true;
|
||||||
links.value.forEach((e) => {
|
// links.value.forEach((e) => {
|
||||||
if (e.source === sd.source && e.target === sd.target)
|
// if (e.source === sd.source && e.target === sd.target)
|
||||||
e.selected = true;
|
// e.selected = true;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const hasntIntersections = (node) => {
|
const hasntIntersections = (node) => {
|
||||||
@@ -112,12 +115,6 @@ export default {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deactivateAllNodes = () =>
|
|
||||||
nodes.value.forEach((e) => {
|
|
||||||
e.selected = false;
|
|
||||||
linker.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
const activateNodes = (ids) =>
|
const activateNodes = (ids) =>
|
||||||
ids.forEach((e) => (nodes.value[e].selected = true));
|
ids.forEach((e) => (nodes.value[e].selected = true));
|
||||||
|
|
||||||
@@ -138,23 +135,32 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const removeNode = (id) => {
|
const removeNode = (id) => {
|
||||||
links.value = links.value.filter(
|
links.value = links.value
|
||||||
(e) => e.source !== id && e.target !== id
|
.filter((e) => e.source !== id && e.target !== id)
|
||||||
);
|
.map((current) => {
|
||||||
nodes.value = nodes.value.filter((el, idx) => idx !== id);
|
current.source =
|
||||||
|
current.source > id ? current.source - 1 : current.source;
|
||||||
|
current.target =
|
||||||
|
current.target > id ? current.target - 1 : current.target;
|
||||||
|
return current;
|
||||||
|
});
|
||||||
|
|
||||||
|
nodes.value = nodes.value.filter((_, idx) => idx !== id);
|
||||||
|
|
||||||
emit("isEuler", []);
|
emit("isEuler", []);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectNode = (id) => {
|
const selectNode = (id) => {
|
||||||
if (linker.sourceEmpty()) {
|
if (linker.sourceEmpty()) linker.setSource(id);
|
||||||
linker.source = id;
|
else linker.addTarget(id);
|
||||||
|
|
||||||
activateNodes([id]);
|
activateNodes([id]);
|
||||||
} else {
|
};
|
||||||
linker.target = id;
|
|
||||||
deactivateNodes([linker.source, linker.target]);
|
const unselectAllNodes = () => {
|
||||||
createLink(linker.load());
|
deactivateNodes(linker.expandIds());
|
||||||
|
|
||||||
linker.reset();
|
linker.reset();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Node drag and drop
|
// Node drag and drop
|
||||||
@@ -186,23 +192,33 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
const createLink = (link) => {
|
linker.onLink((source, target) => {
|
||||||
links.value.push(Object.assign({ selected: false }, link));
|
links.value.push({
|
||||||
|
selected: false,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
|
||||||
euler.loadLinks([...links.value]);
|
euler.loadLinks([...links.value]);
|
||||||
|
|
||||||
if (euler.check()) emit("isEuler", euler.find());
|
if (euler.check()) emit("isEuler", euler.find());
|
||||||
else emit("isEuler", []);
|
else emit("isEuler", []);
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeLink = (id) => {
|
||||||
|
links.value = links.value.filter((_, idx) => idx !== id);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
RADIUS,
|
||||||
nodes,
|
nodes,
|
||||||
links,
|
links,
|
||||||
renderer,
|
renderer,
|
||||||
createNode,
|
createNode,
|
||||||
selectNode,
|
selectNode,
|
||||||
|
unselectAllNodes,
|
||||||
removeNode,
|
removeNode,
|
||||||
deactivateAllNodes,
|
removeLink,
|
||||||
dragNode,
|
dragNode,
|
||||||
dropNode,
|
dropNode,
|
||||||
};
|
};
|
||||||
@@ -234,6 +250,7 @@ line {
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-default {
|
.node-default {
|
||||||
|
|||||||
@@ -1,44 +1,55 @@
|
|||||||
class Linker {
|
class Linker {
|
||||||
#source = null;
|
#source = null;
|
||||||
#target = null;
|
#targets = null;
|
||||||
|
#callback = () => {};
|
||||||
|
|
||||||
constructor() {}
|
constructor() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
get source() {
|
getSource() {
|
||||||
return this.#source;
|
return this.#source;
|
||||||
}
|
}
|
||||||
|
|
||||||
set source(src) {
|
setSource(src) {
|
||||||
if (this.#source === null) this.#source = src;
|
if (this.#source === null) this.#source = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
get target() {
|
getTargets() {
|
||||||
return this.#target;
|
return this.#targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
set target(trg) {
|
setTargets(trg) {
|
||||||
if (trg === this.#source) return;
|
this.#targets = trg;
|
||||||
if (this.#target === null) this.#target = trg;
|
}
|
||||||
|
|
||||||
|
addTarget(trg) {
|
||||||
|
this.#targets.push(trg);
|
||||||
|
this.#callback(this.#source, trg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceEmpty() {
|
sourceEmpty() {
|
||||||
return this.#source === null;
|
return this.#source === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetEmpty() {
|
targetsEmpty() {
|
||||||
return this.#target === null;
|
return this.#targets.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
expandIds() {
|
||||||
return {
|
if (this.sourceEmpty()) {
|
||||||
source: this.#source,
|
return [...this.#targets];
|
||||||
target: this.#target,
|
}
|
||||||
};
|
return [this.#source, ...this.#targets];
|
||||||
|
}
|
||||||
|
|
||||||
|
onLink(fn) {
|
||||||
|
this.#callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.#source = null;
|
this.#source = null;
|
||||||
this.#target = null;
|
this.#targets = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
src/main.js
18
src/main.js
@@ -2,4 +2,20 @@ import { createApp } from "vue";
|
|||||||
import router from "./router";
|
import router from "./router";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
|
|
||||||
createApp(App).use(router).mount("#app");
|
const app = createApp(App).use(router);
|
||||||
|
|
||||||
|
app.directive("click-outside", {
|
||||||
|
beforeMount(el, binding) {
|
||||||
|
el.clickOutsideEvent = function (event) {
|
||||||
|
if (!(el === event.target || el.contains(event.target))) {
|
||||||
|
binding.value(event, el);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.body.addEventListener("click", el.clickOutsideEvent);
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
document.body.removeEventListener("click", el.clickOutsideEvent);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.mount("#app");
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
|
||||||
import Home from "../views/Home.vue";
|
import View from "@/views/View.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "Home",
|
|
||||||
component: Home,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/view",
|
|
||||||
name: "view",
|
name: "view",
|
||||||
component: () => import(/* webpackChunkName: "view" */ "../views/View.vue"),
|
component: View,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -141,5 +141,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@import "~@/assets/css/creation.css";
|
||||||
@import "~@/assets/css/graph.css";
|
@import "~@/assets/css/graph.css";
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user