From 0e7b797a9de3d4dda7ebf80d970d0c9c3069e0a3 Mon Sep 17 00:00:00 2001
From: tdgmdev <tdgmdev@gmail.com>
Date: Tue, 28 Feb 2023 18:09:11 -0500
Subject: [PATCH] Refactored tool system

---
 src/lib/ide/Env.svelte                | 434 --------------------------
 src/lib/{ide => vector}/Color.svelte  |   0
 src/lib/vector/Env.svelte             | 266 ++++++++++++++++
 src/lib/{ide => vector}/Slider.svelte |   0
 src/lib/vector/tools/deleter.js       |  10 +
 src/lib/vector/tools/down.js          |  10 +
 src/lib/vector/tools/edit.js          |  38 +++
 src/lib/vector/tools/export.js        |  13 +
 src/lib/vector/tools/group.js         |  12 +
 src/lib/vector/tools/import.js        |   7 +
 src/lib/vector/tools/node.js          |  45 +++
 src/lib/vector/tools/pan.js           |   7 +
 src/lib/vector/tools/resize.js        |  64 ++++
 src/lib/vector/tools/rotate.js        |  16 +
 src/lib/vector/tools/select.js        |  16 +
 src/lib/vector/tools/ungroup.js       |  14 +
 src/lib/vector/tools/up.js            |  10 +
 src/routes/editor/+page.svelte        |   2 +-
 18 files changed, 529 insertions(+), 435 deletions(-)
 delete mode 100644 src/lib/ide/Env.svelte
 rename src/lib/{ide => vector}/Color.svelte (100%)
 create mode 100644 src/lib/vector/Env.svelte
 rename src/lib/{ide => vector}/Slider.svelte (100%)
 create mode 100644 src/lib/vector/tools/deleter.js
 create mode 100644 src/lib/vector/tools/down.js
 create mode 100644 src/lib/vector/tools/edit.js
 create mode 100644 src/lib/vector/tools/export.js
 create mode 100644 src/lib/vector/tools/group.js
 create mode 100644 src/lib/vector/tools/import.js
 create mode 100644 src/lib/vector/tools/node.js
 create mode 100644 src/lib/vector/tools/pan.js
 create mode 100644 src/lib/vector/tools/resize.js
 create mode 100644 src/lib/vector/tools/rotate.js
 create mode 100644 src/lib/vector/tools/select.js
 create mode 100644 src/lib/vector/tools/ungroup.js
 create mode 100644 src/lib/vector/tools/up.js

diff --git a/src/lib/ide/Env.svelte b/src/lib/ide/Env.svelte
deleted file mode 100644
index 306089d..0000000
--- a/src/lib/ide/Env.svelte
+++ /dev/null
@@ -1,434 +0,0 @@
-<style>
-    .ide {
-        width: 100vw;
-        height: calc(100vh - 50px);
-        display: grid;
-        grid-template-columns: 300px 1fr 300px;
-    }
-
-    .ide-left, .ide-mid, .ide-right {
-        height: 100%;
-    }
-
-    .ide-left  {
-        border-right: var(--border-1);
-    }
-
-    .ide-right  {
-        border-left: var(--border-1);
-    }
-
-    .ide-mid {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        overflow: none;
-    }
-
-    canvas {
-        width: 100%;
-        height: 100%;
-    }
-</style>
-
-<script>
-    
-
-    import pkg from 'paper';
-    const { paper } = pkg;
-    
-    import { onMount } from 'svelte';
-
-    import EditButton from '$lib/page/EditButton.svelte';
-    import Color from './Color.svelte';
-    import { Group } from "paper/dist/paper-core";
-    import { group_outros } from "svelte/internal";
-
-    let tools = [
-        'edit',
-        'pan',
-        'select',
-        'group',
-        'ungroup',
-        'resize',
-        'rotate',
-        'deleter',
-        'node',
-        'up',
-        'down',
-        'import',
-        'export',
-    ]
-
-    let mode = 'edit';
-    let colorType = 'stroke';
-
-    let fillColor = '#446699ff';
-    let strokeColor = '#000000ff';
-
-    let darkStroke = '#d8d8d8';
-    let white = 'white';
-
-    let nodeDist = 25;
-    let grabDist = 15;
-    
-    let color = strokeColor;
-    let stroke = 12;
-  
-    let origin = new paper.Point([0,0]);
-    
-    let viewGroup, innerGroup, canvas, mainPath, lastPoint, file, files, download;
-
-    let importFile = () => {
-        file.click();
-    }
-
-    let getBase64 = (filee) => {
-        const reader = new FileReader();
-        reader.readAsDataURL(filee);
-        reader.onload = e => {
-            let preview = e.target.result;
-            paper.project.importSVG(preview, function (item) {
-                innerGroup.addChild(item);
-                recurseChild(innerGroup);
-            });
-        };
-    }
-
-    let exportFile = () => {
-        var data = new Blob(['<svg>' + innerGroup.exportSVG({asString: true}) + '</svg>'], {type: 'image/svg'});
-
-        var url = window.URL.createObjectURL(data);
-
-        download.href = url;
-        download.click();
-    }
-
-    let setMode = (modeSet) => {
-        if (modeSet)
-            mode = modeSet;
-
-        if (mode == 'group') {
-            group();
-        } else if (mode == 'ungroup') {
-            ungroup();
-        } else if (mode == 'import') {
-            importFile();
-        } else if (mode == 'export') {
-            exportFile();
-        } else if (mode == 'deleter') {
-            deleter();
-        } else if (mode == 'up') {
-            layerUp();
-        } else if (mode == 'down') {
-            layerDown();
-        }
-
-        return mode;
-    }
-
-    let setColorMode = (modeSet) => {
-        if (modeSet)
-            colorType = modeSet;
-
-        color = (modeSet == 'stroke') ? strokeColor : fillColor;
-
-        return colorType;
-    }
-
-    let setColor = ([colorIn, strokeIn]) => {
-        color = colorIn;
-        stroke = strokeIn / 5;
-
-        if (colorType == 'stroke') {
-            strokeColor = color
-        } else {
-            fillColor = color;
-        }
-
-        if (!paper.project || !paper.project.selectedItems) return;
-
-        paper.project.selectedItems.forEach(item => {
-            if (colorType == 'stroke') {
-                item.strokeColor = color
-            } else {
-                item.fillColor = color;
-            }
-        })
-    }
-
-    let initPath = () => {
-        mainPath = new paper.Path();
-
-        mainPath.strokeColor = strokeColor;
-        mainPath.fillColor = fillColor;
-        mainPath.strokeWidth = stroke;
-        mainPath.strokeCap = 'butt';
-        mainPath.strokeJoin = 'bevel';
-
-        innerGroup.addChild(mainPath);
-
-        recurseChild(innerGroup);
-
-    }
-
-    let mouseDown = (event) => {
-        if (mode == 'edit') {
-            initPath();
-        }
-    }
-
-    let layerDown = () => {
-        paper.project.selectedItems.reverse().forEach(item => {
-            item.sendToBack();
-        })
-    };
-
-    let layerUp = () => {
-        paper.project.selectedItems.forEach(item => {
-            item.bringToFront();
-        })
-    };
-
-    let group = () => {
-        var group = new paper.Group(paper.project.selectedItems);
-        innerGroup.addChild(group);
-    };
-
-    let ungroup = () => {
-        paper.project.selectedItems.forEach(item => {
-            if (!item.children || item.className != 'Group' || item == innerGroup) return;
-            item.selected = false;
-            item.children.forEach(child => {
-                innerGroup.addChild(child);
-            })
-        })
-    }
-
-    let deleter = () => {
-        paper.project.selectedItems.forEach(item => {
-            console.log(item);
-            item.remove();
-        })
-    }
-
-    let mouseDownInner = (event) => {
-        event.stopPropagation();
-
-        console.log(event.target);
-
-        var target = event.target;
-
-        while (target.parent != innerGroup) {
-            target = target.parent;
-        }
-
-        console.log(target);
-
-        if (mode == 'select') {
-            target.selected = !target.selected;
-        } else if (mode == 'node') {
-            var nearestPoint = target.getNearestPoint(event.point);
-
-            if (nearestPoint.getDistance(event.point) > nodeDist) return;
-
-            target.segments.forEach(segment => {
-                segment.selected = false;
-            });
-
-            let closestPoint = target.segments[0];
-            let closestDist = 10000;
-
-            target.segments.forEach(segment => {
-                let dist = segment.point.getDistance(nearestPoint);
-                if (dist < closestDist) {
-                    closestDist = dist;
-                    closestPoint = segment.point;
-                }
-            });
-
-            let newPoint;
-
-            if (nearestPoint.getDistance(closestPoint) > grabDist) {
-                newPoint = target.divideAt(target.getLocationOf(nearestPoint));
-            } else {
-                newPoint = closestPoint;
-            }
-
-            newPoint.selected = true;
-        } else if (mode == 'resize' || mode == 'rotate') {
-            target.selected = !target.selected;
-            target.bounds.selected = target.selected;
-        }
-    }
-
-    let mouseDrag = (event) => {
-        if (mode == 'pan') {
-            viewGroup.position = viewGroup.position.add(event.delta);
-        } else if (mode == 'edit') {
-            lastPoint = event.point;
-            
-            mainPath.add(event.point);
-        } else if (mode == 'select' || mode == 'node') {
-            paper.project.selectedItems.forEach(item => {
-                if ((mode == 'select' && item.parent != innerGroup)) return;
-
-                if (mode == 'node') {
-                    if (item.className == 'Path') {
-                        item.segments.forEach(segment => {
-                            if (segment.selected)
-                                segment.point = segment.point.add(event.delta)
-                        })
-                    }
-                } else {
-                    item.position = item.position.add(event.delta)
-                }
-
-                
-            });
-        } else if (mode == 'resize') {
-            paper.project.selectedItems.forEach(item => {
-                var nearestPoint = event.point;
-
-                if (!item.bounds.selected) return;
-
-                let seg = [
-                    item.bounds.topLeft,
-                    item.bounds.topRight,
-                    item.bounds.bottomLeft,
-                    item.bounds.bottomRight,
-                    item.bounds.topCenter,
-                    item.bounds.bottomCenter,
-                    item.bounds.leftCenter,
-                    item.bounds.rightCenter
-                ];
-
-                let segOpp = [
-                    item.bounds.bottomRight,
-                    item.bounds.bottomLeft,
-                    item.bounds.topRight,
-                    item.bounds.topLeft,
-                    item.bounds.bottomCenter,
-                    item.bounds.topCenter,
-                    item.bounds.rightCenter,
-                    item.bounds.leftCenter
-                ]
-
-                let closestPoint = seg[0];
-                let closestDist = 10000;
-                let i = -1;
-
-                seg.forEach((segment,index) => {
-                    let dist = segment.getDistance(nearestPoint);
-                    if (dist < closestDist) {
-                        closestDist = dist;
-                        closestPoint = segOpp[index]; 
-                        i = index;
-                    }
-                });
-
-                if (closestDist < nodeDist) {
-                    var signX = (i == 1 || i == 3 || i == 7) ? 1 : -1;
-                    var signY = (i == 1 || i == 0 || i == 4) ? -1 : 1;
-                    if (i > 5) {
-                        item.scale(1 + signX *  event.delta.x / item.bounds.width, 1, closestPoint);
-                    } else if (i > 3) {
-                        item.scale(1, 1 + signY * event.delta.y / item.bounds.height, closestPoint);
-                    } else {
-                        item.scale(1 + signX * event.delta.x / item.bounds.width, 1 + signY * event.delta.y / item.bounds.height, closestPoint);
-                    }
-                }  
-            });
-        } else if (mode == 'rotate') {
-            paper.project.selectedItems.forEach(item => {
-                if (!item.bounds.selected) return;
-                var rotAngle = event.point.subtract(item.bounds.center).angle - event.point.add(event.delta).subtract(item.bounds.center).angle;
-                item.rotate(-rotAngle);
-            });
-        }
-    }
-
-    let mouseUp = (event) => {
-        if (mode == 'edit') {
-            if (mainPath.lastSegment.point.getDistance(mainPath.firstSegment.point) < nodeDist) {
-                mainPath.closePath();
-            }
-        }
-    }
-
-    let mouseDragInner = (event) => {
-
-    }
-
-    let recurseChild = (parent) => {
-        if (!parent.children || parent.children.length == 0) {
-            parent.onMouseDrag = mouseDragInner;
-            parent.onDoubleClick = mouseDownInner;
-            return;
-        } else {
-            parent.onMouseDrag = () => {};
-            parent.onDoubleClick = () => {};
-        }
-
-        parent.children.forEach(child => {
-            if (child == parent) return;
-            recurseChild(child);
-        })
-    }
-
-    let initView = () => {
-        let rect = new paper.Rectangle(0,0,480,360);
-
-        let path = new paper.Path.Rectangle(rect);
-        path.fillColor = 'white';
-
-        let pathOut = new paper.Path.Rectangle(rect);
-
-        pathOut.strokeColor = darkStroke;
-        pathOut.strokeWidth = '3';
-
-        innerGroup = new paper.Group([]);
-
-        viewGroup = new paper.Group([path,innerGroup,pathOut]);
-
-        /*paper.project.importSVG("/logo.svg", function (item) {
-            innerGroup.addChild(item);
-            recurseChild(innerGroup);
-        })*/
-
-        viewGroup.onMouseDrag = mouseDrag;
-        viewGroup.onMouseDown = mouseDown;
-        viewGroup.onMouseUp = mouseUp;
-    }
-    
-
-    onMount(() => {
-        if (!canvas) return;
-        
-        paper.setup(canvas);
-
-        initView();
-        initPath();
-
-        console.log(paper.view);
-    });
-
-</script>
-
-<div class='ide'>
-    <div class='ide-left'>
-        {#each tools as tool}
-            <EditButton bindMode={setMode} modeIn={mode} mode={tool} />
-        {/each}
-    </div>
-    <div class='ide-mid'>
-        <canvas class='canvas' resize bind:this={canvas}></canvas>
-    </div>
-    <div class='ide-right'>
-        <EditButton bindMode={setColorMode} modeIn={colorType} mode={'stroke'} />
-        <EditButton bindMode={setColorMode} modeIn={colorType} mode={'fill'} />
-        <Color bindColor={setColor} color={color} />    
-        <input type="file"  style="display:none" bind:files on:change={() => getBase64(files[0])} bind:this={file}>
-        <a download="export.svg" bind:this={download} hidden></a>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/lib/ide/Color.svelte b/src/lib/vector/Color.svelte
similarity index 100%
rename from src/lib/ide/Color.svelte
rename to src/lib/vector/Color.svelte
diff --git a/src/lib/vector/Env.svelte b/src/lib/vector/Env.svelte
new file mode 100644
index 0000000..fc72cc7
--- /dev/null
+++ b/src/lib/vector/Env.svelte
@@ -0,0 +1,266 @@
+<style>
+    .ide {
+        width: 100vw;
+        height: calc(100vh - 50px);
+        display: grid;
+        grid-template-columns: 300px 1fr 300px;
+    }
+
+    .ide-left, .ide-mid, .ide-right {
+        height: 100%;
+    }
+
+    .ide-left  {
+        border-right: var(--border-1);
+    }
+
+    .ide-right  {
+        border-left: var(--border-1);
+    }
+
+    .ide-mid {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        overflow: none;
+    }
+
+    canvas {
+        width: 100%;
+        height: 100%;
+    }
+</style>
+
+<script>
+    
+
+    import pkg from 'paper';
+    const { paper } = pkg;
+    
+    import { onMount } from 'svelte';
+
+    import EditButton from '$lib/page/EditButton.svelte';
+    import Color from './Color.svelte';
+    import { Group } from "paper/dist/paper-core";
+    import { group_outros } from "svelte/internal";
+
+    let tools = [
+        'edit',
+        'pan',
+        'select',
+        'group',
+        'ungroup',
+        'resize',
+        'rotate',
+        'deleter',
+        'node',
+        'up',
+        'down',
+        'import',
+        'export',
+    ]
+
+    let toolData = {};
+
+    tools.forEach(async tool => {
+        toolData[tool] = await import(`./tools/${tool}.js`);
+    })
+
+    
+    let viewGroup, canvas, mainPath, lastPoint, file, files, download, settings;
+
+    let darkStroke = '#000000';
+
+    let mode = 'edit';
+
+    settings = {
+        colorType: 'stroke',
+
+        fill: {
+            color: '#446699ff',
+            stroke: '#000000ff'
+        },
+
+        color: '#000000ff',
+
+        stroke: 12,
+
+        innerGroup: null,
+
+        viewGroup: null,
+
+        mainPath: null,
+
+        nodeDist: 45,
+
+        grabDist: 15,
+
+        doc: {
+            
+        }
+
+    };
+
+    let getBase64 = (filee) => {
+        const reader = new FileReader();
+        reader.readAsDataURL(filee);
+        reader.onload = e => {
+            let preview = e.target.result;
+            paper.project.importSVG(preview, function (item) {
+                settings.innerGroup.addChild(item);
+                recurseChild(settings.innerGroup);
+            });
+        };
+    }
+  
+    let execFunc = (obj, val, args) => {
+        obj = obj.toolData;
+        if (obj[val]) {
+            let result = obj[val]({
+                ...args, 
+                ...settings, 
+                delta: args.delta, 
+                point: args.point,
+                target: args.target,
+                selectedItems: paper.project.selectedItems
+            });
+            settings = result || settings;
+            recurseChild(settings.innerGroup);
+        }
+    }
+
+    let origin = new paper.Point([0,0]);
+
+
+    let setMode = (modeSet) => {
+        if (modeSet)
+            mode = modeSet;
+
+        execFunc(toolData[mode],'run',[])
+
+        return mode;
+    }
+
+    let setColorMode = (modeSet) => {
+        if (modeSet)
+            settings.colorType = modeSet;
+
+        color = (modeSet == 'stroke') ? settings.fill.stroke : settings.fill.color;
+
+        return settings.colorType;
+    }
+
+    let setColor = ([colorIn, strokeIn]) => {
+        settings.color = colorIn;
+        settings.stroke = strokeIn / 5;
+
+        if (settings.colorType == 'stroke') {
+            settings.fill.stroke = settings.color
+        } else {
+            settings.fill.color = settings.color;
+        }
+
+        if (!paper.project || !paper.project.selectedItems) return;
+
+        paper.project.selectedItems.forEach(item => {
+            if (settings.colorType == 'stroke') {
+                item.strokeColor = settings.color
+            } else {
+                item.fillColor = settings.color;
+            }
+        })
+    }
+
+    let mouseEvent = (type) => {
+        return (event) => {
+            execFunc(toolData[mode],type,event);
+        }
+    }
+
+    let mouseEventInner = (type) => {
+        return (event) => {
+            event.stopPropagation();
+
+            var target = event.target;
+
+            while (target.parent != settings.innerGroup && target.parent != settings.viewGroup && target.parent) {
+                target = target.parent;
+            }
+
+            event.target = target;
+
+            execFunc(toolData[mode],type,event);
+        }
+    }
+
+    let recurseChild = (parent) => {
+        if (!parent.children || parent.children.length == 0) {
+            parent.onMouseDrag = mouseEventInner('mouseDragInner');
+            parent.onDoubleClick = mouseEventInner('doubleClickInner');
+            return;
+        } else {
+            parent.onMouseDrag = () => {};
+            parent.onDoubleClick = () => {};
+        }
+
+        parent.children.forEach(child => {
+            if (child == parent) return;
+            recurseChild(child);
+        })
+    }
+
+    let initView = () => {
+        let rect = new paper.Rectangle(0,0,480,360);
+
+        let path = new paper.Path.Rectangle(rect);
+        path.fillColor = 'white';
+
+        let pathOut = new paper.Path.Rectangle(rect);
+
+        pathOut.strokeColor = darkStroke;
+        pathOut.strokeWidth = '3';
+
+        settings.innerGroup = new paper.Group([]);
+
+        settings.viewGroup = new paper.Group([path,settings.innerGroup,pathOut]);
+
+        /*paper.project.importSVG("/logo.svg", function (item) {
+            innerGroup.addChild(item);
+            recurseChild(innerGroup);
+        })*/
+
+        settings.viewGroup.onMouseDrag = mouseEvent('mouseDrag');
+        settings.viewGroup.onMouseDown = mouseEvent('mouseDown');
+        settings.viewGroup.onMouseUp = mouseEvent('mouseUp');
+    }
+    
+
+    onMount(() => {
+        if (!canvas) return;
+        
+        paper.setup(canvas);
+
+        initView();
+
+        console.log(paper.view);
+    });
+
+</script>
+
+<div class='ide'>
+    <div class='ide-left'>
+        {#each tools as tool}
+            <EditButton bindMode={setMode} modeIn={mode} mode={tool} />
+        {/each}
+    </div>
+    <div class='ide-mid'>
+        <canvas class='canvas' resize bind:this={canvas}></canvas>
+    </div>
+    <div class='ide-right'>
+        <EditButton bindMode={setColorMode} modeIn={settings.colorType} mode={'stroke'} />
+        <EditButton bindMode={setColorMode} modeIn={settings.colorType} mode={'fill'} />
+        <Color bindColor={setColor} color={settings.color} />    
+        <input type="file"  style="display:none" bind:files on:change={() => getBase64(files[0])} bind:this={settings.doc.file}>
+        <a download="export.svg" bind:this={settings.doc.download} hidden></a>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/lib/ide/Slider.svelte b/src/lib/vector/Slider.svelte
similarity index 100%
rename from src/lib/ide/Slider.svelte
rename to src/lib/vector/Slider.svelte
diff --git a/src/lib/vector/tools/deleter.js b/src/lib/vector/tools/deleter.js
new file mode 100644
index 0000000..606104f
--- /dev/null
+++ b/src/lib/vector/tools/deleter.js
@@ -0,0 +1,10 @@
+
+let toolData = {
+    run: ({ selectedItems }) => { 
+        selectedItems.forEach(item => {
+            item.remove();
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/down.js b/src/lib/vector/tools/down.js
new file mode 100644
index 0000000..885f15d
--- /dev/null
+++ b/src/lib/vector/tools/down.js
@@ -0,0 +1,10 @@
+
+let toolData = {
+    run: ({ selectedItems }) => { 
+        selectedItems.reverse().forEach(item => {
+            item.sendToBack();
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/edit.js b/src/lib/vector/tools/edit.js
new file mode 100644
index 0000000..2edc4a0
--- /dev/null
+++ b/src/lib/vector/tools/edit.js
@@ -0,0 +1,38 @@
+import pkg from 'paper';
+const { paper } = pkg;
+
+let lastPoint;
+
+let toolData = {
+    mouseDown: (settings) => {
+        let { innerGroup, mainPath, fill, stroke } = settings;
+        mainPath = new paper.Path();
+
+        mainPath.strokeColor = fill.stroke;
+        mainPath.fillColor = fill.color;
+
+        mainPath.strokeWidth = stroke;
+        mainPath.strokeCap = 'butt';
+        mainPath.strokeJoin = 'bevel';
+
+        innerGroup.addChild(mainPath);
+
+        settings.mainPath = mainPath;
+        return settings;
+
+    },
+
+    mouseDrag: ( {mainPath, point}) => {
+        lastPoint = point;
+            
+        mainPath.add(point);
+    },
+
+    mouseUp: ( {mainPath, nodeDist }) => {
+        if (mainPath.lastSegment.point.getDistance(mainPath.firstSegment.point) < nodeDist) {
+            mainPath.closePath();
+        }
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/export.js b/src/lib/vector/tools/export.js
new file mode 100644
index 0000000..bb2d9f8
--- /dev/null
+++ b/src/lib/vector/tools/export.js
@@ -0,0 +1,13 @@
+
+let toolData = {
+    run: ({ innerGroup, doc }) => {
+        var data = new Blob(['<svg>' + innerGroup.exportSVG({asString: true}) + '</svg>'], {type: 'image/svg'});
+
+        var url = window.URL.createObjectURL(data);
+
+        doc.download.href = url;
+        doc.download.click();
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/group.js b/src/lib/vector/tools/group.js
new file mode 100644
index 0000000..2e3301e
--- /dev/null
+++ b/src/lib/vector/tools/group.js
@@ -0,0 +1,12 @@
+
+import pkg from 'paper';
+const { paper } = pkg;
+
+let toolData = {
+    run: ({ selectedItems, innerGroup }) => { 
+        var group = new paper.Group(selectedItems);
+        innerGroup.addChild(group);
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/import.js b/src/lib/vector/tools/import.js
new file mode 100644
index 0000000..a6b3bf9
--- /dev/null
+++ b/src/lib/vector/tools/import.js
@@ -0,0 +1,7 @@
+let toolData = {
+    run: ({ doc }) => {
+        doc.file.click();
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/node.js b/src/lib/vector/tools/node.js
new file mode 100644
index 0000000..6c01642
--- /dev/null
+++ b/src/lib/vector/tools/node.js
@@ -0,0 +1,45 @@
+let toolData = {
+    doubleClickInner: (event) => {
+        var nearestPoint = event.target.getNearestPoint(event.point);
+
+        if (nearestPoint.getDistance(event.point) > event.nodeDist) return;
+
+        event.target.segments.forEach(segment => {
+            segment.selected = false;
+        });
+
+        let closestPoint = event.target.segments[0];
+        let closestDist = 10000;
+
+        event.target.segments.forEach(segment => {
+            let dist = segment.point.getDistance(nearestPoint);
+            if (dist < closestDist) {
+                closestDist = dist;
+                closestPoint = segment.point;
+            }
+        });
+
+        let newPoint;
+
+        if (nearestPoint.getDistance(closestPoint) > event.grabDist) {
+            newPoint = event.target.divideAt(event.target.getLocationOf(nearestPoint));
+        } else {
+            newPoint = closestPoint;
+        }
+
+        newPoint.selected = true;
+    },
+
+    mouseDrag : ( {selectedItems, delta} ) => {
+        selectedItems.forEach(item => {
+            if (item.className == 'Path') {
+                item.segments.forEach(segment => {
+                    if (segment.selected)
+                        segment.point = segment.point.add(delta)
+                })
+            }
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/pan.js b/src/lib/vector/tools/pan.js
new file mode 100644
index 0000000..a872ce5
--- /dev/null
+++ b/src/lib/vector/tools/pan.js
@@ -0,0 +1,7 @@
+let toolData = {
+    mouseDrag: ( { viewGroup, delta } ) => {
+        viewGroup.position = viewGroup.position.add(delta);
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/resize.js b/src/lib/vector/tools/resize.js
new file mode 100644
index 0000000..2358e76
--- /dev/null
+++ b/src/lib/vector/tools/resize.js
@@ -0,0 +1,64 @@
+let toolData = {
+    doubleClickInner: ({ target }) => {
+        target.selected = !target.selected;
+        target.bounds.selected = target.selected;
+    },
+
+    mouseDrag: ( {selectedItems, point, delta, nodeDist} ) => {
+        selectedItems.forEach(item => {
+            var nearestPoint = point;
+
+            if (!item.bounds.selected) return;
+
+
+            let seg = [
+                item.bounds.topLeft,
+                item.bounds.topRight,
+                item.bounds.bottomLeft,
+                item.bounds.bottomRight,
+                item.bounds.topCenter,
+                item.bounds.bottomCenter,
+                item.bounds.leftCenter,
+                item.bounds.rightCenter
+            ];
+
+            let segOpp = [
+                item.bounds.bottomRight,
+                item.bounds.bottomLeft,
+                item.bounds.topRight,
+                item.bounds.topLeft,
+                item.bounds.bottomCenter,
+                item.bounds.topCenter,
+                item.bounds.rightCenter,
+                item.bounds.leftCenter
+            ]
+
+            let closestPoint = seg[0];
+            let closestDist = 10000;
+            let i = -1;
+
+            seg.forEach((segment,index) => {
+                let dist = segment.getDistance(nearestPoint);
+                if (dist < closestDist) {
+                    closestDist = dist;
+                    closestPoint = segOpp[index]; 
+                    i = index;
+                }
+            });
+
+            if (closestDist < nodeDist) {
+                var signX = (i == 1 || i == 3 || i == 7) ? 1 : -1;
+                var signY = (i == 1 || i == 0 || i == 4) ? -1 : 1;
+                if (i > 5) {
+                    item.scale(1 + signX *  delta.x / item.bounds.width, 1, closestPoint);
+                } else if (i > 3) {
+                    item.scale(1, 1 + signY * delta.y / item.bounds.height, closestPoint);
+                } else {
+                    item.scale(1 + signX * delta.x / item.bounds.width, 1 + signY * delta.y / item.bounds.height, closestPoint);
+                }
+            }
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/rotate.js b/src/lib/vector/tools/rotate.js
new file mode 100644
index 0000000..dce4ba9
--- /dev/null
+++ b/src/lib/vector/tools/rotate.js
@@ -0,0 +1,16 @@
+let toolData = {
+    doubleClickInner: ({ target }) => {
+        target.selected = !target.selected;
+        target.bounds.selected = target.selected;
+    },
+
+    mouseDrag: ( {selectedItems, point, delta} ) => {
+        selectedItems.forEach(item => {
+            if (!item.bounds.selected) return;
+            var rotAngle = point.subtract(item.bounds.center).angle - point.add(delta).subtract(item.bounds.center).angle;
+            item.rotate(-rotAngle);
+        });
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/select.js b/src/lib/vector/tools/select.js
new file mode 100644
index 0000000..8ea16d7
--- /dev/null
+++ b/src/lib/vector/tools/select.js
@@ -0,0 +1,16 @@
+let toolData = {
+    doubleClickInner: ({ target }) => {
+        target.selected = !target.selected;
+    },
+
+    mouseDragInner: ( {selectedItems, delta, innerGroup} ) => {
+        selectedItems.forEach(item => {
+            if ((item.parent != innerGroup)) return;
+
+            item.position = item.position.add(delta)
+        })
+
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/ungroup.js b/src/lib/vector/tools/ungroup.js
new file mode 100644
index 0000000..d8fbb35
--- /dev/null
+++ b/src/lib/vector/tools/ungroup.js
@@ -0,0 +1,14 @@
+
+let toolData = {
+    run: ({ selectedItems, innerGroup }) => { 
+        selectedItems.forEach(item => {
+            if (!item.children || item.className != 'Group' || item == innerGroup) return;
+            item.selected = false;
+            item.children.forEach(child => {
+                innerGroup.addChild(child);
+            })
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/lib/vector/tools/up.js b/src/lib/vector/tools/up.js
new file mode 100644
index 0000000..a23c93a
--- /dev/null
+++ b/src/lib/vector/tools/up.js
@@ -0,0 +1,10 @@
+
+let toolData = {
+    run: ({ selectedItems }) => { 
+        selectedItems.forEach(item => {
+            item.bringToFront();
+        })
+    }
+}
+
+export { toolData };
\ No newline at end of file
diff --git a/src/routes/editor/+page.svelte b/src/routes/editor/+page.svelte
index 07397d2..1fa5c06 100644
--- a/src/routes/editor/+page.svelte
+++ b/src/routes/editor/+page.svelte
@@ -1,5 +1,5 @@
 <script>
-    import Env from '$lib/ide/Env.svelte'
+    import Env from '$lib/vector/Env.svelte'
 </script>
 
 <Env />
\ No newline at end of file