Update packages. Add quadtree (still need update for ID handling instead of index).
This commit is contained in:
parent
f32c51ca38
commit
685bd47fc4
|
|
@ -1,10 +1,12 @@
|
|||
<script lang="ts">
|
||||
import type { Position } from '#shared/canvas.util';
|
||||
import { Quadtree } from '#shared/physics.util';
|
||||
import type CanvasNodeEditor from './canvas/CanvasNodeEditor.vue';
|
||||
import type CanvasEdgeEditor from './canvas/CanvasEdgeEditor.vue';
|
||||
export type Element = { type: 'node' | 'edge', id: string };
|
||||
|
||||
interface ActionMap {
|
||||
interface ActionMap
|
||||
{
|
||||
remove: CanvasNode | CanvasEdge | undefined;
|
||||
create: CanvasNode | CanvasEdge | undefined;
|
||||
property: CanvasNode | CanvasEdge;
|
||||
|
|
@ -64,6 +66,22 @@ import { clamp } from '#shared/general.utils';
|
|||
import type { CanvasContent, CanvasEdge, CanvasNode } from '~/types/canvas';
|
||||
|
||||
const canvas = defineModel<CanvasContent>({ required: true, });
|
||||
const props = defineProps<{
|
||||
path: string
|
||||
}>();
|
||||
const quadtree = new Quadtree({ x1: -10240, x2: 10240, y1: -10240, y2: 10240 }, canvas, 12, 6);
|
||||
|
||||
watch(props, () => {
|
||||
quadtree.clear();
|
||||
|
||||
if(!canvas.value.nodes)
|
||||
return;
|
||||
|
||||
for(const node of canvas.value.nodes)
|
||||
{
|
||||
quadtree.insert(node.id, { x1: node.x, x2: node.x + node.width, y1: node.y, y2: node.y + node.height });
|
||||
}
|
||||
}, { immediate: true, });
|
||||
|
||||
const dispX = ref(0), dispY = ref(0), minZoom = ref(0.1), zoom = ref(0.5);
|
||||
const focusing = ref<Element>(), editing = ref<Element>();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,30 @@
|
|||
<template>
|
||||
<span class="text-accent-blue inline-flex items-center cursor-pointer hover:text-opacity-85"><slot v-bind="$attrs"></slot></span>
|
||||
</template>
|
||||
<span class="text-accent-blue inline-flex items-center" :class="class">
|
||||
<HoverCard class="max-w-[600px] max-h-[600px] w-full overflow-auto z-[45]" :class="{'overflow-hidden !p-0': overview?.type === 'canvas'}" :disabled="!overview">
|
||||
<template #content>
|
||||
<Markdown v-if="overview?.type === 'markdown'" class="!px-6" :path="pathname" :filter="hash.substring(1)" popover />
|
||||
<template v-else-if="overview?.type === 'canvas'"><div class="w-[600px] h-[600px] relative"><Canvas :path="pathname" /></div></template>
|
||||
</template>
|
||||
<span>
|
||||
<slot v-bind="$attrs"></slot>
|
||||
<Icon class="w-4 h-4 inline-block" v-if="overview && overview.type !== 'markdown'" :icon="iconByType[overview.type]" />
|
||||
</span>
|
||||
</HoverCard>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { parseURL } from 'ufo';
|
||||
import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||
import { iconByType } from '#shared/general.utils';
|
||||
|
||||
const { href } = defineProps<{
|
||||
href: string
|
||||
class?: string
|
||||
}>();
|
||||
|
||||
const { hash, pathname } = parseURL(href);
|
||||
|
||||
const { content } = useContent();
|
||||
const overview = computed(() => content.value.find(e => e.path === pathname));
|
||||
</script>
|
||||
26
package.json
26
package.json
|
|
@ -11,20 +11,20 @@
|
|||
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@nuxtjs/sitemap": "^7.0.1",
|
||||
"@nuxtjs/tailwindcss": "^6.12.2",
|
||||
"@nuxtjs/sitemap": "^7.2.3",
|
||||
"@nuxtjs/tailwindcss": "^6.13.1",
|
||||
"@vueuse/gesture": "^2.0.0",
|
||||
"@vueuse/math": "^11.3.0",
|
||||
"@vueuse/nuxt": "^11.3.0",
|
||||
"@vueuse/math": "^12.5.0",
|
||||
"@vueuse/nuxt": "^12.5.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"drizzle-orm": "^0.35.3",
|
||||
"drizzle-orm": "^0.38.4",
|
||||
"hast": "^1.0.0",
|
||||
"hast-util-heading": "^3.0.0",
|
||||
"hast-util-heading-rank": "^3.0.0",
|
||||
"lodash.capitalize": "^4.2.1",
|
||||
"mdast-util-find-and-replace": "^3.0.2",
|
||||
"nodemailer": "^6.9.16",
|
||||
"nuxt": "^3.15.0",
|
||||
"nodemailer": "^6.10.0",
|
||||
"nuxt": "3.15.1",
|
||||
"nuxt-security": "^2.1.5",
|
||||
"radix-vue": "^1.9.12",
|
||||
"rehype-raw": "^7.0.0",
|
||||
|
|
@ -37,18 +37,18 @@
|
|||
"rollup-plugin-vue": "^6.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
"@types/bun": "^1.2.0",
|
||||
"@types/lodash.capitalize": "^4.2.9",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"@types/unist": "^3.0.3",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"bun-types": "^1.1.42",
|
||||
"drizzle-kit": "^0.26.2",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"bun-types": "^1.2.0",
|
||||
"drizzle-kit": "^0.30.2",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"rehype-stringify": "^10.0.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@
|
|||
<div class="flex flex-row justify-between items-center gap-x-4">
|
||||
<div v-if="selected.customPath" class="flex lg:items-center truncate">
|
||||
<pre class="md:text-base text-sm truncate" style="direction: rtl">/{{ selected.parent !== '' ? selected.parent + '/' : '' }}</pre>
|
||||
<TextInput v-model="selected.name" @input="(e) => {
|
||||
<TextInput v-model="selected.name" @input="() => {
|
||||
if(selected && selected.customPath)
|
||||
{
|
||||
selected.name = parsePath(selected.name);
|
||||
|
|
@ -186,13 +186,13 @@
|
|||
</SplitterGroup>
|
||||
</template>
|
||||
<template v-else-if="selected.type === 'canvas'">
|
||||
<CanvasEditor v-if="selected.content" :modelValue="selected.content" />
|
||||
<CanvasEditor v-if="selected.content" :modelValue="selected.content" :path="getPath(selected)" />
|
||||
</template>
|
||||
<template v-else-if="selected.type === 'map'">
|
||||
<span class="flex flex-1 justify-center items-center"><ProseH3>Editeur de carte en cours de développement</ProseH3></span>
|
||||
</template>
|
||||
<template v-else-if="selected.type === 'file'">
|
||||
<span>Modifier le contenu :</span><input type="file" @change="(e) => console.log((e.target as HTMLInputElement).files?.length)" />
|
||||
<span>Modifier le contenu :</span><input type="file" @change="(e: Event) => console.log((e.target as HTMLInputElement).files?.length)" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { CanvasContent } from '~/types/canvas';
|
||||
import type { ContentMap, FileType } from '~/types/content';
|
||||
import type { FileType } from '~/types/content';
|
||||
|
||||
export function unifySlug(slug: string | string[]): string
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,478 @@
|
|||
import type { CanvasContent } from "~/types/canvas";
|
||||
import { clamp } from "./general.utils";
|
||||
|
||||
export function intersects(aLeft: number, aTop: number, aRight: number, aBottom: number, bLeft: number, bTop: number, bRight: number, bBottom: number): boolean
|
||||
{
|
||||
return aLeft <= bRight && aRight >= bLeft && aTop <= bBottom && aBottom >= bTop;
|
||||
}
|
||||
|
||||
enum QuadConsts
|
||||
{
|
||||
ENodeINext = 0,
|
||||
ENodeIElmt = 1,
|
||||
ElmtILft = 0, ElmtIRgt = 1, ElmtITop = 2, ElmtIBtm = 3,
|
||||
ElmtIId = 4,
|
||||
NodeIFirst = 0,
|
||||
NodeICount = 1,
|
||||
PropCount = 6,
|
||||
PropILft = 0, PropIRgt = 1, PropITop = 2, PropIBtm = 3,
|
||||
PropIIdx = 4,
|
||||
PropIDpt = 5,
|
||||
}
|
||||
|
||||
const DEFAULT_SIZE = 128;
|
||||
const MAX_BYTE_LENGTH = 1024*1024*16;
|
||||
|
||||
export interface AABB {
|
||||
x1: number;
|
||||
x2: number;
|
||||
y1: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export class IntList
|
||||
{
|
||||
#data: Int32Array;
|
||||
#fields: number;
|
||||
#capacity: number = DEFAULT_SIZE;
|
||||
#length: number = 0;
|
||||
#free: number = -1;
|
||||
|
||||
constructor(fields: number)
|
||||
{
|
||||
if(fields <= 0)
|
||||
throw new Error("Invalid field count");
|
||||
|
||||
this.#data = new Int32Array(new ArrayBuffer(this.#capacity * fields * Int32Array.BYTES_PER_ELEMENT, { maxByteLength: MAX_BYTE_LENGTH }));
|
||||
this.#fields = fields;
|
||||
}
|
||||
get length(): number
|
||||
{
|
||||
return this.#length;
|
||||
}
|
||||
|
||||
get(index: number, field: number): number
|
||||
{
|
||||
if(index >= this.#length || index < 0)
|
||||
throw new Error("Invalid index");
|
||||
|
||||
if(field >= this.#fields || field < 0)
|
||||
throw new Error("Invalid field");
|
||||
|
||||
return this.#data[index*this.#fields + field];
|
||||
}
|
||||
set(index: number, field: number, value: number): void
|
||||
{
|
||||
if(index >= this.#length || index < 0)
|
||||
throw new Error("Invalid index");
|
||||
|
||||
if(field >= this.#fields || field < 0)
|
||||
throw new Error("Invalid field");
|
||||
|
||||
this.#data[index*this.#fields + field] = value;
|
||||
}
|
||||
clear(): void
|
||||
{
|
||||
//Never edit the array during clear, we'll keep every data as they *must* be override before being read.
|
||||
this.#length = 0;
|
||||
this.#free = -1;
|
||||
}
|
||||
push(): number
|
||||
{
|
||||
const pos = (this.#length + 1) * this.#fields;
|
||||
|
||||
if(pos > this.#capacity * this.#fields)
|
||||
{
|
||||
this.#capacity *= 2;
|
||||
|
||||
if((this.#data.buffer as ArrayBuffer).resizable)
|
||||
{
|
||||
(this.#data.buffer as ArrayBuffer).resize(clamp(this.#capacity * this.#fields * Int32Array.BYTES_PER_ELEMENT, 1, MAX_BYTE_LENGTH));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error("Cannot resize the buffer");
|
||||
}
|
||||
}
|
||||
|
||||
return this.#length++;
|
||||
}
|
||||
pop(): void
|
||||
{
|
||||
if(this.#length <= 0)
|
||||
return;
|
||||
|
||||
--this.#length;
|
||||
}
|
||||
insert(): number
|
||||
{
|
||||
if(this.#free !== -1)
|
||||
{
|
||||
const index = this.#free;
|
||||
this.#free = this.#data[index * this.#fields];
|
||||
return index;
|
||||
}
|
||||
else
|
||||
return this.push();
|
||||
}
|
||||
erase(index: number): void
|
||||
{
|
||||
if(index >= this.#length || index < 0)
|
||||
throw new Error("Invalid index");
|
||||
|
||||
this.#data[index * this.#fields] = this.#free;
|
||||
this.#free = index;
|
||||
}
|
||||
|
||||
toArray(): number[]
|
||||
{
|
||||
return [...this.#data.slice(0, this.#length)];
|
||||
}
|
||||
|
||||
//DEBUG
|
||||
/* printReadable(names: string[] = []): void
|
||||
{
|
||||
const size = this.#length * this.#fields;
|
||||
for(let i = 0; i < size; i)
|
||||
{
|
||||
const obj: Record<string, number> = {};
|
||||
for(let j = 0; j < this.#fields; ++i, ++j)
|
||||
{
|
||||
obj[names[j] ?? j] = this.#data[i];
|
||||
}
|
||||
console.log(obj);
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
const _process = new IntList(1);
|
||||
const _nodes = new IntList(QuadConsts.PropCount);
|
||||
|
||||
/**
|
||||
* A node that contains elments is called a leaf. Its count is equals to the amount of children it holds.
|
||||
* A node that contains other nodes is called a branch. Its count is equals to -1.
|
||||
* The AABB of each node isn't stored and is computed on the go.
|
||||
*/
|
||||
export class Quadtree
|
||||
{
|
||||
#bounds: AABB;
|
||||
#maxDepth: number = 8;
|
||||
#maxElmts: number = 4;
|
||||
#nodes: IntList = new IntList(2);
|
||||
#enodes: IntList = new IntList(2);
|
||||
#content: IntList = new IntList(5);
|
||||
|
||||
#ids: string[] = [];
|
||||
|
||||
ref: Ref<CanvasContent>;
|
||||
|
||||
constructor(bounds: AABB, ref: Ref<CanvasContent>, maxDepth?: number, maxElmts?: number)
|
||||
{
|
||||
this.#bounds = bounds;
|
||||
this.#bounds.x1 = Math.round(this.#bounds.x1);
|
||||
this.#bounds.x2 = Math.round(this.#bounds.x2);
|
||||
this.#bounds.y1 = Math.round(this.#bounds.y1);
|
||||
this.#bounds.y2 = Math.round(this.#bounds.y2);
|
||||
|
||||
this.#maxDepth = maxDepth ?? this.#maxDepth;
|
||||
this.#maxElmts = maxElmts ?? this.#maxElmts;
|
||||
|
||||
this.ref = ref;
|
||||
|
||||
this.#nodes.insert();
|
||||
this.#nodes.set(0, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(0, QuadConsts.NodeICount, 0);
|
||||
}
|
||||
fetch(x: number, y: number): string[]
|
||||
{
|
||||
return this.query({x1: x, x2: x, y1: y, y2: y});
|
||||
}
|
||||
query(aabb: AABB): string[]
|
||||
{
|
||||
_process.clear();
|
||||
|
||||
const leaves = this.#findLeaves(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, Math.floor(aabb.x1), Math.floor(aabb.x2), Math.floor(aabb.y1), Math.floor(aabb.y2));
|
||||
|
||||
const tmp: Record<number, boolean> = {};
|
||||
|
||||
for(let i = 0; i < leaves.length; ++i)
|
||||
{
|
||||
const index = leaves.get(i, QuadConsts.PropIIdx);
|
||||
|
||||
let node = this.#nodes.get(index, QuadConsts.NodeIFirst);
|
||||
while(node !== -1)
|
||||
{
|
||||
const elmt = this.#enodes.get(node, QuadConsts.ENodeIElmt);
|
||||
|
||||
const idIndex = this.#content.get(elmt, QuadConsts.ElmtIId);
|
||||
const id = this.#ids[idIndex];
|
||||
const canvasNode = this.ref.value.nodes?.find(e => e.id === id)!;
|
||||
|
||||
if (!tmp[elmt] && intersects(aabb.x1, aabb.y1, aabb.x2, aabb.y2, canvasNode.x, canvasNode.y, canvasNode.x + canvasNode.width, canvasNode.y + canvasNode.height))
|
||||
{
|
||||
_process.set(_process.push(), 0, idIndex);
|
||||
tmp[elmt] = true;
|
||||
}
|
||||
node = this.#enodes.get(node, QuadConsts.ENodeINext);
|
||||
}
|
||||
}
|
||||
|
||||
return _process.toArray().map(e => this.#ids[e]);
|
||||
}
|
||||
insert(id: string, aabb: AABB): number
|
||||
{
|
||||
const index = this.#content.insert();
|
||||
const idIndex = this.#ids.push(id) - 1;
|
||||
|
||||
this.#content.set(index, QuadConsts.ElmtILft, Math.floor(aabb.x1));
|
||||
this.#content.set(index, QuadConsts.ElmtIRgt, Math.floor(aabb.x2));
|
||||
this.#content.set(index, QuadConsts.ElmtITop, Math.floor(aabb.y1));
|
||||
this.#content.set(index, QuadConsts.ElmtIBtm, Math.floor(aabb.y2));
|
||||
this.#content.set(index, QuadConsts.ElmtIId, idIndex);
|
||||
|
||||
this.#insertNode(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, index);
|
||||
return index;
|
||||
}
|
||||
remove(index: number): void
|
||||
{
|
||||
if(index >= this.#content.length || index < 0)
|
||||
throw new Error("Provided index is out of bounds.");
|
||||
|
||||
const leaves = this.#findLeaves(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, this.#content.get(index, QuadConsts.ElmtILft), this.#content.get(index, QuadConsts.ElmtIRgt), this.#content.get(index, QuadConsts.ElmtITop), this.#content.get(index, QuadConsts.ElmtIBtm))
|
||||
|
||||
for(let i = 0; i < leaves.length; ++i)
|
||||
{
|
||||
const prop = leaves.get(i, QuadConsts.PropIIdx);
|
||||
|
||||
let node = this.#nodes.get(prop, QuadConsts.NodeIFirst), prev = -1;
|
||||
while(node !== -1 && this.#enodes.get(node, QuadConsts.ENodeIElmt) !== index)
|
||||
{
|
||||
prev = node;
|
||||
node = this.#enodes.get(node, QuadConsts.ENodeINext);
|
||||
}
|
||||
|
||||
if(node !== -1)
|
||||
{
|
||||
const next = this.#enodes.get(node, QuadConsts.ENodeINext);
|
||||
|
||||
if(prev == -1)
|
||||
this.#nodes.set(prop, QuadConsts.NodeIFirst, next);
|
||||
else
|
||||
this.#enodes.set(prev, QuadConsts.ENodeINext, next);
|
||||
|
||||
this.#nodes.set(prop, QuadConsts.NodeICount, this.#nodes.get(prop, QuadConsts.NodeICount) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.#content.erase(index);
|
||||
}
|
||||
clear(): void
|
||||
{
|
||||
this.#nodes.clear();
|
||||
this.#content.clear();
|
||||
this.#enodes.clear();
|
||||
this.#ids.length = 0;
|
||||
|
||||
this.#nodes.insert();
|
||||
this.#nodes.set(0, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(0, QuadConsts.NodeICount, 0);
|
||||
}
|
||||
cleanup(): void
|
||||
{
|
||||
let updated = false;
|
||||
_process.clear();
|
||||
|
||||
if(this.#nodes.get(0, QuadConsts.NodeICount) === -1)
|
||||
_process.set(_process.push(), 0, 0);
|
||||
|
||||
while(_process.length > 0)
|
||||
{
|
||||
const node = _process.get(_process.length - 1, 0);
|
||||
const fc = this.#nodes.get(node, QuadConsts.NodeIFirst);
|
||||
|
||||
let empty = 0;
|
||||
_process.pop();
|
||||
|
||||
for(let i = 0; i < 4; ++i)
|
||||
{
|
||||
const current = fc + i;
|
||||
|
||||
const count = this.#nodes.get(current, QuadConsts.NodeICount);
|
||||
|
||||
if(count === 0) //Count the amount of empty leaves
|
||||
++empty
|
||||
else if(count === -1) //Add this node to the check process if it's a branch
|
||||
_process.set(_process.push(), 0, current);
|
||||
}
|
||||
|
||||
if(empty === 4)
|
||||
{
|
||||
//Because of the way the IntList is made, it's preferable to erase in the reversed order as the last erased index becomes the first available index.
|
||||
this.#nodes.erase(fc + 3);
|
||||
this.#nodes.erase(fc + 2);
|
||||
this.#nodes.erase(fc + 1);
|
||||
this.#nodes.erase(fc + 0);
|
||||
|
||||
//The branch becomes a empty leaf
|
||||
this.#nodes.set(node, QuadConsts.NodeICount, 0);
|
||||
this.#nodes.set(node, QuadConsts.NodeIFirst, -1);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
traverse(cb: (index: number, depth: number, left: number, top: number, right: number, bottom: number, leaf: boolean) => void): void
|
||||
{
|
||||
_nodes.clear();
|
||||
|
||||
insert(_nodes, 0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2);
|
||||
|
||||
while(_nodes.length > 0)
|
||||
{
|
||||
const last = _nodes.length - 1;
|
||||
|
||||
const node = _nodes.get(last, QuadConsts.PropIIdx);
|
||||
const left = _nodes.get(last, QuadConsts.PropILft);
|
||||
const right = _nodes.get(last, QuadConsts.PropIRgt);
|
||||
const top = _nodes.get(last, QuadConsts.PropITop);
|
||||
const bottom = _nodes.get(last, QuadConsts.PropIBtm);
|
||||
const depth = _nodes.get(last, QuadConsts.PropIDpt);
|
||||
|
||||
_nodes.pop();
|
||||
|
||||
const count = this.#nodes.get(node, QuadConsts.NodeICount);
|
||||
|
||||
cb(node, depth, left, top, right, bottom, count !== -1);
|
||||
|
||||
//If it's a branch
|
||||
if (count === -1)
|
||||
{
|
||||
const fc = this.#nodes.get(node, QuadConsts.NodeIFirst);
|
||||
const mx = left + (right - left) / 2, my = top + (bottom - top) / 2;
|
||||
|
||||
insert(_nodes, fc + 0, depth + 1, left, mx, top, my);
|
||||
insert(_nodes, fc + 1, depth + 1, mx, right, top, my);
|
||||
insert(_nodes, fc + 2, depth + 1, left, mx, my, bottom);
|
||||
insert(_nodes, fc + 3, depth + 1, mx, right, my, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#findLeaves(index: number, depth: number, lleft: number, lright: number, ltop: number, lbottom: number, eleft: number, eright: number, etop: number, ebottom: number): IntList
|
||||
{
|
||||
const leaves = new IntList(QuadConsts.PropCount);
|
||||
_nodes.clear();
|
||||
insert(_nodes, index, depth, lleft, lright, ltop, lbottom);
|
||||
|
||||
while (_nodes.length > 0)
|
||||
{
|
||||
const last = _nodes.length - 1;
|
||||
|
||||
const nodeLeft = _nodes.get(last, QuadConsts.PropILft);
|
||||
const nodeRight = _nodes.get(last, QuadConsts.PropIRgt);
|
||||
const nodeTop = _nodes.get(last, QuadConsts.PropITop);
|
||||
const nodeBottom = _nodes.get(last, QuadConsts.PropIBtm);
|
||||
const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx);
|
||||
const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt);
|
||||
|
||||
_nodes.pop();
|
||||
|
||||
if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1)
|
||||
insert(leaves, nodeIndex, nodeDepth, nodeLeft, nodeRight, nodeTop, nodeBottom);
|
||||
else
|
||||
{
|
||||
const fc = this.#nodes.get(nodeIndex, QuadConsts.NodeIFirst);
|
||||
|
||||
const mx = nodeLeft + (nodeRight - nodeLeft) / 2, my = nodeTop + (nodeBottom - nodeTop) / 2;
|
||||
|
||||
if(etop <= my)
|
||||
{
|
||||
if(eleft <= mx) //Add a new
|
||||
insert(_nodes, fc + 0, nodeDepth + 1, nodeLeft, mx, nodeTop, my);
|
||||
if(eright > mx)
|
||||
insert(_nodes, fc + 1, nodeDepth + 1, mx, nodeRight, nodeTop, my);
|
||||
}
|
||||
if(ebottom > my)
|
||||
{
|
||||
if(eleft <= mx)
|
||||
insert(_nodes, fc + 2, nodeDepth + 1, nodeLeft, mx, my, nodeBottom);
|
||||
if(eright > mx)
|
||||
insert(_nodes, fc + 3, nodeDepth + 1, mx, nodeRight, my, nodeBottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leaves;
|
||||
}
|
||||
#insertLeaf(index: number, depth: number, left: number, top: number, right: number, bottom: number, elmt: number): void
|
||||
{
|
||||
const fc = this.#nodes.get(index, QuadConsts.NodeIFirst);
|
||||
this.#nodes.set(index, QuadConsts.NodeIFirst, this.#enodes.insert());
|
||||
this.#enodes.set(this.#nodes.get(index, QuadConsts.NodeIFirst), QuadConsts.ENodeINext, fc);
|
||||
this.#enodes.set(this.#nodes.get(index, QuadConsts.NodeIFirst), QuadConsts.ENodeIElmt, elmt);
|
||||
|
||||
if(this.#nodes.get(index, QuadConsts.NodeICount) === this.#maxElmts && depth < this.#maxDepth)
|
||||
{
|
||||
_process.clear();
|
||||
//For each element of the leaf
|
||||
while(this.#nodes.get(index, QuadConsts.NodeIFirst) !== -1)
|
||||
{
|
||||
const current = this.#nodes.get(index, QuadConsts.NodeIFirst);
|
||||
const next = this.#enodes.get(current, QuadConsts.ENodeINext);
|
||||
const value = this.#enodes.get(current, QuadConsts.ENodeIElmt);
|
||||
|
||||
//Remove the element
|
||||
this.#nodes.set(index, QuadConsts.NodeIFirst, next);
|
||||
this.#enodes.erase(current);
|
||||
|
||||
//Store it for reprocess
|
||||
_process.set(_process.push(), 0, value);
|
||||
}
|
||||
|
||||
const newFc = this.#nodes.insert();
|
||||
this.#nodes.set(newFc + 0, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(newFc + 0, QuadConsts.NodeICount, 0);
|
||||
|
||||
this.#nodes.insert();
|
||||
this.#nodes.set(newFc + 1, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(newFc + 1, QuadConsts.NodeICount, 0);
|
||||
|
||||
this.#nodes.insert();
|
||||
this.#nodes.set(newFc + 2, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(newFc + 2, QuadConsts.NodeICount, 0);
|
||||
|
||||
this.#nodes.insert();
|
||||
this.#nodes.set(newFc + 3, QuadConsts.NodeIFirst, -1);
|
||||
this.#nodes.set(newFc + 3, QuadConsts.NodeICount, 0);
|
||||
|
||||
this.#nodes.set(index, QuadConsts.NodeIFirst, newFc);
|
||||
|
||||
this.#nodes.set(index, QuadConsts.NodeICount, -1);
|
||||
|
||||
for(let i = 0; i < _process.length; ++i)
|
||||
this.#insertNode(index, depth, left, right, top, bottom, _process.get(i, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.#nodes.set(index, QuadConsts.NodeICount, this.#nodes.get(index, QuadConsts.NodeICount) + 1);
|
||||
}
|
||||
}
|
||||
#insertNode(index: number, depth: number, left: number, right: number, top: number, bottom: number, elmt: number): void
|
||||
{
|
||||
const leaves = this.#findLeaves(index, depth, left, right, top, bottom, this.#content.get(elmt, QuadConsts.ElmtILft), this.#content.get(elmt, QuadConsts.ElmtIRgt), this.#content.get(elmt, QuadConsts.ElmtITop), this.#content.get(elmt, QuadConsts.ElmtIBtm));
|
||||
|
||||
for(let i = 0; i < leaves.length; ++i)
|
||||
this.#insertLeaf(leaves.get(i, QuadConsts.PropIIdx), leaves.get(i, QuadConsts.PropIDpt), leaves.get(i, QuadConsts.PropILft), leaves.get(i, QuadConsts.PropITop), leaves.get(i, QuadConsts.PropIRgt), leaves.get(i, QuadConsts.PropIBtm), elmt);
|
||||
}
|
||||
}
|
||||
|
||||
function insert(list: IntList, index: number, depth: number, left: number, right: number, top: number, bottom: number): void
|
||||
{
|
||||
const idx = list.push();
|
||||
list.set(idx, QuadConsts.PropILft, left);
|
||||
list.set(idx, QuadConsts.PropIRgt, right);
|
||||
list.set(idx, QuadConsts.PropITop, top);
|
||||
list.set(idx, QuadConsts.PropIBtm, bottom);
|
||||
list.set(idx, QuadConsts.PropIIdx, index);
|
||||
list.set(idx, QuadConsts.PropIDpt, depth);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import type { Direction } from "~/shared/canvas.util";
|
||||
|
||||
export interface CanvasContent {
|
||||
nodes?: CanvasNode[];
|
||||
edges?: CanvasEdge[];
|
||||
|
|
@ -22,9 +24,9 @@ export interface CanvasNode {
|
|||
export interface CanvasEdge {
|
||||
id: string;
|
||||
fromNode: string;
|
||||
fromSide: 'bottom' | 'top' | 'left' | 'right';
|
||||
fromSide: Direction;
|
||||
toNode: string;
|
||||
toSide: 'bottom' | 'top' | 'left' | 'right';
|
||||
toSide: Direction;
|
||||
color?: CanvasColor;
|
||||
label?: string;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue