146 lines
4.0 KiB
Vue
146 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { chunks } from '~/helpers';
|
|
import * as d3 from "d3";
|
|
|
|
const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve));
|
|
|
|
const point_array = useState<number[][] | undefined>('point_array', () => undefined)
|
|
const chunk_size = useState<number>('chunk_size', () => 8)
|
|
const threshold = useState<number>('threshold', () => 20)
|
|
const grid_redraw = useState<boolean>('grid_redraw', () => false)
|
|
const active_point = useState<{ x: number, y: number } | undefined>('active_point', () => undefined)
|
|
const target_points = useState<{ x: number, y: number, type: string }[]>('target_points', () => [])
|
|
|
|
const paths_array = ref<{ path: string, unwalkable: boolean, x: number, y: number }[]>()
|
|
const props = defineProps(['cw', 'ch'])
|
|
const d3svg = ref()
|
|
|
|
const setActivePoint = (item: { x: number, y: number }) => {
|
|
active_point.value = { x: item.x, y: item.y }
|
|
}
|
|
|
|
const redraw = ref(false)
|
|
|
|
const sampling_data = async () => {
|
|
if (redraw.value) {
|
|
return
|
|
}
|
|
redraw.value = true
|
|
grid_redraw.value = true
|
|
const points = point_array.value
|
|
const chunk = chunk_size.value
|
|
if (!points) return
|
|
const prepared_array = [...chunks(points, chunk)].map(line => {
|
|
const line_data = [] as any[][]
|
|
line.map((item: any) => [...chunks(item, chunk)]).map((item) => {
|
|
item.map((one_line, k) => {
|
|
if (!line_data[k]) line_data[k] = []
|
|
line_data[k].push(...one_line)
|
|
})
|
|
})
|
|
return line_data.map(el => {
|
|
return el.filter(e => e > 0).length > threshold.value ? 1 : 0
|
|
})
|
|
});
|
|
|
|
const res: any[] = []
|
|
prepared_array.forEach((line, indexY) => {
|
|
line.forEach((point, indexX) => {
|
|
const targetX = indexX * chunk
|
|
const targetY = indexY * chunk
|
|
res.push({
|
|
path: `M${targetX} ${targetY} ${targetX + chunk} ${targetY} ${targetX + chunk} ${targetY + chunk} ${targetX} ${targetY + chunk}Z`,
|
|
unwalkable: !!point,
|
|
x: indexX,
|
|
y: indexY,
|
|
})
|
|
})
|
|
})
|
|
paths_array.value = res
|
|
|
|
// console.time('redraw D3')
|
|
const svg = d3.select('#d3svg')
|
|
svg.selectAll("path").remove();
|
|
const path_elements = svg.selectAll("path")
|
|
|
|
for (let i = 0; i < paths_array.value.length; i++) {
|
|
const element = paths_array.value[i];
|
|
|
|
let _class = '';
|
|
if (element.unwalkable) {
|
|
_class += ' unwalkable'
|
|
} else {
|
|
_class += ' walkable'
|
|
}
|
|
if (target_points.value.find(el => el.x == element.x && el.y === element.y)) {
|
|
_class += ' active'
|
|
}
|
|
const clickFunk = () => setActivePoint({ x: element.x, y: element.y })
|
|
if (path_elements.nodes()[i]) {
|
|
d3.select(path_elements.nodes()[i])
|
|
.attr('d', element.path)
|
|
.attr('class', _class)
|
|
.on('click', clickFunk)
|
|
} else {
|
|
svg.append("path")
|
|
.attr('d', element.path)
|
|
.attr('class', _class)
|
|
.on('click', clickFunk)
|
|
}
|
|
if (i % 200 == 0) {
|
|
await nextFrame()
|
|
}
|
|
};
|
|
// console.timeEnd('redraw D3')
|
|
grid_redraw.value = false
|
|
redraw.value = false
|
|
}
|
|
watch(point_array, () => {
|
|
sampling_data()
|
|
}, { deep: true })
|
|
watch(target_points, () => {
|
|
sampling_data()
|
|
}, { deep: true })
|
|
watch(chunk_size, () => {
|
|
sampling_data()
|
|
})
|
|
watch(threshold, () => {
|
|
sampling_data()
|
|
})
|
|
onMounted(() => {
|
|
// sampling_data()
|
|
})
|
|
</script>
|
|
<template>
|
|
<div>
|
|
<svg :width="props.cw" :height="props.ch" ref="d3svg" id="d3svg"></svg>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
svg path {
|
|
fill: transparent;
|
|
stroke: rgba(70, 70, 0, 0.1);
|
|
}
|
|
|
|
svg path.unwalkable {
|
|
fill: rgba(70, 70, 0, 0.5);
|
|
}
|
|
|
|
svg path.endPoint {
|
|
fill: blue;
|
|
}
|
|
|
|
svg path.active {
|
|
fill: lawngreen;
|
|
}
|
|
|
|
svg path.active {
|
|
/* fill: gold; */
|
|
}
|
|
|
|
|
|
svg path:hover {
|
|
fill: rgba(255, 0, 0, 0.5);
|
|
}
|
|
</style> |