{"Document":{"ObjectName":"Scenario::ScenarioDocumentModel","id":1,"BaseScenario":{"ObjectName":"Scenario::BaseScenario","id":0,"Constraint":{"ObjectName":"Scenario::IntervalModel","id":0,"Metadata":{"ScriptingName":"video-mapping-rect","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Inlet":{"uuid":"a1574bb0-cbd4-4c7d-9417-0c25cfd1187b","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Audio In","Exposed":"audio in"},"Outlet":{"uuid":"a1d97535-18ac-444a-8417-0cbc1692d897","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Audio Out","Exposed":"audio out","Address":"audio:/out/main","GainInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10000,"Hidden":false,"Custom":"Gain","Exposed":"gain","Value":{},"Init":{},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},"PanInlet":{"uuid":"9a13fb32-269a-47bf-99a9-930188c1f19c","ObjectName":"Inlet","id":10001,"Hidden":false,"Custom":"Pan","Exposed":"pan","Value":{},"Init":{},"Domain":{}},"Gain":1.0,"Pan":[1.0,1.0],"Propagate":true},"Processes":[{"uuid":"846a5de5-47f9-46c5-a898-013cb20951d0","ObjectName":"Javascript","id":1,"Metadata":{"ScriptingName":"rect-mapper","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-55.06506648769448,-225.39099999999996],"Size":[203.0,160.0],"Loops":false,"FoldMode":0,"Script":"import Score\nimport QtQuick\nimport QtQuick3D\nimport QtQuick3D.Helpers\nimport \"MeshUtils.js\" as MeshUtils\nimport \"ShapeData.js\" as ShapeData\n\nScript {\n    id: root\n\n    TextureInlet {\n        id: tex0\n        objectName: \"Texture 1\"\n    }\n    TextureInlet {\n        id: tex1\n        objectName: \"Texture 2\"\n    }\n    TextureInlet {\n        id: tex2\n        objectName: \"Texture 3\"\n    }\n    TextureInlet {\n        id: tex3\n        objectName: \"Texture 4\"\n    }\n    TextureInlet {\n        id: tex4\n        objectName: \"Texture 5\"\n    }\n    TextureInlet {\n        id: tex5\n        objectName: \"Texture 6\"\n    }\n    TextureInlet {\n        id: tex6\n        objectName: \"Texture 7\"\n    }\n    TextureInlet {\n        id: tex7\n        objectName: \"Texture 8\"\n    }\n\n    IntSlider {\n        id: subdiv\n        objectName: \"Subdivisions\"\n        min: 4\n        max: 64\n        init: 12\n    }\n    ValueInlet {\n        id: rectsIn\n        objectName: \"Quads\"\n    }\n\n    readonly property string rootPath: \"file:///\" + Util.settings(\"d6966670-f69f-48d0-96f6-72a5e2190cbc\").RootPath + \"/packages/default/Presets/Javascript/rect-mapper/\"\n    TextureOutlet {\n        objectName: \"Output\"\n        item: Item {\n            id: outputRoot\n            anchors.fill: parent\n\n            View3D {\n                id: renderRoot\n                anchors.fill: parent\n\n                environment: SceneEnvironment {\n                    clearColor: \"black\"\n                    backgroundMode: SceneEnvironment.Color\n                    antialiasingQuality: SceneEnvironment.High\n                    antialiasingMode: SceneEnvironment.MSAA\n                }\n\n                OrthographicCamera {\n                    id: camera\n                    z: 100\n                }\n\n                Texture {\n                    id: texSrc0\n                    sourceItem: tex0.item\n                }\n                Texture {\n                    id: texSrc1\n                    sourceItem: tex1.item\n                }\n                Texture {\n                    id: texSrc2\n                    sourceItem: tex2.item\n                }\n                Texture {\n                    id: texSrc3\n                    sourceItem: tex3.item\n                }\n                Texture {\n                    id: texSrc4\n                    sourceItem: tex4.item\n                }\n                Texture {\n                    id: texSrc5\n                    sourceItem: tex5.item\n                }\n                Texture {\n                    id: texSrc6\n                    sourceItem: tex6.item\n                }\n                Texture {\n                    id: texSrc7\n                    sourceItem: tex7.item\n                }\n\n                Repeater3D {\n                    model: root.rects.length\n\n                    Model {\n                        id: quadModel\n                        property int idx: index\n                        visible: root.getVisible(quadModel.idx)\n\n                        property var meshData: {\n                            // Explicit dependencies so QML re-evaluates on changes\n                            var _v = root.stateVersion;\n                            var _s = root.subdivisions;\n                            return root.computeMesh(quadModel.idx);\n                        }\n                        geometry: ProceduralMesh {\n                            positions: quadModel.meshData.positions\n                            normals: quadModel.meshData.normals\n                            uv0s: quadModel.meshData.uvs\n                            indexes: quadModel.meshData.indices\n                        }\n\n                        materials: CustomMaterial {\n                            shadingMode: CustomMaterial.Unshaded\n                            cullMode: CustomMaterial.NoCulling\n\n                            readonly property var blendModes: [\n                                CustomMaterial.Zero, CustomMaterial.One,\n                                CustomMaterial.SrcColor, CustomMaterial.OneMinusSrcColor,\n                                CustomMaterial.DstColor, CustomMaterial.OneMinusDstColor,\n                                CustomMaterial.SrcAlpha, CustomMaterial.OneMinusSrcAlpha,\n                                CustomMaterial.DstAlpha, CustomMaterial.OneMinusDstAlpha\n                            ]\n                            sourceBlend: blendModes[root.getSrcBlend(quadModel.idx)]\n                            destinationBlend: blendModes[root.getDstBlend(quadModel.idx)]\n\n                            property TextureInput srcTex: TextureInput {\n                                texture: root.getTexSrc(quadModel.idx)\n                            }\n                            property real blendTop: root.getBlend(quadModel.idx, \"top\")\n                            property real blendBottom: root.getBlend(quadModel.idx, \"bottom\")\n                            property real blendLeft: root.getBlend(quadModel.idx, \"left\")\n                            property real blendRight: root.getBlend(quadModel.idx, \"right\")\n                            property real blendGamma: root.getBlendGamma(quadModel.idx)\n                            property real shapeOpacity: root.getOpacity(quadModel.idx)\n                            property vector2d uvOffset: root.getUvOffset(quadModel.idx)\n                            property vector2d uvScale: root.getUvScale(quadModel.idx)\n                            property real uvRotation: root.getUvRotation(quadModel.idx)\n                            property real manualUv: root.isManualUv(quadModel.idx)\n\n                            vertexShader: `${rootPath}/edgeblend.vert`\n                            fragmentShader: `${rootPath}/edgeblend.frag`\n                        }\n                    }\n                }\n            }\n\n            // Shape outline & vertex overlay\n            Canvas {\n                anchors.fill: parent\n                visible: root.showOverlay\n\n                readonly property var overlayColors: [\"#ff4444\", \"#44ff44\", \"#4444ff\", \"#ffff44\", \"#ff44ff\", \"#44ffff\", \"#ff8844\", \"#88ff44\"]\n\n                property int ver: root.stateVersion\n                onVerChanged: requestPaint()\n\n                onPaint: {\n                    var ctx = getContext(\"2d\");\n                    var w = width, h = height;\n                    ctx.clearRect(0, 0, w, h);\n\n                    for (var qi = 0; qi < root.rects.length; qi++) {\n                        var r = root.rects[qi];\n                        var c = r.vertices;\n                        if (!c || c.length < 3) continue;\n                        if (!root.getVisible(qi)) continue;\n\n                        var col = overlayColors[r.source || 0];\n                        var edges = r.edges;\n\n                        // Shape outline\n                        ctx.beginPath();\n                        ctx.moveTo(c[0][0] * w, c[0][1] * h);\n                        for (var ci = 0; ci < c.length; ci++) {\n                            var nextIdx = (ci + 1) % c.length;\n                            var edge = (edges && ci < edges.length) ? edges[ci] : null;\n                            if (edge && edge.cp1 && edge.cp2) {\n                                ctx.bezierCurveTo(\n                                    edge.cp1[0] * w, edge.cp1[1] * h,\n                                    edge.cp2[0] * w, edge.cp2[1] * h,\n                                    c[nextIdx][0] * w, c[nextIdx][1] * h\n                                );\n                            } else {\n                                ctx.lineTo(c[nextIdx][0] * w, c[nextIdx][1] * h);\n                            }\n                        }\n                        ctx.strokeStyle = col;\n                        ctx.lineWidth = 2;\n                        ctx.stroke();\n\n                        // Vertex dots\n                        for (var vi = 0; vi < c.length; vi++) {\n                            var vx = c[vi][0] * w, vy = c[vi][1] * h;\n                            ctx.beginPath();\n                            ctx.arc(vx, vy, 4, 0, 2 * Math.PI);\n                            ctx.fillStyle = \"#fff\";\n                            ctx.fill();\n                            ctx.strokeStyle = col;\n                            ctx.lineWidth = 1.5;\n                            ctx.stroke();\n                        }\n\n                        // Shape label\n                        var sx = 0, sy = 0;\n                        for (var vi = 0; vi < c.length; vi++) {\n                            sx += c[vi][0]; sy += c[vi][1];\n                        }\n                        sx = (sx / c.length) * w;\n                        sy = (sy / c.length) * h;\n                        ctx.font = \"bold 11px sans-serif\";\n                        ctx.textAlign = \"center\";\n                        ctx.textBaseline = \"middle\";\n                        ctx.strokeStyle = \"#000\";\n                        ctx.lineWidth = 3;\n                        ctx.strokeText(r.name, sx, sy);\n                        ctx.fillStyle = \"#fff\";\n                        ctx.fillText(r.name, sx, sy);\n                    }\n                }\n            }\n        }\n    }\n\n    property var rects: []\n    property int stateVersion: 0\n    property int soloIndex: -1\n    property bool showOverlay: false\n    property real lastSentW: 0\n    property real lastSentH: 0\n    readonly property int subdivisions: subdiv.value\n\n    // Per-shape mesh cache\n    property var meshCacheData: []\n    property var meshCacheKeys: []\n\n    function computeMesh(idx) {\n        if (idx < 0 || idx >= rects.length || !rects[idx].vertices)\n            return { positions: [], normals: [], uvs: [], indices: [] };\n\n        var shape = rects[idx];\n        var w = renderRoot.width;\n        var h = renderRoot.height;\n        var N = root.subdivisions;\n        var z = idx * 0.1;\n\n        var key = MeshUtils.meshCacheKey(shape, N, w, h);\n        if (idx < meshCacheKeys.length && meshCacheKeys[idx] === key)\n            return meshCacheData[idx];\n\n        var result;\n        var uvMode = shape.uvMode || \"auto\";\n        var isSimpleQuad = (shape.vertices.length === 4 && (!shape.edges || shape.edges.length === 0));\n\n        if (uvMode === \"interpolated\") {\n            // Interpolated: quads use existing warp paths; non-quads force warped mesh\n            if (isSimpleQuad && shape.warp && shape.gridOffsets) {\n                result = MeshUtils.computeQuadMeshWithGrid(shape.vertices, shape.gridW || 4, shape.gridH || 4, shape.gridOffsets, N, w, h, z);\n            } else if (isSimpleQuad) {\n                result = MeshUtils.computeQuadMesh(shape.vertices, N, w, h, z);\n            } else {\n                result = MeshUtils.computePolygonMeshWarped(shape.vertices, shape.edges, N, w, h, z);\n            }\n        } else {\n            // auto, aligned, manual: use standard logic\n            if (isSimpleQuad && shape.warp && shape.gridOffsets) {\n                result = MeshUtils.computeQuadMeshWithGrid(shape.vertices, shape.gridW || 4, shape.gridH || 4, shape.gridOffsets, N, w, h, z);\n            } else if (isSimpleQuad && shape.warp) {\n                result = MeshUtils.computeQuadMesh(shape.vertices, N, w, h, z);\n            } else if (shape.warp) {\n                result = MeshUtils.computePolygonMeshWarped(shape.vertices, shape.edges, N, w, h, z);\n            } else {\n                result = MeshUtils.computePolygonMesh(shape.vertices, shape.edges, N, w, h, z);\n            }\n        }\n\n        // Aligned UV mode: override UVs to axis-aligned bounding box\n        if (uvMode === \"aligned\") {\n            result = MeshUtils.recomputeAlignedUVs(result, w, h);\n        }\n\n        while (meshCacheData.length <= idx) {\n            meshCacheData.push(null);\n            meshCacheKeys.push(\"\");\n        }\n        meshCacheData[idx] = result;\n        meshCacheKeys[idx] = key;\n\n        return result;\n    }\n\n    function getBlend(idx, edge) {\n        if (idx < 0 || idx >= rects.length) return 0;\n        var b = rects[idx].blend;\n        return b ? (b[edge] || 0) : 0;\n    }\n\n    function getBlendGamma(idx) {\n        if (idx < 0 || idx >= rects.length) return 1.0;\n        return rects[idx].blendGamma || 1.0;\n    }\n\n    function getSrcBlend(idx) {\n        if (idx < 0 || idx >= rects.length) return 1;\n        var v = rects[idx].srcBlend;\n        return (typeof v === 'number') ? v : 1;\n    }\n\n    function getDstBlend(idx) {\n        if (idx < 0 || idx >= rects.length) return 7;\n        var v = rects[idx].dstBlend;\n        return (typeof v === 'number') ? v : 7;\n    }\n\n    function getTexSrc(idx) {\n        if (idx < 0 || idx >= rects.length) return texSrc0;\n        var s = rects[idx].source || 0;\n        var srcs = [texSrc0, texSrc1, texSrc2, texSrc3, texSrc4, texSrc5, texSrc6, texSrc7];\n        return srcs[Math.max(0, Math.min(7, s))];\n    }\n\n    function getOpacity(idx) {\n        if (idx < 0 || idx >= rects.length) return 1.0;\n        var v = rects[idx].opacity;\n        return (v !== undefined) ? v : 1.0;\n    }\n\n    function getVisible(idx) {\n        if (idx < 0 || idx >= rects.length) return true;\n        if (root.soloIndex >= 0) return idx === root.soloIndex;\n        var v = rects[idx].visible;\n        return (v !== undefined) ? v : true;\n    }\n\n    function getUvOffset(idx) {\n        if (idx < 0 || idx >= rects.length) return Qt.vector2d(0, 0);\n        var o = rects[idx].uvOffset || [0, 0];\n        return Qt.vector2d(o[0], o[1]);\n    }\n\n    function getUvScale(idx) {\n        if (idx < 0 || idx >= rects.length) return Qt.vector2d(1, 1);\n        var s = rects[idx].uvScale || [1, 1];\n        return Qt.vector2d(s[0], s[1]);\n    }\n\n    function getUvRotation(idx) {\n        if (idx < 0 || idx >= rects.length) return 0;\n        return rects[idx].uvRotation || 0;\n    }\n\n    function isManualUv(idx) {\n        if (idx < 0 || idx >= rects.length) return 0.0;\n        return (rects[idx].uvMode === \"manual\") ? 1.0 : 0.0;\n    }\n\n    loadState: function (state) {\n        if (state && state.mapperState) {\n            try {\n                var s = typeof state.mapperState === \"string\" ? JSON.parse(state.mapperState) : state.mapperState;\n                root.rects = s || [];\n            } catch (e) {\n                root.rects = [];\n            }\n        } else {\n            root.rects = [];\n        }\n        root.stateVersion++;\n    }\n\n    stateUpdated: function (k, v) {\n        if (k === \"mapperState\") {\n            try {\n                var s = typeof v === \"string\" ? JSON.parse(v) : v;\n                root.rects = Array.isArray(s) ? s : [];\n            } catch (e) {\n                return;\n            }\n            root.stateVersion++;\n        }\n    }\n\n    uiEvent: function (message) {\n        if (!message) return;\n        if (message.type === \"updateRects\") {\n            root.rects = message.rects || [];\n            root.soloIndex = (typeof message.soloIndex === 'number') ? message.soloIndex : -1;\n            root.showOverlay = !!message.showOverlay;\n            root.stateVersion++;\n        }\n    }\n\n    tick: function (token, state) {\n        if (typeof rectsIn.value !== 'undefined' && rectsIn.value !== null) {\n            var v = rectsIn.value;\n            try {\n                var data = typeof v === \"string\" ? JSON.parse(v) : v;\n                if (Array.isArray(data)) {\n                    if (ShapeData.shapesDiffer(root.rects, data)) {\n                        root.rects = data;\n                        meshCacheData = [];\n                        meshCacheKeys = [];\n                        root.stateVersion++;\n                    }\n                }\n            } catch (e) {}\n        }\n\n        var w = renderRoot.width;\n        var h = renderRoot.height;\n        if (w !== root.lastSentW || h !== root.lastSentH) {\n            root.lastSentW = w;\n            root.lastSentH = h;\n            uiSend({ type: \"renderSize\", width: w, height: h });\n        }\n    }\n\n    start: function () {}\n}\n","Ui":"import Score as Score\nimport QtQuick\nimport QtQuick.Controls\nimport QtQuick.Layouts\nimport \"GeomUtils.js\" as Geom\nimport \"SnapUtils.js\" as Snap\nimport \"ShapeData.js\" as ShapeData\nimport OssiaUI as S\n\nScore.ScriptUI {\n    id: root\n    anchors.fill: parent\n\n    property var rects: []\n    property int stateVersion: 0\n    property int selectedRect: -1\n    property bool isDragging: false\n    property int dragCorner: -1\n    property real renderWidth: 0\n    property real renderHeight: 0\n    property var snapLinesX: []\n    property var snapLinesY: []\n    property bool snappingEnabled: true\n    readonly property real snapThresholdPx: snappingEnabled ? 10 : 0\n    property bool drawingMode: false\n    property var drawingPoints: []\n    property int drawingVersion: 0\n    property int soloIndex: -1  // transient: when >= 0, only this shape renders\n    property int listDragIndex: -1\n    property int listDropIndex: -1\n    property bool showOverlay: false\n    property bool polyDrawing: false\n    property var polyPoints: []\n    property int polyVersion: 0\n    property var polyMousePos: [-1, -1]\n\n    // ---- State management ----\n\n    loadState: function (state) {\n        if (state && state.mapperState) {\n            try {\n                var s = typeof state.mapperState === \"string\" ? JSON.parse(state.mapperState) : state.mapperState;\n                root.rects = s || [];\n            } catch (e) {\n                root.rects = [];\n            }\n        } else {\n            root.rects = [];\n        }\n        root.stateVersion++;\n    }\n\n    stateUpdated: function (k, v) {\n        if (k === \"mapperState\") {\n            try {\n                var s = typeof v === \"string\" ? JSON.parse(v) : v;\n                root.rects = Array.isArray(s) ? s : [];\n            } catch (e) {\n                return;\n            }\n            root.stateVersion++;\n        }\n    }\n\n    executionEvent: function (message) {\n        if (message && message.type === \"renderSize\") {\n            root.renderWidth = message.width;\n            root.renderHeight = message.height;\n        }\n    }\n\n    // ---- Actions ----\n\n    function saveState(actionName) {\n        root.beginUpdateState(actionName || \"Edit mapping\");\n        root.updateState(\"mapperState\", JSON.stringify(root.rects));\n        root.endUpdateState();\n    }\n\n    function sendLiveUpdate() {\n        root.executionSend({ type: \"updateRects\", rects: root.rects, soloIndex: root.soloIndex, showOverlay: root.showOverlay });\n    }\n\n    function addShape(type) {\n        var shape = ShapeData.createShape(type || \"quad\", rects.length);\n        shape.name = shape.name + \" \" + (rects.length + 1);\n        rects.push(shape);\n        selectedRect = rects.length - 1;\n        stateVersion++;\n        saveState(\"Add shape\");\n        sendLiveUpdate();\n    }\n\n    function deleteRect(idx) {\n        if (idx < 0 || idx >= rects.length) return;\n        rects.splice(idx, 1);\n        if (selectedRect >= rects.length) selectedRect = rects.length - 1;\n        if (soloIndex === idx) soloIndex = -1;\n        else if (soloIndex > idx) soloIndex--;\n        stateVersion++;\n        saveState(\"Delete shape\");\n        sendLiveUpdate();\n    }\n\n    function duplicateRect(idx) {\n        if (idx < 0 || idx >= rects.length) return;\n        var src = rects[idx];\n        var dup = ShapeData.cloneShape(src);\n        dup.id = \"shape-\" + Date.now();\n        dup.name = Geom.incrementName(src.name);\n        for (var i = 0; i < dup.vertices.length; i++) {\n            dup.vertices[i][0] += 0.05;\n            dup.vertices[i][1] += 0.05;\n        }\n        if (dup.edges) {\n            for (var i = 0; i < dup.edges.length; i++) {\n                var e = dup.edges[i];\n                if (e && e.cp1 && e.cp2) {\n                    e.cp1[0] += 0.05; e.cp1[1] += 0.05;\n                    e.cp2[0] += 0.05; e.cp2[1] += 0.05;\n                }\n            }\n        }\n        rects.push(dup);\n        selectedRect = rects.length - 1;\n        stateVersion++;\n        saveState(\"Duplicate shape\");\n        sendLiveUpdate();\n    }\n\n    function moveRectUp(idx) {\n        if (idx >= rects.length - 1) return;\n        var tmp = rects[idx];\n        rects[idx] = rects[idx + 1];\n        rects[idx + 1] = tmp;\n        selectedRect = idx + 1;\n        if (soloIndex === idx) soloIndex = idx + 1;\n        else if (soloIndex === idx + 1) soloIndex = idx;\n        stateVersion++;\n        saveState(\"Reorder layers\");\n        sendLiveUpdate();\n    }\n\n    function moveRectDown(idx) {\n        if (idx <= 0) return;\n        var tmp = rects[idx];\n        rects[idx] = rects[idx - 1];\n        rects[idx - 1] = tmp;\n        selectedRect = idx - 1;\n        if (soloIndex === idx) soloIndex = idx - 1;\n        else if (soloIndex === idx - 1) soloIndex = idx;\n        stateVersion++;\n        saveState(\"Reorder layers\");\n        sendLiveUpdate();\n    }\n\n    function moveRect(fromIdx, toIdx) {\n        if (fromIdx === toIdx || fromIdx < 0 || fromIdx >= rects.length || toIdx < 0 || toIdx >= rects.length) return;\n        var item = rects.splice(fromIdx, 1)[0];\n        rects.splice(toIdx, 0, item);\n        selectedRect = toIdx;\n        if (soloIndex === fromIdx) soloIndex = toIdx;\n        else if (fromIdx < toIdx && soloIndex > fromIdx && soloIndex <= toIdx) soloIndex--;\n        else if (fromIdx > toIdx && soloIndex >= toIdx && soloIndex < fromIdx) soloIndex++;\n        stateVersion++;\n        saveState(\"Reorder layers\");\n        sendLiveUpdate();\n    }\n\n    // ---- Vertex editing ----\n\n    function insertVertexOnEdge(nx, ny) {\n        if (selectedRect < 0 || selectedRect >= rects.length) return;\n        if (isShapeLocked(selectedRect)) return;\n        var shape = rects[selectedRect];\n        var nearest = Geom.nearestEdge(nx, ny, shape.vertices);\n        if (nearest.edgeIndex < 0) return;\n        var ei = nearest.edgeIndex;\n        var t = nearest.t;\n        var a = shape.vertices[ei];\n        var b = shape.vertices[(ei + 1) % shape.vertices.length];\n        var newVert = [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])];\n        shape.vertices.splice(ei + 1, 0, newVert);\n        if (shape.edges) {\n            // Split the edge: new vertex gets a null (straight) edge after it\n            shape.edges.splice(ei + 1, 0, null);\n        }\n        stateVersion++;\n        saveState(\"Insert vertex\");\n        sendLiveUpdate();\n    }\n\n    function removeVertex(vertIdx) {\n        if (selectedRect < 0 || selectedRect >= rects.length) return;\n        if (isShapeLocked(selectedRect)) return;\n        var shape = rects[selectedRect];\n        if (shape.vertices.length <= 3) return;\n        shape.vertices.splice(vertIdx, 1);\n        if (shape.edges && shape.edges.length > vertIdx) {\n            shape.edges.splice(vertIdx, 1);\n        }\n        stateVersion++;\n        saveState(\"Remove vertex\");\n        sendLiveUpdate();\n    }\n\n    function toggleBezierEdge(edgeIdx) {\n        if (selectedRect < 0 || selectedRect >= rects.length) return;\n        if (isShapeLocked(selectedRect)) return;\n        var shape = rects[selectedRect];\n        if (!shape.edges) shape.edges = [];\n        while (shape.edges.length < shape.vertices.length)\n            shape.edges.push(null);\n\n        if (shape.edges[edgeIdx] && shape.edges[edgeIdx].cp1) {\n            // Remove bezier: make straight\n            shape.edges[edgeIdx] = null;\n        } else {\n            // Add bezier: place control points at 1/3 and 2/3 of the edge\n            var a = shape.vertices[edgeIdx];\n            var b = shape.vertices[(edgeIdx + 1) % shape.vertices.length];\n            shape.edges[edgeIdx] = {\n                cp1: [a[0] + (b[0] - a[0]) / 3, a[1] + (b[1] - a[1]) / 3],\n                cp2: [a[0] + 2 * (b[0] - a[0]) / 3, a[1] + 2 * (b[1] - a[1]) / 3]\n            };\n        }\n        stateVersion++;\n        saveState(\"Toggle bezier\");\n        sendLiveUpdate();\n    }\n\n    function toggleAllBezierEdges() {\n        if (selectedRect < 0 || selectedRect >= rects.length) return;\n        if (isShapeLocked(selectedRect)) return;\n        var shape = rects[selectedRect];\n        // Check if any edge is bezier\n        var hasBezier = false;\n        if (shape.edges) {\n            for (var i = 0; i < shape.edges.length; i++) {\n                if (shape.edges[i] && shape.edges[i].cp1) { hasBezier = true; break; }\n            }\n        }\n        if (hasBezier) {\n            // Clear all bezier edges\n            shape.edges = [];\n        } else {\n            // Make all edges bezier\n            if (!shape.edges) shape.edges = [];\n            while (shape.edges.length < shape.vertices.length)\n                shape.edges.push(null);\n            for (var i = 0; i < shape.vertices.length; i++) {\n                var a = shape.vertices[i];\n                var b = shape.vertices[(i + 1) % shape.vertices.length];\n                shape.edges[i] = {\n                    cp1: [a[0] + (b[0] - a[0]) / 3, a[1] + (b[1] - a[1]) / 3],\n                    cp2: [a[0] + 2 * (b[0] - a[0]) / 3, a[1] + 2 * (b[1] - a[1]) / 3]\n                };\n            }\n        }\n        stateVersion++;\n        saveState(\"Toggle bezier\");\n        sendLiveUpdate();\n    }\n\n    // ---- Snapping wrappers ----\n\n    function snapVertex(nx, ny, excludeIdx) {\n        if (!snappingEnabled) { snapLinesX = []; snapLinesY = []; return [nx, ny]; }\n        var result = Snap.snapVertex(nx, ny, excludeIdx, rects, viewport.width, viewport.height, snapThresholdPx);\n        snapLinesX = result.linesX;\n        snapLinesY = result.linesY;\n        return result.pos;\n    }\n\n    function snapShapeMove(origVertices, dx, dy, excludeIdx) {\n        if (!snappingEnabled) { snapLinesX = []; snapLinesY = []; return { dx: dx, dy: dy }; }\n        var result = Snap.snapShapeMove(origVertices, dx, dy, excludeIdx, rects, viewport.width, viewport.height, snapThresholdPx);\n        snapLinesX = result.linesX;\n        snapLinesY = result.linesY;\n        return { dx: result.dx, dy: result.dy };\n    }\n\n    function clearSnap() { snapLinesX = []; snapLinesY = []; }\n\n    function finishFreehandShape() {\n        var pts = root.drawingPoints;\n        root.drawingMode = false;\n        root.drawingPoints = [];\n        root.stateVersion++;\n        if (pts.length < 3) return;\n\n        // Simplify with RDP (epsilon in normalized coords)\n        var simplified = Geom.simplifyPath(pts, 0.005);\n        if (simplified.length < 3) simplified = pts;\n\n        // Remove last point if it's too close to first (auto-closed)\n        var last = simplified[simplified.length - 1];\n        var first = simplified[0];\n        var d = Math.sqrt((last[0]-first[0])*(last[0]-first[0]) + (last[1]-first[1])*(last[1]-first[1]));\n        if (d < 0.01 && simplified.length > 3)\n            simplified.pop();\n\n        if (simplified.length < 3) return;\n\n        var shape = ShapeData.createFreehand(simplified, rects.length);\n        rects.push(shape);\n        selectedRect = rects.length - 1;\n        stateVersion++;\n        saveState(\"Draw freehand shape\");\n        sendLiveUpdate();\n    }\n\n    function finishPolyShape() {\n        var pts = root.polyPoints;\n        root.polyDrawing = false;\n        root.polyPoints = [];\n        root.polyVersion++;\n        root.stateVersion++;\n        if (pts.length < 3) return;\n\n        var shape = ShapeData.createFreehand(pts, rects.length);\n        shape.name = \"Polygon \" + (rects.length + 1);\n        rects.push(shape);\n        selectedRect = rects.length - 1;\n        stateVersion++;\n        saveState(\"Draw polygon\");\n        sendLiveUpdate();\n    }\n\n    function isShapeLocked(idx) {\n        if (idx < 0 || idx >= rects.length) return false;\n        return rects[idx].locked || false;\n    }\n\n    function isSimpleWarpedQuad(shape) {\n        if (!shape || !shape.vertices || shape.vertices.length !== 4) return false;\n        if (!shape.warp) return false;\n        if (shape.edges) {\n            for (var i = 0; i < shape.edges.length; i++)\n                if (shape.edges[i] && shape.edges[i].cp1) return false;\n        }\n        return true;\n    }\n\n    readonly property var sourceColors: [\"#44ff4444\", \"#4444ff44\", \"#444444ff\", \"#44ffff44\", \"#44ff44ff\", \"#4444ffff\", \"#44ff8844\", \"#4488ff44\"]\n    readonly property var sourceBorderColors: [\"#ff4444\", \"#44ff44\", \"#4444ff\", \"#ffff44\", \"#ff44ff\", \"#44ffff\", \"#ff8844\", \"#88ff44\"]\n    readonly property var blendModeNames: [\"Zero\", \"One\", \"Src Color\", \"1-Src Color\", \"Dst Color\", \"1-Dst Color\", \"Src Alpha\", \"1-Src Alpha\", \"Dst Alpha\", \"1-Dst Alpha\"]\n\n    // ---- Layout ----\n\n    // Reusable inspector rows, built on the shared kit so this editor matches\n    // the tracking-zones one without restyling anything locally.\n    component PropSlider: S.SSliderRow {\n        labelWidth: 56\n    }\n    component PropCombo: S.SComboRow {\n        labelWidth: 56\n    }\n\n    S.ThemedPage {\n        anchors.fill: parent\n\n    ColumnLayout {\n        anchors.fill: parent\n        anchors.margins: S.Theme.gap\n        spacing: S.Theme.gap\n\n    SplitView {\n        Layout.fillWidth: true\n        Layout.fillHeight: true\n        spacing: S.Theme.gap\n\n        // ---- Left Panel ----\n        S.SPanel {\n            SplitView.preferredWidth: 210\n            SplitView.minimumWidth: 130\n            SplitView.maximumWidth: 300\n            SplitView.fillHeight: true\n\n        ColumnLayout {\n            anchors.fill: parent\n            anchors.margins: S.Theme.pad\n            SplitView.fillHeight: true\n            spacing: 4\n\n            RowLayout {\n                Layout.fillWidth: true\n                spacing: 2\n\n                S.SButton {\n                    text: \"Add shape\"\n                    Layout.fillWidth: true\n                    onClicked: root.addShape(shapeTypeCombo.currentValue)\n                }\n                S.SCombo {\n                    id: shapeTypeCombo\n                    Layout.preferredWidth: 90\n                    implicitHeight: 30\n                    font.pixelSize: 11\n                    model: ListModel {\n                        ListElement { text: \"Quad\"; value: \"quad\" }\n                        ListElement { text: \"Triangle\"; value: \"triangle\" }\n                        ListElement { text: \"Pentagon\"; value: \"pentagon\" }\n                        ListElement { text: \"Hexagon\"; value: \"hexagon\" }\n                        ListElement { text: \"Circle\"; value: \"circle\" }\n                        ListElement { text: \"Star\"; value: \"star\" }\n                    }\n                    textRole: \"text\"\n                    valueRole: \"value\"\n                }\n            }\n\n            ListView {\n                id: rectListView\n                Layout.fillWidth: true\n                Layout.fillHeight: true\n                clip: true\n                spacing: 2\n\n                model: {\n                    root.stateVersion;\n                    return root.rects.length;\n                }\n\n                delegate: Rectangle {\n                    id: listDel\n                    required property int index\n                    property int delIndex: index\n                    width: rectListView.width\n                    height: S.Theme.listRowH\n                    radius: S.Theme.radiusSm\n                    color: {\n                        if (root.listDragIndex >= 0 && root.listDropIndex === delIndex && root.listDragIndex !== delIndex)\n                            return palette.mid;\n                        return root.selectedRect === delIndex ? palette.highlight : (delIndex % 2 ? palette.alternateBase : palette.base);\n                    }\n                    border.color: root.selectedRect === delIndex ? palette.light : \"transparent\"\n                    border.width: 1\n\n                    property var rd: {\n                        root.stateVersion;\n                        return delIndex < root.rects.length ? root.rects[delIndex] : null;\n                    }\n                    opacity: (listDel.rd && listDel.rd.visible === false) ? 0.4 : 1.0\n\n                    MouseArea {\n                        anchors.fill: parent\n                        onClicked: { root.forceActiveFocus(); root.selectedRect = listDel.delIndex; }\n                    }\n\n                    RowLayout {\n                        id: delRow\n                        anchors.left: parent.left\n                        anchors.right: parent.right\n                        anchors.margins: 4\n                        anchors.verticalCenter: parent.verticalCenter\n                        spacing: 4\n\n                        S.SLayerDragHandle {\n                            view: rectListView\n                            index: listDel.delIndex\n                            Layout.preferredWidth: 14\n                            onStarted: {\n                                root.forceActiveFocus();\n                                root.listDragIndex = index;\n                                root.listDropIndex = index;\n                                root.selectedRect = index;\n                            }\n                            onTargetIndexChanged: if (dragging) root.listDropIndex = targetIndex\n                            onMoved: function(from, to) { root.moveRect(from, to); }\n                            onFinished: {\n                                root.listDragIndex = -1;\n                                root.listDropIndex = -1;\n                            }\n                        }\n\n                        Rectangle {\n                            width: 10; height: 10; radius: 2\n                            color: listDel.rd ? root.sourceBorderColors[listDel.rd.source] : palette.mid\n                        }\n\n                        TextInput {\n                            text: listDel.rd ? listDel.rd.name : \"\"\n                            font.bold: true\n                            font.pixelSize: 11\n                            color: palette.text\n                            Layout.fillWidth: true\n                            selectByMouse: true\n                            function commitName() {\n                                if (listDel.delIndex < root.rects.length && root.rects[listDel.delIndex].name !== text) {\n                                    root.rects[listDel.delIndex].name = text;\n                                    root.stateVersion++;\n                                    root.saveState(\"Rename shape\");\n                                    root.sendLiveUpdate();\n                                }\n                            }\n                            onEditingFinished: { commitName(); focus = false; }\n                            onActiveFocusChanged: if (!activeFocus) commitName()\n                        }\n\n                        S.SButton {\n                            text: \"\\u2715\"\n                            compact: true\n                            implicitWidth: 18; implicitHeight: 18\n                            font.pixelSize: 11\n                            tip: \"Delete\"\n                            onClicked: root.deleteRect(listDel.delIndex)\n                        }\n                    }\n                }\n            }\n\n            RowLayout {\n                Layout.fillWidth: true\n                spacing: 2\n                S.SCheck {\n                    text: \"Snap\"\n                    checked: root.snappingEnabled\n                    onToggled: root.snappingEnabled = checked\n                }\n                S.SCheck {\n                    text: \"Overlay\"\n                    checked: root.showOverlay\n                    onToggled: { root.showOverlay = checked; root.sendLiveUpdate(); }\n                }\n            }\n        }\n        }\n\n        // ---- Center Panel: viewport ----\n        Item {\n            id: viewport\n            SplitView.fillWidth: true\n            SplitView.fillHeight: true\n            clip: true\n\n            Rectangle {\n                anchors.fill: parent\n                color: palette.base\n            }\n\n            // Background grid\n            Canvas {\n                anchors.fill: parent\n                onPaint: {\n                    var ctx = getContext(\"2d\");\n                    ctx.clearRect(0, 0, width, height);\n                    ctx.strokeStyle = \"rgba(255,255,255,0.08)\";\n                    ctx.lineWidth = 1;\n                    var step = Math.max(width, height) / 10;\n                    for (var x = step; x < width; x += step) {\n                        ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke();\n                    }\n                    for (var y = step; y < height; y += step) {\n                        ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke();\n                    }\n                }\n            }\n\n            // Shape overlay canvas\n            Canvas {\n                id: quadOverlay\n                anchors.fill: parent\n\n                property int ver: root.stateVersion\n                onVerChanged: requestPaint()\n\n                onPaint: {\n                    var ctx = getContext(\"2d\");\n                    var w = width, h = height;\n                    ctx.clearRect(0, 0, w, h);\n\n                    for (var qi = 0; qi < root.rects.length; qi++) {\n                        var r = root.rects[qi];\n                        var c = r.vertices;\n                        if (!c || c.length < 3) continue;\n\n                        var isSelected = (qi === root.selectedRect);\n                        var isMuted = (r.visible === false);\n                        var isSoloHidden = (root.soloIndex >= 0 && qi !== root.soloIndex);\n                        var isHidden = isMuted || isSoloHidden;\n                        var edges = r.edges;\n\n                        // Draw hidden shapes ghosted\n                        if (isHidden) ctx.globalAlpha = 0.15;\n\n                        // Build path (handles both straight and bezier edges)\n                        ctx.beginPath();\n                        ctx.moveTo(c[0][0] * w, c[0][1] * h);\n                        for (var ci = 0; ci < c.length; ci++) {\n                            var nextIdx = (ci + 1) % c.length;\n                            var edge = (edges && ci < edges.length) ? edges[ci] : null;\n                            if (edge && edge.cp1 && edge.cp2) {\n                                ctx.bezierCurveTo(\n                                    edge.cp1[0] * w, edge.cp1[1] * h,\n                                    edge.cp2[0] * w, edge.cp2[1] * h,\n                                    c[nextIdx][0] * w, c[nextIdx][1] * h\n                                );\n                            } else {\n                                ctx.lineTo(c[nextIdx][0] * w, c[nextIdx][1] * h);\n                            }\n                        }\n\n                        // Fill\n                        ctx.fillStyle = root.sourceColors[r.source];\n                        ctx.fill();\n\n                        // Border\n                        ctx.strokeStyle = isSelected ? \"white\" : root.sourceBorderColors[r.source];\n                        ctx.lineWidth = isSelected ? 2 : 1;\n                        ctx.stroke();\n\n                        // Bezier tangent lines for selected shape\n                        if (isSelected && edges) {\n                            ctx.strokeStyle = \"rgba(197,128,20,0.6)\";\n                            ctx.lineWidth = 1;\n                            for (var ei = 0; ei < edges.length; ei++) {\n                                var e = edges[ei];\n                                if (!e || !e.cp1 || !e.cp2) continue;\n                                // Line from vertex to cp1\n                                ctx.beginPath();\n                                ctx.moveTo(c[ei][0] * w, c[ei][1] * h);\n                                ctx.lineTo(e.cp1[0] * w, e.cp1[1] * h);\n                                ctx.stroke();\n                                // Line from next vertex to cp2\n                                var ni = (ei + 1) % c.length;\n                                ctx.beginPath();\n                                ctx.moveTo(c[ni][0] * w, c[ni][1] * h);\n                                ctx.lineTo(e.cp2[0] * w, e.cp2[1] * h);\n                                ctx.stroke();\n                            }\n                        }\n\n                        // Grid lines for selected simple warped quads\n                        if (isSelected && root.isSimpleWarpedQuad(r)) {\n                            var gw = r.gridW || 4;\n                            var gh = r.gridH || 4;\n                            ctx.strokeStyle = \"rgba(255,255,255,0.25)\";\n                            ctx.lineWidth = 1;\n                            // Horizontal grid lines\n                            for (var gr = 0; gr <= gh; gr++) {\n                                ctx.beginPath();\n                                for (var gc = 0; gc <= gw; gc++) {\n                                    var gp = ShapeData.gridPointPos(r, gr, gc);\n                                    if (gc === 0) ctx.moveTo(gp[0] * w, gp[1] * h);\n                                    else ctx.lineTo(gp[0] * w, gp[1] * h);\n                                }\n                                ctx.stroke();\n                            }\n                            // Vertical grid lines\n                            for (var gc = 0; gc <= gw; gc++) {\n                                ctx.beginPath();\n                                for (var gr = 0; gr <= gh; gr++) {\n                                    var gp = ShapeData.gridPointPos(r, gr, gc);\n                                    if (gr === 0) ctx.moveTo(gp[0] * w, gp[1] * h);\n                                    else ctx.lineTo(gp[0] * w, gp[1] * h);\n                                }\n                                ctx.stroke();\n                            }\n                        }\n\n                        // Label at centroid\n                        var ct = Geom.polygonCentroid(c);\n                        var cx = ct[0] * w, cy = ct[1] * h;\n                        var label;\n                        if (root.isDragging && isSelected)\n                            label = Geom.formatInfo(r, root.renderWidth, root.renderHeight);\n                        else\n                            label = r.name + \"\\n(Tex \" + (r.source + 1) + \")\";\n\n                        var lines = label.split(\"\\n\");\n                        ctx.font = \"bold 11px sans-serif\";\n                        ctx.textAlign = \"center\";\n                        ctx.textBaseline = \"middle\";\n                        var lineH = 14;\n                        var startY = cy - (lines.length - 1) * lineH / 2;\n                        for (var li = 0; li < lines.length; li++) {\n                            var ly = startY + li * lineH;\n                            ctx.strokeStyle = \"#000\";\n                            ctx.lineWidth = 3;\n                            ctx.strokeText(lines[li], cx, ly);\n                            ctx.fillStyle = \"white\";\n                            ctx.fillText(lines[li], cx, ly);\n                        }\n\n                        // Reset alpha after hidden shape\n                        if (isHidden) ctx.globalAlpha = 1.0;\n                    }\n                }\n            }\n\n            // Freehand drawing overlay\n            Canvas {\n                id: drawOverlay\n                anchors.fill: parent\n                visible: root.drawingMode\n                z: 15\n\n                property int drawVer: root.drawingVersion\n                onDrawVerChanged: requestPaint()\n\n                onPaint: {\n                    var ctx = getContext(\"2d\");\n                    var w = width, h = height;\n                    ctx.clearRect(0, 0, w, h);\n                    var pts = root.drawingPoints;\n                    if (pts.length < 2) return;\n\n                    // Build closed path from raw points\n                    ctx.beginPath();\n                    ctx.moveTo(pts[0][0] * w, pts[0][1] * h);\n                    for (var i = 1; i < pts.length; i++)\n                        ctx.lineTo(pts[i][0] * w, pts[i][1] * h);\n                    ctx.closePath();\n\n                    // Filled preview\n                    if (pts.length >= 3) {\n                        ctx.fillStyle = \"rgba(197,128,20,0.25)\";\n                        ctx.fill();\n                    }\n\n                    // Stroke outline\n                    ctx.strokeStyle = \"#c58014\";\n                    ctx.lineWidth = 2;\n                    ctx.lineJoin = \"round\";\n                    ctx.lineCap = \"round\";\n                    ctx.stroke();\n                }\n            }\n\n            // Polygon pen mode overlay\n            Canvas {\n                id: polyOverlay\n                anchors.fill: parent\n                visible: root.polyDrawing\n                z: 15\n\n                property int pVer: root.polyVersion\n                onPVerChanged: requestPaint()\n\n                onPaint: {\n                    var ctx = getContext(\"2d\");\n                    var w = width, h = height;\n                    ctx.clearRect(0, 0, w, h);\n                    var pts = root.polyPoints;\n                    if (pts.length < 1) return;\n\n                    // Filled preview (if >= 3 points)\n                    if (pts.length >= 3) {\n                        ctx.beginPath();\n                        ctx.moveTo(pts[0][0] * w, pts[0][1] * h);\n                        for (var i = 1; i < pts.length; i++)\n                            ctx.lineTo(pts[i][0] * w, pts[i][1] * h);\n                        ctx.closePath();\n                        ctx.fillStyle = \"rgba(197,128,20,0.15)\";\n                        ctx.fill();\n                    }\n\n                    // Edges between placed points\n                    ctx.beginPath();\n                    ctx.moveTo(pts[0][0] * w, pts[0][1] * h);\n                    for (var i = 1; i < pts.length; i++)\n                        ctx.lineTo(pts[i][0] * w, pts[i][1] * h);\n                    ctx.strokeStyle = \"#c58014\";\n                    ctx.lineWidth = 2;\n                    ctx.lineJoin = \"round\";\n                    ctx.stroke();\n\n                    // Rubber band from last point to mouse\n                    var mp = root.polyMousePos;\n                    if (mp[0] >= 0 && pts.length >= 1) {\n                        ctx.beginPath();\n                        ctx.moveTo(pts[pts.length-1][0] * w, pts[pts.length-1][1] * h);\n                        ctx.lineTo(mp[0] * w, mp[1] * h);\n                        ctx.strokeStyle = \"rgba(197,128,20,0.5)\";\n                        ctx.lineWidth = 1;\n                        ctx.setLineDash([4, 4]);\n                        ctx.stroke();\n                        ctx.setLineDash([]);\n                    }\n\n                    // Vertex dots\n                    for (var i = 0; i < pts.length; i++) {\n                        var px = pts[i][0] * w, py = pts[i][1] * h;\n                        ctx.beginPath();\n                        ctx.arc(px, py, 4, 0, 2 * Math.PI);\n                        ctx.fillStyle = (i === 0) ? \"#fff\" : \"#c58014\";\n                        ctx.fill();\n                        ctx.strokeStyle = \"#000\";\n                        ctx.lineWidth = 1;\n                        ctx.stroke();\n                    }\n\n                    // Highlight first point when mouse is near (close hint)\n                    if (pts.length >= 3 && mp[0] >= 0) {\n                        var dx = mp[0] - pts[0][0], dy = mp[1] - pts[0][1];\n                        if (Math.sqrt(dx*dx + dy*dy) < 0.02) {\n                            ctx.beginPath();\n                            ctx.arc(pts[0][0] * w, pts[0][1] * h, 8, 0, 2 * Math.PI);\n                            ctx.strokeStyle = \"#fff\";\n                            ctx.lineWidth = 2;\n                            ctx.stroke();\n                        }\n                    }\n                }\n            }\n\n            // Corner handles for selected shape (hidden when locked)\n            Repeater {\n                model: {\n                    root.stateVersion;\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 0;\n                    if (root.isShapeLocked(root.selectedRect)) return 0;\n                    return root.rects[root.selectedRect].vertices.length;\n                }\n\n                Rectangle {\n                    id: cornerHandle\n                    property int ci: index\n                    property var corner: {\n                        root.stateVersion;\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length)\n                            return [0, 0];\n                        var c = root.rects[root.selectedRect].vertices;\n                        return ci < c.length ? c[ci] : [0, 0];\n                    }\n\n                    x: corner[0] * viewport.width - 7\n                    y: corner[1] * viewport.height - 7\n                    width: 14\n                    height: 14\n                    radius: 7\n                    color: palette.buttonText\n                    border.color: palette.button\n                    border.width: 1\n                    z: 10\n\n                    Label {\n                        anchors.centerIn: parent\n                        text: (cornerHandle.ci + 1).toString()\n                        color: palette.button\n                        font.pixelSize: 9\n                        font.bold: true\n                    }\n\n                    MouseArea {\n                        anchors.fill: parent\n                        anchors.margins: -5\n                        acceptedButtons: Qt.LeftButton | Qt.RightButton\n                        cursorShape: root.isShapeLocked(root.selectedRect) ? Qt.ForbiddenCursor : Qt.CrossCursor\n\n                        property real startX\n                        property real startY\n                        property real origCX\n                        property real origCY\n                        property bool removed: false\n\n                        onPressed: function (mouse) {\n                            if (root.isShapeLocked(root.selectedRect)) return;\n                            removed = false;\n                            // Right-click: toggle bezier on outgoing edge\n                            if (mouse.button === Qt.RightButton) {\n                                root.toggleBezierEdge(cornerHandle.ci);\n                                return;\n                            }\n                            // Ctrl+click: remove vertex\n                            if (mouse.modifiers & Qt.ControlModifier) {\n                                root.removeVertex(cornerHandle.ci);\n                                removed = true;\n                                return;\n                            }\n                            root.isDragging = true;\n                            root.dragCorner = cornerHandle.ci;\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            startX = p.x;\n                            startY = p.y;\n                            var c = root.rects[root.selectedRect].vertices[cornerHandle.ci];\n                            origCX = c[0];\n                            origCY = c[1];\n                            root.beginUpdateState(\"Move vertex\");\n                        }\n\n                        onPositionChanged: function (mouse) {\n                            if (removed || !root.isDragging) return;\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var nx = origCX + (p.x - startX) / viewport.width;\n                            var ny = origCY + (p.y - startY) / viewport.height;\n                            var snapped = root.snapVertex(nx, ny, root.selectedRect);\n                            root.rects[root.selectedRect].vertices[cornerHandle.ci] = snapped;\n                            root.stateVersion++;\n                            root.sendLiveUpdate();\n                        }\n\n                        onReleased: {\n                            if (removed || !root.isDragging) return;\n                            root.isDragging = false;\n                            root.dragCorner = -1;\n                            root.clearSnap();\n                            root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                            root.endUpdateState();\n                        }\n                    }\n                }\n            }\n\n            // Rotation handle for selected shape (hidden when locked)\n            Item {\n                id: rotHandle\n                visible: { root.stateVersion; return root.selectedRect >= 0 && root.selectedRect < root.rects.length && !root.isShapeLocked(root.selectedRect); }\n                z: 10\n\n                property var topCenter: {\n                    root.stateVersion;\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length)\n                        return [0.5, 0.5];\n                    var c = root.rects[root.selectedRect].vertices;\n                    return [(c[0][0] + c[1 % c.length][0]) / 2,\n                            (c[0][1] + c[1 % c.length][1]) / 2];\n                }\n                readonly property real handleOffset: 25\n                property real hx: topCenter[0] * viewport.width\n                property real hy: topCenter[1] * viewport.height - handleOffset\n\n                Rectangle {\n                    x: rotHandle.hx - 0.5\n                    y: rotHandle.hy + 5\n                    width: 1\n                    height: rotHandle.handleOffset - 5\n                    color: palette.buttonText\n                    opacity: 0.5\n                }\n\n                Rectangle {\n                    id: rotCircle\n                    x: rotHandle.hx - 7\n                    y: rotHandle.hy - 7\n                    width: 14\n                    height: 14\n                    radius: 7\n                    color: palette.light\n                    border.color: palette.buttonText\n                    border.width: 1\n\n                    Label {\n                        anchors.centerIn: parent\n                        text: \"\\u21BB\"\n                        color: palette.buttonText\n                        font.pixelSize: 10\n                        font.bold: true\n                    }\n\n                    MouseArea {\n                        anchors.fill: parent\n                        anchors.margins: -5\n                        cursorShape: root.isShapeLocked(root.selectedRect) ? Qt.ForbiddenCursor : Qt.ClosedHandCursor\n\n                        property real startAngle\n                        property var origVertices\n                        property var origEdges\n                        property var origGridOffsets\n\n                        onPressed: function (mouse) {\n                            if (root.isShapeLocked(root.selectedRect)) return;\n                            root.isDragging = true;\n                            var shape = root.rects[root.selectedRect];\n                            var ct = Geom.polygonCentroid(shape.vertices);\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var cx = ct[0] * viewport.width;\n                            var cy = ct[1] * viewport.height;\n                            startAngle = Math.atan2(p.y - cy, p.x - cx);\n                            origVertices = JSON.parse(JSON.stringify(shape.vertices));\n                            origEdges = shape.edges ? JSON.parse(JSON.stringify(shape.edges)) : null;\n                            origGridOffsets = shape.gridOffsets ? shape.gridOffsets.slice() : null;\n                            root.beginUpdateState(\"Rotate shape\");\n                        }\n\n                        onPositionChanged: function (mouse) {\n                            var ct = Geom.polygonCentroid(origVertices);\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var cx = ct[0] * viewport.width;\n                            var cy = ct[1] * viewport.height;\n                            var angle = Math.atan2(p.y - cy, p.x - cx) - startAngle;\n                            var shape = root.rects[root.selectedRect];\n                            shape.vertices = Geom.rotateVertices(origVertices, ct, angle);\n                            if (origEdges)\n                                shape.edges = Geom.rotateEdges(origEdges, ct, angle);\n                            if (origGridOffsets) {\n                                var cos_a = Math.cos(angle);\n                                var sin_a = Math.sin(angle);\n                                var newOff = origGridOffsets.slice();\n                                for (var i = 0; i < newOff.length; i += 2) {\n                                    var dx = origGridOffsets[i];\n                                    var dy = origGridOffsets[i + 1];\n                                    newOff[i] = dx * cos_a - dy * sin_a;\n                                    newOff[i + 1] = dx * sin_a + dy * cos_a;\n                                }\n                                shape.gridOffsets = newOff;\n                            }\n                            root.stateVersion++;\n                            root.sendLiveUpdate();\n                        }\n\n                        onReleased: {\n                            root.isDragging = false;\n                            root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                            root.endUpdateState();\n                        }\n                    }\n                }\n            }\n\n            // Scale handle for selected shape (hidden when locked)\n            Item {\n                id: scaleHandle\n                visible: { root.stateVersion; return root.selectedRect >= 0 && root.selectedRect < root.rects.length && !root.isShapeLocked(root.selectedRect); }\n                z: 10\n\n                property var bottomRight: {\n                    root.stateVersion;\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length)\n                        return [0.5, 0.5];\n                    var bb = Geom.polygonBBox(root.rects[root.selectedRect].vertices);\n                    return [bb.maxX, bb.maxY];\n                }\n                readonly property real handleOffset: 20\n                property real hx: bottomRight[0] * viewport.width + handleOffset\n                property real hy: bottomRight[1] * viewport.height + handleOffset\n\n                // Connecting line from bottom-right corner to handle\n                Canvas {\n                    x: scaleHandle.bottomRight[0] * viewport.width\n                    y: scaleHandle.bottomRight[1] * viewport.height\n                    width: scaleHandle.handleOffset + 7\n                    height: scaleHandle.handleOffset + 7\n                    onPaint: {\n                        var ctx = getContext(\"2d\");\n                        ctx.clearRect(0, 0, width, height);\n                        ctx.strokeStyle = palette.buttonText;\n                        ctx.globalAlpha = 0.5;\n                        ctx.lineWidth = 1;\n                        ctx.beginPath();\n                        ctx.moveTo(0, 0);\n                        ctx.lineTo(width - 7, height - 7);\n                        ctx.stroke();\n                    }\n                }\n\n                Rectangle {\n                    id: scaleCircle\n                    x: scaleHandle.hx - 7\n                    y: scaleHandle.hy - 7\n                    width: 14\n                    height: 14\n                    radius: 2\n                    color: palette.light\n                    border.color: palette.buttonText\n                    border.width: 1\n\n                    Label {\n                        anchors.centerIn: parent\n                        text: \"\\u2922\"\n                        color: palette.buttonText\n                        font.pixelSize: 10\n                        font.bold: true\n                    }\n\n                    MouseArea {\n                        anchors.fill: parent\n                        anchors.margins: -5\n                        cursorShape: root.isShapeLocked(root.selectedRect) ? Qt.ForbiddenCursor : Qt.SizeFDiagCursor\n\n                        property real startDist\n                        property var origVertices\n                        property var origEdges\n                        property var origGridOffsets\n\n                        onPressed: function (mouse) {\n                            if (root.isShapeLocked(root.selectedRect)) return;\n                            root.isDragging = true;\n                            var shape = root.rects[root.selectedRect];\n                            var ct = Geom.polygonCentroid(shape.vertices);\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var cpx = ct[0] * viewport.width;\n                            var cpy = ct[1] * viewport.height;\n                            startDist = Math.sqrt((p.x - cpx) * (p.x - cpx) + (p.y - cpy) * (p.y - cpy));\n                            if (startDist < 1) startDist = 1;\n                            origVertices = JSON.parse(JSON.stringify(shape.vertices));\n                            origEdges = shape.edges ? JSON.parse(JSON.stringify(shape.edges)) : null;\n                            origGridOffsets = shape.gridOffsets ? shape.gridOffsets.slice() : null;\n                            root.beginUpdateState(\"Scale shape\");\n                        }\n\n                        onPositionChanged: function (mouse) {\n                            var ct = Geom.polygonCentroid(origVertices);\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var cpx = ct[0] * viewport.width;\n                            var cpy = ct[1] * viewport.height;\n                            var curDist = Math.sqrt((p.x - cpx) * (p.x - cpx) + (p.y - cpy) * (p.y - cpy));\n                            var s = curDist / startDist;\n\n                            var shape = root.rects[root.selectedRect];\n                            shape.vertices = Geom.scaleVertices(origVertices, ct, s, s);\n                            if (origEdges)\n                                shape.edges = Geom.scaleEdges(origEdges, ct, s, s);\n                            if (origGridOffsets) {\n                                var newOff = origGridOffsets.slice();\n                                for (var i = 0; i < newOff.length; i++)\n                                    newOff[i] = origGridOffsets[i] * s;\n                                shape.gridOffsets = newOff;\n                            }\n                            root.stateVersion++;\n                            root.sendLiveUpdate();\n                        }\n\n                        onReleased: {\n                            root.isDragging = false;\n                            root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                            root.endUpdateState();\n                        }\n                    }\n                }\n            }\n\n            // Bezier control point handles for selected shape (hidden when locked)\n            Repeater {\n                id: bezierCpRepeater\n                model: {\n                    root.stateVersion;\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [];\n                    if (root.isShapeLocked(root.selectedRect)) return [];\n                    var shape = root.rects[root.selectedRect];\n                    if (!shape.edges) return [];\n                    var handles = [];\n                    for (var i = 0; i < shape.edges.length; i++) {\n                        var e = shape.edges[i];\n                        if (e && e.cp1 && e.cp2) {\n                            handles.push({ edgeIdx: i, cpIdx: 0 });\n                            handles.push({ edgeIdx: i, cpIdx: 1 });\n                        }\n                    }\n                    return handles;\n                }\n\n                Rectangle {\n                    id: cpHandle\n                    property var cpInfo: modelData || { edgeIdx: 0, cpIdx: 0 }\n                    property var cpPos: {\n                        root.stateVersion;\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [0, 0];\n                        var shape = root.rects[root.selectedRect];\n                        if (!shape.edges || cpInfo.edgeIdx >= shape.edges.length) return [0, 0];\n                        var e = shape.edges[cpInfo.edgeIdx];\n                        if (!e) return [0, 0];\n                        return cpInfo.cpIdx === 0 ? (e.cp1 || [0,0]) : (e.cp2 || [0,0]);\n                    }\n\n                    x: cpPos[0] * viewport.width - 5\n                    y: cpPos[1] * viewport.height - 5\n                    width: 10\n                    height: 10\n                    radius: 0\n                    color: palette.light\n                    border.color: palette.buttonText\n                    border.width: 1\n                    z: 10\n\n                    MouseArea {\n                        anchors.fill: parent\n                        anchors.margins: -5\n                        cursorShape: root.isShapeLocked(root.selectedRect) ? Qt.ForbiddenCursor : Qt.CrossCursor\n\n                        property real startX\n                        property real startY\n                        property real origX\n                        property real origY\n\n                        onPressed: function (mouse) {\n                            if (root.isShapeLocked(root.selectedRect)) return;\n                            root.isDragging = true;\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            startX = p.x;\n                            startY = p.y;\n                            origX = cpHandle.cpPos[0];\n                            origY = cpHandle.cpPos[1];\n                            root.beginUpdateState(\"Move control point\");\n                        }\n\n                        onPositionChanged: function (mouse) {\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var nx = origX + (p.x - startX) / viewport.width;\n                            var ny = origY + (p.y - startY) / viewport.height;\n                            var shape = root.rects[root.selectedRect];\n                            var cpKey = cpHandle.cpInfo.cpIdx === 0 ? \"cp1\" : \"cp2\";\n                            shape.edges[cpHandle.cpInfo.edgeIdx][cpKey] = [nx, ny];\n                            root.stateVersion++;\n                            root.sendLiveUpdate();\n                        }\n\n                        onReleased: {\n                            root.isDragging = false;\n                            root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                            root.endUpdateState();\n                        }\n                    }\n                }\n            }\n\n            // Grid point handles for selected simple warped quads (hidden when locked)\n            Repeater {\n                id: gridHandleRepeater\n                model: {\n                    root.stateVersion;\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [];\n                    if (root.isShapeLocked(root.selectedRect)) return [];\n                    var shape = root.rects[root.selectedRect];\n                    if (!root.isSimpleWarpedQuad(shape)) return [];\n                    var gw = shape.gridW || 4;\n                    var gh = shape.gridH || 4;\n                    var handles = [];\n                    for (var row = 0; row <= gh; row++) {\n                        for (var col = 0; col <= gw; col++) {\n                            // Skip the 4 corners (handled by corner handles)\n                            if ((row === 0 || row === gh) && (col === 0 || col === gw)) continue;\n                            handles.push({ row: row, col: col });\n                        }\n                    }\n                    return handles;\n                }\n\n                Rectangle {\n                    id: gridHandle\n                    property var gpInfo: modelData || { row: 0, col: 0 }\n                    property var gpPos: {\n                        root.stateVersion;\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [0, 0];\n                        return ShapeData.gridPointPos(root.rects[root.selectedRect], gpInfo.row, gpInfo.col);\n                    }\n\n                    x: gpPos[0] * viewport.width - 4\n                    y: gpPos[1] * viewport.height - 4\n                    width: 8\n                    height: 8\n                    radius: 0\n                    color: \"transparent\"\n                    border.color: Qt.rgba(1.0, 1.0, 1.0, 0.6)\n                    border.width: 1\n                    z: 9\n\n                    MouseArea {\n                        anchors.fill: parent\n                        anchors.margins: -5\n                        cursorShape: root.isShapeLocked(root.selectedRect) ? Qt.ForbiddenCursor : Qt.CrossCursor\n\n                        property real startX\n                        property real startY\n                        property real origOffX\n                        property real origOffY\n\n                        onPressed: function (mouse) {\n                            if (root.isShapeLocked(root.selectedRect)) return;\n                            root.isDragging = true;\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            startX = p.x;\n                            startY = p.y;\n\n                            var shape = root.rects[root.selectedRect];\n                            var gw = shape.gridW || 4;\n                            var gh = shape.gridH || 4;\n                            var gi = gridHandle.gpInfo.row * (gw + 1) + gridHandle.gpInfo.col;\n\n                            // Initialize gridOffsets if needed\n                            if (!shape.gridOffsets) {\n                                shape.gridOffsets = ShapeData.initGridOffsets(gw, gh);\n                            }\n\n                            origOffX = shape.gridOffsets[gi * 2] || 0;\n                            origOffY = shape.gridOffsets[gi * 2 + 1] || 0;\n                            root.beginUpdateState(\"Move grid point\");\n                        }\n\n                        onPositionChanged: function (mouse) {\n                            if (!root.isDragging) return;\n                            var p = mapToItem(viewport, mouse.x, mouse.y);\n                            var dx = (p.x - startX) / viewport.width;\n                            var dy = (p.y - startY) / viewport.height;\n\n                            var shape = root.rects[root.selectedRect];\n                            var gw = shape.gridW || 4;\n                            var gi = gridHandle.gpInfo.row * (gw + 1) + gridHandle.gpInfo.col;\n\n                            shape.gridOffsets[gi * 2] = origOffX + dx;\n                            shape.gridOffsets[gi * 2 + 1] = origOffY + dy;\n                            root.stateVersion++;\n                            root.sendLiveUpdate();\n                        }\n\n                        onReleased: {\n                            if (!root.isDragging) return;\n                            root.isDragging = false;\n                            root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                            root.endUpdateState();\n                        }\n\n                        onDoubleClicked: function (mouse) {\n                            // Reset this grid point to default position\n                            var shape = root.rects[root.selectedRect];\n                            if (!shape.gridOffsets) return;\n                            var gw = shape.gridW || 4;\n                            var gi = gridHandle.gpInfo.row * (gw + 1) + gridHandle.gpInfo.col;\n                            shape.gridOffsets[gi * 2] = 0;\n                            shape.gridOffsets[gi * 2 + 1] = 0;\n                            root.stateVersion++;\n                            root.saveState(\"Reset grid point\");\n                            root.sendLiveUpdate();\n                        }\n                    }\n                }\n            }\n\n            // Snap guide lines (vertical)\n            Repeater {\n                model: root.snapLinesX.length\n                Rectangle {\n                    x: root.snapLinesX[index] * viewport.width - 0.5\n                    y: 0\n                    width: 1\n                    height: viewport.height\n                    color: palette.light\n                    opacity: 0.7\n                    visible: root.isDragging\n                    z: 8\n                }\n            }\n\n            // Snap guide lines (horizontal)\n            Repeater {\n                model: root.snapLinesY.length\n                Rectangle {\n                    x: 0\n                    y: root.snapLinesY[index] * viewport.height - 0.5\n                    width: viewport.width\n                    height: 1\n                    color: palette.light\n                    opacity: 0.7\n                    visible: root.isDragging\n                    z: 8\n                }\n            }\n\n            // Background MouseArea: selection, deselection, move-drag, freehand draw, polygon pen\n            MouseArea {\n                anchors.fill: parent\n                z: -1\n                hoverEnabled: root.polyDrawing\n\n                property int dragMode: 0 // 0=none, 1=move, 2=drawing\n                property real startX\n                property real startY\n                property var origVertices: null\n                property var origEdges: null\n                property bool wantsAltDup: false\n                property bool didDuplicate: false\n                property int hitSrcIdx: -1\n\n                onDoubleClicked: function (mouse) {\n                    if (root.drawingMode) return;\n                    var nx = mouse.x / viewport.width;\n                    var ny = mouse.y / viewport.height;\n\n                    // Polygon pen mode: double-click finishes\n                    if (root.polyDrawing) {\n                        // Remove the 2 points added by the double-click press events\n                        if (root.polyPoints.length > 0) root.polyPoints.pop();\n                        if (root.polyPoints.length > 0) root.polyPoints.pop();\n                        root.finishPolyShape();\n                        dragMode = 0;\n                        root.isDragging = false;\n                        return;\n                    }\n\n                    // Nothing selected: start polygon pen mode\n                    if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) {\n                        root.polyPoints = [[nx, ny]];\n                        root.polyVersion++;\n                        root.polyDrawing = true;\n                        dragMode = 0;\n                        root.isDragging = false;\n                        return;\n                    }\n\n                    if (root.isShapeLocked(root.selectedRect)) return;\n                    // Check if click is near edge or in body\n                    var shape = root.rects[root.selectedRect];\n                    var nearest = Geom.nearestEdge(nx, ny, shape.vertices);\n                    if (nearest.dist > 0.03) {\n                        // Body click: set UV mode to manual\n                        shape.uvMode = \"manual\";\n                        root.stateVersion++;\n                        root.saveState(\"UV manual mode\");\n                        root.sendLiveUpdate();\n                    } else {\n                        root.insertVertexOnEdge(nx, ny);\n                    }\n                    // Cancel any drag that was started by the first click\n                    dragMode = 0;\n                    root.isDragging = false;\n                }\n\n                onPressed: function (mouse) {\n                    root.forceActiveFocus();\n                    var nx = mouse.x / viewport.width;\n                    var ny = mouse.y / viewport.height;\n                    wantsAltDup = false;\n                    didDuplicate = false;\n                    hitSrcIdx = -1;\n\n                    // Continue freehand drawing if already in drawing mode\n                    if (root.drawingMode) {\n                        dragMode = 2;\n                        root.drawingPoints = [[nx, ny]];\n                        return;\n                    }\n\n                    // Polygon pen mode: add vertex or close near first point\n                    if (root.polyDrawing) {\n                        if (root.polyPoints.length >= 3) {\n                            var first = root.polyPoints[0];\n                            var dx = nx - first[0], dy = ny - first[1];\n                            if (Math.sqrt(dx*dx + dy*dy) < 0.02) {\n                                root.finishPolyShape();\n                                return;\n                            }\n                        }\n                        root.polyPoints.push([nx, ny]);\n                        root.polyVersion++;\n                        return;\n                    }\n\n                    var hitIdx = -1;\n                    for (var i = root.rects.length - 1; i >= 0; i--) {\n                        if (Geom.pointInPolygon(nx, ny, root.rects[i].vertices)) {\n                            hitIdx = i;\n                            break;\n                        }\n                    }\n\n                    if (hitIdx >= 0) {\n                        root.selectedRect = hitIdx;\n                        // Allow selection but prevent dragging locked shapes\n                        if (root.isShapeLocked(hitIdx)) return;\n                        wantsAltDup = !!(mouse.modifiers & Qt.AltModifier);\n                        hitSrcIdx = hitIdx;\n                        dragMode = 1;\n                        root.isDragging = true;\n                        root.dragCorner = -1;\n                        startX = mouse.x;\n                        startY = mouse.y;\n                        origVertices = JSON.parse(JSON.stringify(root.rects[hitIdx].vertices));\n                        origEdges = root.rects[hitIdx].edges ? JSON.parse(JSON.stringify(root.rects[hitIdx].edges)) : null;\n                        root.beginUpdateState(wantsAltDup ? \"Duplicate shape\" : \"Move shape\");\n                    } else if (mouse.modifiers & Qt.AltModifier) {\n                        // Alt+click on empty space: start freehand drawing\n                        root.selectedRect = -1;\n                        dragMode = 2;\n                        root.drawingMode = true;\n                        root.drawingPoints = [[nx, ny]];\n                    } else {\n                        root.selectedRect = -1;\n                        dragMode = 0;\n                    }\n                }\n\n                onPositionChanged: function (mouse) {\n                    // Rubber band for polygon pen mode\n                    if (root.polyDrawing) {\n                        root.polyMousePos = [mouse.x / viewport.width, mouse.y / viewport.height];\n                        root.polyVersion++;\n                    }\n\n                    if (dragMode === 2) {\n                        // Freehand drawing: sample points with min distance\n                        var nx = mouse.x / viewport.width;\n                        var ny = mouse.y / viewport.height;\n                        var pts = root.drawingPoints;\n                        if (pts.length > 0) {\n                            var last = pts[pts.length - 1];\n                            var dx = nx - last[0], dy = ny - last[1];\n                            if (dx*dx + dy*dy < 0.0001) return; // min distance filter\n                        }\n                        pts.push([nx, ny]);\n                        root.drawingVersion++;\n                        return;\n                    }\n\n                    if (dragMode !== 1) return;\n\n                    if (wantsAltDup && !didDuplicate) {\n                        var src = root.rects[hitSrcIdx];\n                        var dup = ShapeData.cloneShape(src);\n                        dup.id = \"shape-\" + Date.now();\n                        dup.name = Geom.incrementName(src.name);\n                        root.rects.push(dup);\n                        root.selectedRect = root.rects.length - 1;\n                        didDuplicate = true;\n                        root.stateVersion++;\n                    }\n\n                    var dx = (mouse.x - startX) / viewport.width;\n                    var dy = (mouse.y - startY) / viewport.height;\n                    var snapped = root.snapShapeMove(origVertices, dx, dy, root.selectedRect);\n                    var r = root.rects[root.selectedRect];\n                    for (var i = 0; i < r.vertices.length; i++) {\n                        r.vertices[i] = [origVertices[i][0] + snapped.dx,\n                                        origVertices[i][1] + snapped.dy];\n                    }\n                    if (origEdges) {\n                        if (!r.edges) r.edges = [];\n                        for (var i = 0; i < origEdges.length; i++) {\n                            var e = origEdges[i];\n                            if (e && e.cp1 && e.cp2) {\n                                r.edges[i] = {\n                                    cp1: [e.cp1[0] + snapped.dx, e.cp1[1] + snapped.dy],\n                                    cp2: [e.cp2[0] + snapped.dx, e.cp2[1] + snapped.dy]\n                                };\n                            }\n                        }\n                    }\n                    root.stateVersion++;\n                    root.sendLiveUpdate();\n                }\n\n                onReleased: {\n                    if (dragMode === 2) {\n                        root.finishFreehandShape();\n                        dragMode = 0;\n                        return;\n                    }\n                    if (dragMode !== 0) {\n                        root.isDragging = false;\n                        root.clearSnap();\n                        root.updateState(\"mapperState\", JSON.stringify(root.rects));\n                        root.endUpdateState();\n                    }\n                    dragMode = 0;\n                    wantsAltDup = false;\n                }\n            }\n        }\n\n        // ---- Right Panel: shape properties ----\n        S.SPanel {\n            SplitView.preferredWidth: 290\n            SplitView.minimumWidth: 220\n            SplitView.maximumWidth: 400\n            SplitView.fillHeight: true\n\n        ScrollView {\n            id: propsScroll\n            anchors.fill: parent\n            anchors.margins: S.Theme.pad\n            clip: true\n            ScrollBar.horizontal.policy: ScrollBar.AlwaysOff\n            contentWidth: availableWidth\n\n        ColumnLayout {\n            id: propsPanel\n            width: propsScroll.availableWidth\n            spacing: S.Theme.gapSm\n\n            property var selShape: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return null;\n                return root.rects[root.selectedRect];\n            }\n            // Primitive properties that re-evaluate on stateVersion change\n            // (selShape returns same object ref, so deep reads won't re-fire)\n            property bool selMuted: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return false;\n                return root.rects[root.selectedRect].visible === false;\n            }\n            property bool selLocked: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return false;\n                return root.rects[root.selectedRect].locked || false;\n            }\n            property var selBlend: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return null;\n                var b = root.rects[root.selectedRect].blend;\n                return b || { top: 0, right: 0, bottom: 0, left: 0 };\n            }\n            property real selGamma: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 1.0;\n                return root.rects[root.selectedRect].blendGamma || 1.0;\n            }\n            property real selOpacity: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 1.0;\n                var v = root.rects[root.selectedRect].opacity;\n                return (v !== undefined) ? v : 1.0;\n            }\n            property string selUvMode: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return \"auto\";\n                return root.rects[root.selectedRect].uvMode || \"auto\";\n            }\n            property var selUvOffset: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [0,0];\n                return root.rects[root.selectedRect].uvOffset || [0,0];\n            }\n            property var selUvScale: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return [1,1];\n                return root.rects[root.selectedRect].uvScale || [1,1];\n            }\n            property real selUvRotation: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 0;\n                return root.rects[root.selectedRect].uvRotation || 0;\n            }\n            property int selSrcBlend: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 1;\n                var v = root.rects[root.selectedRect].srcBlend;\n                return (typeof v === 'number') ? v : 1;\n            }\n            property int selDstBlend: {\n                root.stateVersion;\n                if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return 7;\n                var v = root.rects[root.selectedRect].dstBlend;\n                return (typeof v === 'number') ? v : 7;\n            }\n\n            // `live` = still dragging: preview only, no undo command.\n            function setBlendEdge(edge, val, live) {\n                var r = root.rects[root.selectedRect];\n                if (!r.blend) r.blend = { top: 0, right: 0, bottom: 0, left: 0 };\n                r.blend[edge] = val;\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"Edge blend\");\n                root.sendLiveUpdate();\n            }\n            function setGamma(val, live) {\n                root.rects[root.selectedRect].blendGamma = val;\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"Blend gamma\");\n                root.sendLiveUpdate();\n            }\n            function setOpacity(val, live) {\n                root.rects[root.selectedRect].opacity = val;\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"Shape opacity\");\n                root.sendLiveUpdate();\n            }\n            function setUvMode(mode) {\n                root.rects[root.selectedRect].uvMode = mode;\n                root.stateVersion++;\n                root.saveState(\"UV mode\");\n                root.sendLiveUpdate();\n            }\n            function setUvOffset(x, y, live) {\n                root.rects[root.selectedRect].uvOffset = [x, y];\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"UV offset\");\n                root.sendLiveUpdate();\n            }\n            function setUvScale(x, y, live) {\n                root.rects[root.selectedRect].uvScale = [x, y];\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"UV scale\");\n                root.sendLiveUpdate();\n            }\n            function setUvRotation(val, live) {\n                root.rects[root.selectedRect].uvRotation = val;\n                if (live) { root.sendLiveUpdate(); return; }\n                root.stateVersion++;\n                root.saveState(\"UV rotation\");\n                root.sendLiveUpdate();\n            }\n            function setSrcBlend(idx) {\n                root.rects[root.selectedRect].srcBlend = idx;\n                root.stateVersion++;\n                root.saveState(\"Src blend mode\");\n                root.sendLiveUpdate();\n            }\n            function setDstBlend(idx) {\n                root.rects[root.selectedRect].dstBlend = idx;\n                root.stateVersion++;\n                root.saveState(\"Dst blend mode\");\n                root.sendLiveUpdate();\n            }\n\n            // All controls hidden when nothing selected\n            ColumnLayout {\n                visible: root.selectedRect >= 0 && root.selectedRect < root.rects.length\n                Layout.fillWidth: true\n                spacing: 4\n\n            // --- General ---\n            S.SSectionLabel { text: \"General\" }\n\n            RowLayout {\n                spacing: 4\n                Layout.fillWidth: true\n                S.SButton {\n                    text: root.soloIndex === root.selectedRect ? \"Solo ON\" : \"Solo\"\n                    implicitHeight: 24\n                    font.pixelSize: 11\n                    font.bold: root.soloIndex === root.selectedRect\n                    palette.buttonText: root.soloIndex === root.selectedRect ? root.palette.light : root.palette.buttonText\n                    Layout.fillWidth: true\n                    onClicked: {\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return;\n                        root.soloIndex = (root.soloIndex === root.selectedRect) ? -1 : root.selectedRect;\n                        root.stateVersion++;\n                        root.sendLiveUpdate();\n                    }\n                }\n                S.SButton {\n                    text: propsPanel.selMuted ? \"Muted\" : \"Mute\"\n                    implicitHeight: 24\n                    font.pixelSize: 11\n                    font.bold: propsPanel.selMuted\n                    palette.buttonText: propsPanel.selMuted ? \"#ff4444\" : root.palette.buttonText\n                    Layout.fillWidth: true\n                    onClicked: {\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return;\n                        var cur = root.rects[root.selectedRect].visible;\n                        root.rects[root.selectedRect].visible = (cur !== undefined ? !cur : false);\n                        root.stateVersion++;\n                        root.saveState(\"Toggle mute\");\n                        root.sendLiveUpdate();\n                    }\n                }\n            }\n\n            RowLayout {\n                spacing: 4\n                Layout.fillWidth: true\n                S.SButton {\n                    text: propsPanel.selLocked ? \"Locked\" : \"Lock\"\n                    implicitHeight: 24\n                    font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onClicked: {\n                        if (root.selectedRect < 0 || root.selectedRect >= root.rects.length) return;\n                        root.rects[root.selectedRect].locked = !(root.rects[root.selectedRect].locked || false);\n                        root.stateVersion++;\n                        root.saveState(\"Toggle lock\");\n                        root.sendLiveUpdate();\n                    }\n                }\n                S.SButton {\n                    text: \"Duplicate\"\n                    implicitHeight: 24\n                    font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onClicked: {\n                        if (root.selectedRect >= 0 && root.selectedRect < root.rects.length)\n                            root.duplicateRect(root.selectedRect);\n                    }\n                }\n            }\n\n            // --- Source & Warp ---\n            S.SSectionLabel { text: \"Shape\" }\n\n            RowLayout {\n                spacing: 4\n                Layout.fillWidth: true\n                Label { text: \"Source:\"; color: palette.windowText; font.pixelSize: 11 }\n                S.SCombo {\n                    model: [\"Tex 1\", \"Tex 2\", \"Tex 3\", \"Tex 4\", \"Tex 5\", \"Tex 6\", \"Tex 7\", \"Tex 8\"]\n                    currentIndex: propsPanel.selShape ? propsPanel.selShape.source : 0\n                    implicitWidth: 90\n                    implicitHeight: 24\n                    font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onActivated: {\n                        root.rects[root.selectedRect].source = currentIndex;\n                        root.stateVersion++;\n                        root.saveState(\"Change source\");\n                        root.sendLiveUpdate();\n                    }\n                }\n            }\n\n            RowLayout {\n                spacing: 4\n                Layout.fillWidth: true\n                S.SCheck {\n                    text: \"Warp\"\n                    checked: propsPanel.selShape ? (propsPanel.selShape.warp || false) : false\n                    font.pixelSize: 11\n                    implicitHeight: 24\n                    onToggled: {\n                        root.rects[root.selectedRect].warp = checked;\n                        root.stateVersion++;\n                        root.saveState(\"Toggle warp\");\n                        root.sendLiveUpdate();\n                    }\n                }\n            }\n\n            RowLayout {\n                spacing: 4\n                Layout.fillWidth: true\n                visible: propsPanel.selShape ? root.isSimpleWarpedQuad(propsPanel.selShape) : false\n                Label { text: \"Grid:\"; color: palette.windowText; font.pixelSize: 11 }\n                S.SSpin {\n                    from: 1; to: 16\n                    value: propsPanel.selShape ? (propsPanel.selShape.gridW || 4) : 4\n                    Layout.fillWidth: true; implicitHeight: 28; font.pixelSize: 11\n                    onValueModified: {\n                        root.rects[root.selectedRect].gridW = value;\n                        root.rects[root.selectedRect].gridOffsets = null;\n                        root.stateVersion++;\n                        root.saveState(\"Grid width\");\n                        root.sendLiveUpdate();\n                    }\n                }\n                Label { text: \"\\u00D7\"; color: palette.windowText; font.pixelSize: 11 }\n                S.SSpin {\n                    from: 1; to: 16\n                    value: propsPanel.selShape ? (propsPanel.selShape.gridH || 4) : 4\n                    Layout.fillWidth: true; implicitHeight: 28; font.pixelSize: 11\n                    onValueModified: {\n                        root.rects[root.selectedRect].gridH = value;\n                        root.rects[root.selectedRect].gridOffsets = null;\n                        root.stateVersion++;\n                        root.saveState(\"Grid height\");\n                        root.sendLiveUpdate();\n                    }\n                }\n            }\n\n            // --- Opacity ---\n            PropSlider {\n                label: \"Opacity\"\n                from: 0; to: 1.0; stepSize: 0.01; decimals: 2\n                value: propsPanel.selOpacity\n                onMoved: function (v) { propsPanel.setOpacity(v, true) }\n                onCommitted: function (v) { propsPanel.setOpacity(v) }\n            }\n\n            // --- Edge Blend ---\n            S.SSectionLabel { text: \"Edge Blend\" }\n\n            PropSlider {\n                label: \"T\"\n                from: 0; to: 0.5; stepSize: 0.01; decimals: 2\n                value: propsPanel.selBlend ? propsPanel.selBlend.top : 0\n                onMoved: function (v) { propsPanel.setBlendEdge(\"top\", v, true) }\n                onCommitted: function (v) { propsPanel.setBlendEdge(\"top\", v) }\n            }\n            PropSlider {\n                label: \"B\"\n                from: 0; to: 0.5; stepSize: 0.01; decimals: 2\n                value: propsPanel.selBlend ? propsPanel.selBlend.bottom : 0\n                onMoved: function (v) { propsPanel.setBlendEdge(\"bottom\", v, true) }\n                onCommitted: function (v) { propsPanel.setBlendEdge(\"bottom\", v) }\n            }\n            PropSlider {\n                label: \"L\"\n                from: 0; to: 0.5; stepSize: 0.01; decimals: 2\n                value: propsPanel.selBlend ? propsPanel.selBlend.left : 0\n                onMoved: function (v) { propsPanel.setBlendEdge(\"left\", v, true) }\n                onCommitted: function (v) { propsPanel.setBlendEdge(\"left\", v) }\n            }\n            PropSlider {\n                label: \"R\"\n                from: 0; to: 0.5; stepSize: 0.01; decimals: 2\n                value: propsPanel.selBlend ? propsPanel.selBlend.right : 0\n                onMoved: function (v) { propsPanel.setBlendEdge(\"right\", v, true) }\n                onCommitted: function (v) { propsPanel.setBlendEdge(\"right\", v) }\n            }\n            PropSlider {\n                label: \"Gamma\"\n                from: 0.5; to: 4.0; stepSize: 0.1; decimals: 1\n                value: propsPanel.selGamma\n                onMoved: function (v) { propsPanel.setGamma(v, true) }\n                onCommitted: function (v) { propsPanel.setGamma(v) }\n            }\n\n            // --- Blend Mode ---\n            S.SSectionLabel { text: \"Blend Mode\" }\n\n            RowLayout {\n                spacing: 2\n                Layout.fillWidth: true\n                Label { text: \"Src:\"; color: palette.windowText; font.pixelSize: 10; Layout.preferredWidth: 24 }\n                S.SCombo {\n                    model: root.blendModeNames\n                    currentIndex: propsPanel.selSrcBlend\n                    implicitHeight: 24; font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onActivated: propsPanel.setSrcBlend(currentIndex)\n                }\n            }\n            RowLayout {\n                spacing: 2\n                Layout.fillWidth: true\n                Label { text: \"Dst:\"; color: palette.windowText; font.pixelSize: 10; Layout.preferredWidth: 24 }\n                S.SCombo {\n                    model: root.blendModeNames\n                    currentIndex: propsPanel.selDstBlend\n                    implicitHeight: 24; font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onActivated: propsPanel.setDstBlend(currentIndex)\n                }\n            }\n\n            // --- UV Mapping ---\n            S.SSectionLabel { text: \"UV Mapping\" }\n\n            RowLayout {\n                spacing: 2\n                Layout.fillWidth: true\n                Label { text: \"Mode:\"; color: palette.windowText; font.pixelSize: 10; Layout.preferredWidth: 32 }\n                S.SCombo {\n                    id: uvModeCombo\n                    model: [\"Auto\", \"Interpolated\", \"Aligned\", \"Manual\"]\n                    readonly property var modeValues: [\"auto\", \"interpolated\", \"aligned\", \"manual\"]\n                    currentIndex: {\n                        var m = propsPanel.selUvMode;\n                        var idx = modeValues.indexOf(m);\n                        return idx >= 0 ? idx : 0;\n                    }\n                    implicitHeight: 24; font.pixelSize: 11\n                    Layout.fillWidth: true\n                    onActivated: propsPanel.setUvMode(modeValues[currentIndex])\n                }\n            }\n\n            // Manual UV controls\n            PropSlider {\n                visible: propsPanel.selUvMode === \"manual\"\n                label: \"Ox\"\n                from: -1; to: 1; stepSize: 0.01; decimals: 2\n                value: propsPanel.selUvOffset[0]\n                onMoved: function (v) { propsPanel.setUvOffset(v, propsPanel.selUvOffset[1], true) }\n                onCommitted: function (v) { propsPanel.setUvOffset(v, propsPanel.selUvOffset[1]) }\n            }\n            PropSlider {\n                visible: propsPanel.selUvMode === \"manual\"\n                label: \"Oy\"\n                from: -1; to: 1; stepSize: 0.01; decimals: 2\n                value: propsPanel.selUvOffset[1]\n                onMoved: function (v) { propsPanel.setUvOffset(propsPanel.selUvOffset[0], v, true) }\n                onCommitted: function (v) { propsPanel.setUvOffset(propsPanel.selUvOffset[0], v) }\n            }\n            PropSlider {\n                visible: propsPanel.selUvMode === \"manual\"\n                label: \"Sx\"\n                from: 0.1; to: 4.0; stepSize: 0.01; decimals: 2\n                value: propsPanel.selUvScale[0]\n                onMoved: function (v) { propsPanel.setUvScale(v, propsPanel.selUvScale[1], true) }\n                onCommitted: function (v) { propsPanel.setUvScale(v, propsPanel.selUvScale[1]) }\n            }\n            PropSlider {\n                visible: propsPanel.selUvMode === \"manual\"\n                label: \"Sy\"\n                from: 0.1; to: 4.0; stepSize: 0.01; decimals: 2\n                value: propsPanel.selUvScale[1]\n                onMoved: function (v) { propsPanel.setUvScale(propsPanel.selUvScale[0], v, true) }\n                onCommitted: function (v) { propsPanel.setUvScale(propsPanel.selUvScale[0], v) }\n            }\n            PropSlider {\n                visible: propsPanel.selUvMode === \"manual\"\n                label: \"Rot\"\n                from: -180; to: 180; stepSize: 1; decimals: 0\n                value: propsPanel.selUvRotation\n                onMoved: function (v) { propsPanel.setUvRotation(v, true) }\n                onCommitted: function (v) { propsPanel.setUvRotation(v) }\n            }\n\n            } // end controls wrapper\n\n            Item { Layout.fillHeight: true }\n        }\n        } // end ScrollView\n        } // end right panel\n    }\n\n    S.SStatusBar {\n        visible: detail.length > 0\n        detail: root.renderWidth > 0\n                ? (Math.round(root.renderWidth) + \"×\" + Math.round(root.renderHeight))\n                : \"\"\n    }\n    }\n    }\n\n    // Keyboard shortcuts\n    Keys.onPressed: function (event) {\n        if (event.key === Qt.Key_Escape && root.drawingMode) {\n            root.drawingMode = false;\n            root.drawingPoints = [];\n            root.stateVersion++;\n            event.accepted = true;\n            return;\n        }\n        if (event.key === Qt.Key_Escape && root.polyDrawing) {\n            root.polyDrawing = false;\n            root.polyPoints = [];\n            root.polyVersion++;\n            root.stateVersion++;\n            event.accepted = true;\n            return;\n        }\n        if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && root.polyDrawing) {\n            root.finishPolyShape();\n            event.accepted = true;\n            return;\n        }\n        if ((event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) && root.selectedRect >= 0) {\n            root.deleteRect(root.selectedRect);\n            event.accepted = true;\n        } else if (event.key === Qt.Key_B && root.selectedRect >= 0) {\n            root.toggleAllBezierEdges();\n            event.accepted = true;\n        } else if (root.selectedRect >= 0 && root.selectedRect < root.rects.length\n                   && !root.isShapeLocked(root.selectedRect)\n                   && (event.key === Qt.Key_Left || event.key === Qt.Key_Right\n                       || event.key === Qt.Key_Up || event.key === Qt.Key_Down)) {\n            var step = (event.modifiers & Qt.ShiftModifier) ? 0.05 : 0.01;\n            var dx = 0, dy = 0;\n            if (event.key === Qt.Key_Left)  dx = -step;\n            if (event.key === Qt.Key_Right) dx =  step;\n            if (event.key === Qt.Key_Up)    dy = -step;\n            if (event.key === Qt.Key_Down)  dy =  step;\n            var r = root.rects[root.selectedRect];\n            for (var i = 0; i < r.vertices.length; i++) {\n                r.vertices[i][0] += dx;\n                r.vertices[i][1] += dy;\n            }\n            if (r.edges) {\n                for (var i = 0; i < r.edges.length; i++) {\n                    var e = r.edges[i];\n                    if (e && e.cp1 && e.cp2) {\n                        e.cp1[0] += dx; e.cp1[1] += dy;\n                        e.cp2[0] += dx; e.cp2[1] += dy;\n                    }\n                }\n            }\n            root.stateVersion++;\n            root.saveState(\"Move shape\");\n            root.sendLiveUpdate();\n            event.accepted = true;\n        }\n    }\n}\n","State":[["mapperState",{"String":"[{\"id\":\"shape-1788877600453\",\"name\":\"Polygon 1\",\"vertices\":[[0.2015723587164785,0.1205999346907855],[0.08986307204083921,0.44942407621003627],[0.5299707436424543,0.5482794352110769],[0.6820568809237464,0.2423481136710144]],\"source\":0,\"blend\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"blendGamma\":1,\"srcBlend\":1,\"dstBlend\":7,\"warp\":false,\"opacity\":1,\"visible\":true,\"locked\":false,\"uvMode\":\"manual\",\"uvOffset\":[0,0],\"uvScale\":[1,1],\"uvRotation\":0},{\"id\":\"shape-1788877619756\",\"name\":\"Quad 2\",\"vertices\":[[0.08986307204083921,0.5952370877968278],[0.38986307204083914,0.5952370877968278],[0.38986307204083914,0.8952370877968278],[0.1014856977624613,0.876850979523079]],\"source\":0,\"blend\":{\"top\":0.21,\"right\":0,\"bottom\":0.02,\"left\":0.14},\"blendGamma\":1.9000000000000001,\"srcBlend\":1,\"dstBlend\":7,\"warp\":true,\"gridW\":4,\"gridH\":4,\"opacity\":1,\"visible\":true,\"locked\":false,\"uvMode\":\"auto\",\"uvOffset\":[0,0],\"uvScale\":[1,1],\"uvRotation\":0,\"gridOffsets\":[0,0,0.005383580080753701,-0.03268641470888662,0,0,0,-0.03575076608784474,0,0,-0.06460296096904442,0.015321756894790603,0,0,0,0,0,0,0.04441453566621804,-0.008171603677221655,0,0,0.03768506056527591,0.013278855975485188,0.0026917900403768506,0.04290091930541369,0.03277775297191905,-0.04187946884576098,0,0,-0.06460296096904442,0.0030643513789581204,0,0,0,0,0,0,0.11709286675639301,0.010214504596527068,0,0,0.0013458950201884253,0.04290091930541369,0,0,-0.012113055181695828,0.06639427987742594,0,0]},{\"id\":\"shape-1788877707618\",\"name\":\"Freehand 3\",\"vertices\":[[0.7332400890435609,0.840194075587334],[0.650911889390606,0.8487350414550605],[0.7305007750229495,0.9379064848732953],[0.760472725459488,0.9615725971067498],[0.7895373054890659,0.9755849347063076],[0.8958018167651836,1],[0.9245903952366561,0.982621507977324],[0.9392892636689838,0.9647239808658976],[0.9494734905221811,0.8952370877968278],[0.9247840715232445,0.7357063508552296],[0.8791647196116592,0.5811309977271408],[0.8714615472425385,0.5703106564191825],[0.8205818743490232,0.5482794352110769],[0.7710632570659488,0.540728875953816],[0.6854843636900896,0.5478838475163819],[0.6192300326040476,0.5721281939843614],[0.6060476929718634,0.5812629335295078],[0.5961522020424004,0.6075834203524884],[0.6231493943472409,0.665802530939001],[0.6820568809237464,0.7173069685930273],[0.7710632570659488,0.7132643672140265],[0.7505933658916488,0.7836226677726239]],\"source\":2,\"blend\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"blendGamma\":1,\"srcBlend\":1,\"dstBlend\":7,\"warp\":false,\"opacity\":1,\"visible\":true,\"locked\":false,\"uvMode\":\"manual\",\"uvOffset\":[-0.3,-0.35],\"uvScale\":[0.1,0.1],\"uvRotation\":4},{\"id\":\"shape-1788877741090\",\"name\":\"Quad 4\",\"vertices\":[[0.3200024125310361,0.31956077630234925],[0.5001086808297865,0.2838100102145046],[0.4140804744422072,0.43161389172625125],[0.32134830755122457,0.3917773237997957]],\"source\":3,\"blend\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"blendGamma\":1,\"srcBlend\":1,\"dstBlend\":7,\"warp\":true,\"gridW\":4,\"gridH\":4,\"opacity\":1,\"visible\":true,\"locked\":false,\"uvMode\":\"auto\",\"uvOffset\":[0,0],\"uvScale\":[1,1],\"uvRotation\":0},{\"id\":\"shape-1788878364045\",\"name\":\"Quad 5\",\"vertices\":[[0.5961522020424004,0.024361593462716994],[0.6252167820719783,0.022318692543411584],[0.7749687402518962,0.3917773237997957],[0.4749687402518961,0.3917773237997957]],\"source\":1,\"blend\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"blendGamma\":1,\"srcBlend\":1,\"dstBlend\":4,\"warp\":true,\"gridW\":4,\"gridH\":4,\"opacity\":1,\"visible\":true,\"locked\":false,\"uvMode\":\"auto\",\"uvOffset\":[0,0],\"uvScale\":[1,1],\"uvRotation\":0}]"}]],"Root":"<LIBRARY>:packages/default/Presets/Javascript/rect-mapper/rect-mapper.qml","Inlets":[{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"Texture 1","Exposed":"texture 1","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":1,"Hidden":false,"Custom":"Texture 2","Exposed":"texture 2","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":2,"Hidden":false,"Custom":"Texture 3","Exposed":"texture 3","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":3,"Hidden":false,"Custom":"Texture 4","Exposed":"texture 4","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":4,"Hidden":false,"Custom":"Texture 5","Exposed":"texture 5","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":5,"Hidden":false,"Custom":"Texture 6","Exposed":"texture 6","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":6,"Hidden":false,"Custom":"Texture 7","Exposed":"texture 7","Format":0,"Filter":2,"AddressMode":1},{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":7,"Hidden":false,"Custom":"Texture 8","Exposed":"texture 8","Format":0,"Filter":2,"AddressMode":1},{"uuid":"348b80a4-45dc-4f70-8f5f-6546c85089a2","ObjectName":"Inlet","id":8,"Hidden":true,"Custom":"Subdivisions","Exposed":"subdivisions","Value":{"Int":32},"Init":{"Int":12},"Domain":{"Int":{"Min":4,"Max":64}}},{"uuid":"769dd38a-bfb3-4dc6-b52a-b6abb7afe2a3","ObjectName":"Inlet","id":9,"Hidden":false,"Custom":"Quads","Exposed":"quads"}],"Outlets":[{"uuid":"f1c71046-b754-49a5-8e66-d01374773dfc","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Output","Exposed":"output","Address":"Window:/"}]},{"uuid":"74ca45ff-92c9-44a0-8f1a-754dea05ee1b","ObjectName":"gfxProcess","id":2,"Metadata":{"ScriptingName":"Checkerboard","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-341.90999999999997,-225.39099999999996],"Size":[228.0,331.0],"Loops":false,"FoldMode":0,"Vertex":"","Fragment":"/*\n{\n  \"CATEGORIES\" : [\n    \"Pattern\", \"Color\"\n  ],\n  \"ISFVSN\" : \"2\",\n  \"INPUTS\" : [\n    {\n      \"NAME\" : \"width\",\n      \"TYPE\" : \"float\",\n      \"DEFAULT\" : 0.25\n    },\n    {\n      \"NAME\" : \"offset\",\n      \"TYPE\" : \"point2D\",\n      \"DEFAULT\" : [\n        0,\n        0\n      ],\n      \"MIN\" : [\n      \t0,\n      \t0\n      ],\n      \"MAX\" : [\n      \t1,\n      \t1\n      ]\n    },\n    {\n      \"NAME\" : \"color1\",\n      \"TYPE\" : \"color\",\n      \"DEFAULT\" : [\n        1,\n        1,\n        1,\n        1\n      ]\n    },\n    {\n      \"NAME\" : \"color2\",\n      \"TYPE\" : \"color\",\n      \"DEFAULT\" : [\n        0,\n        0,\n        0,\n        1\n      ]\n    },\n    {\n      \"NAME\" : \"splitPos\",\n      \"TYPE\" : \"point2D\",\n      \"MAX\" : [\n        1,\n        1\n      ],\n      \"DEFAULT\" : [\n        0.5,\n        0.5\n      ],\n      \"MIN\" : [\n        0,\n        0\n      ]\n    }\n  ],\n  \"CREDIT\" : \"by VIDVOX\"\n}\n*/\n\n\n\nvoid main() {\n\t//\tdetermine if we are on an even or odd line\n\t//\tmath goes like..\n\t//\tmod(((coord+offset) / width),2)\n\t\n\t\n\tvec4\t\tout_color = color2;\n\tfloat\t\tsize = width * RENDERSIZE.x;\n\n\tif (size == 0.0)\t{\n\t\tout_color = color1;\n\t}\n\telse if ((mod(((gl_FragCoord.x+(offset.x*RENDERSIZE.x)) / size),2.0) < 2.0 * splitPos.x)&&(mod(((gl_FragCoord.y+(offset.y*RENDERSIZE.y)) / size),2.0) > 2.0 * splitPos.y))\t{\n\t\tout_color = color1;\n\t}\n\telse if ((mod(((gl_FragCoord.x+(offset.x*RENDERSIZE.x)) / size),2.0) > 2.0 * splitPos.x)&&(mod(((gl_FragCoord.y+(offset.y*RENDERSIZE.y)) / size),2.0) < 2.0 * splitPos.y))\t{\n\t\tout_color = color1;\n\t}\n\t\n\tgl_FragColor = out_color;\n}","Root":"<LIBRARY>:packages/default/Presets/GLSL_shaders/basic/generator/Checkerboard.fs","Inlets":[{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":0,"Hidden":true,"Custom":"width","Exposed":"width","Value":{"Float":0.012500000186264515},"Init":{"Float":0.25},"Domain":{"Float":{"Min":-0.25,"Max":0.5}}},{"uuid":"0adbbdda-fda4-451e-91cc-1da731bde9d5","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"offset","Exposed":"offset","Value":{"Vec2f":[0.0,0.0]},"Init":{"Vec2f":[0.0,0.0]},"Domain":{"Vec2f":{"Min":[0.0,0.0],"Max":[1.0,1.0],"Values":[[],[]]}},"Integral":false},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":2,"Hidden":true,"Custom":"color1","Exposed":"color1","Value":{"Vec4f":[1.0,1.0,1.0,1.0]},"Init":{"Vec4f":[1.0,1.0,1.0,1.0]},"Domain":{}},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":3,"Hidden":true,"Custom":"color2","Exposed":"color2","Value":{"Vec4f":[0.0,0.0,0.0,1.0]},"Init":{"Vec4f":[0.0,0.0,0.0,1.0]},"Domain":{}},{"uuid":"0adbbdda-fda4-451e-91cc-1da731bde9d5","ObjectName":"Inlet","id":4,"Hidden":true,"Custom":"splitPos","Exposed":"splitpos","Value":{"Vec2f":[0.5,0.5]},"Init":{"Vec2f":[0.5,0.5]},"Domain":{"Vec2f":{"Min":[0.0,0.0],"Max":[1.0,1.0],"Values":[[],[]]}},"Integral":false}],"Outlets":[{"uuid":"f1c71046-b754-49a5-8e66-d01374773dfc","ObjectName":"Outlet","id":1,"Hidden":false,"Custom":"Texture Out","Exposed":"texture out"}]},{"uuid":"74ca45ff-92c9-44a0-8f1a-754dea05ee1b","ObjectName":"gfxProcess","id":9,"Metadata":{"ScriptingName":"Corner Colors","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-337.35349231860005,422.1826459465991],"Size":[228.0,488.0],"Loops":false,"FoldMode":2,"Vertex":"","Fragment":"/*{\n    \"CATEGORIES\": [\n        \"Color\"\n    ],\n    \"CREDIT\": \"VIDVOX\",\n    \"DESCRIPTION\": \"Generates a gradient that fades between four different colors.\",\n    \"INPUTS\": [\n        {\n            \"DEFAULT\": [\n                1,\n                0,\n                0,\n                1\n            ],\n            \"NAME\": \"color1\",\n            \"TYPE\": \"color\"\n        },\n        {\n            \"DEFAULT\": [\n                0,\n                1,\n                0,\n                1\n            ],\n            \"NAME\": \"color2\",\n            \"TYPE\": \"color\"\n        },\n        {\n            \"DEFAULT\": [\n                0,\n                0,\n                1,\n                1\n            ],\n            \"NAME\": \"color3\",\n            \"TYPE\": \"color\"\n        },\n        {\n            \"DEFAULT\": [\n                1,\n                1,\n                1,\n                1\n            ],\n            \"NAME\": \"color4\",\n            \"TYPE\": \"color\"\n        },\n        {\n            \"DEFAULT\": 0,\n            \"MAX\": 1,\n            \"MIN\": 0,\n            \"NAME\": \"rotationAngle\",\n            \"TYPE\": \"float\"\n        }\n    ],\n    \"ISFVSN\": \"2\"\n}\n*/\n\nconst float pi = 3.1415926535897932384626433832795;\n\nvec2 rotatePointNorm(vec2 pt, float rot)\t{\n\tvec2 returnMe = pt;\n\n\tfloat r = distance(vec2(0.50), returnMe);\n\tfloat a = atan((returnMe.y-0.5),(returnMe.x-0.5));\n\n\treturnMe.x = r * cos(a + 2.0 * pi * rot - pi) + 0.5;\n\treturnMe.y = r * sin(a + 2.0 * pi * rot - pi) + 0.5;\n\t\n\treturnMe = returnMe;\n\t\n\treturn returnMe;\n}\n\nvoid main()\t{\n\tvec4\t\tinputPixelColor = vec4(0.0);\n\tvec4\t\tdist = vec4(0.0);\n\tvec2\t\tpt = isf_FragNormCoord;\n\tpt = rotatePointNorm(pt,rotationAngle+0.5);\n\tdist.r = max(1.0-distance(vec2(0.0,0.0),pt),0.0);\n\tdist.g = max(1.0-distance(vec2(1.0,0.0),pt),0.0);\n\tdist.b = max(1.0-distance(vec2(0.0,1.0),pt),0.0);\n\tdist.a = max(1.0-distance(vec2(1.0,1.0),pt),0.0);\n\t\n\tinputPixelColor = (color1 * dist.r + color2 * dist.g + color3 * dist.b + color4 * dist.a) / (dist.r + dist.g + dist.b + dist.a);\n\t\n\tgl_FragColor = inputPixelColor;\n}\n","Root":"<LIBRARY>:packages/default/Presets/GLSL_shaders/basic/generator/Corner Colors.fs","Inlets":[{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":0,"Hidden":true,"Custom":"color1","Exposed":"color1","Value":{"Vec4f":[1.0,0.0,0.0,1.0]},"Init":{"Vec4f":[1.0,0.0,0.0,1.0]},"Domain":{}},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"color2","Exposed":"color2","Value":{"Vec4f":[0.0,1.0,0.0,1.0]},"Init":{"Vec4f":[0.0,1.0,0.0,1.0]},"Domain":{}},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":2,"Hidden":true,"Custom":"color3","Exposed":"color3","Value":{"Vec4f":[0.0,0.0,1.0,1.0]},"Init":{"Vec4f":[0.0,0.0,1.0,1.0]},"Domain":{}},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":3,"Hidden":true,"Custom":"color4","Exposed":"color4","Value":{"Vec4f":[1.0,1.0,1.0,1.0]},"Init":{"Vec4f":[1.0,1.0,1.0,1.0]},"Domain":{}},{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":4,"Hidden":true,"Custom":"rotationAngle","Exposed":"rotationangle","Value":{"Float":0.3166666626930237},"Init":{"Float":0.0},"Domain":{"Float":{"Min":0.0,"Max":1.0}}}],"Outlets":[{"uuid":"f1c71046-b754-49a5-8e66-d01374773dfc","ObjectName":"Outlet","id":1,"Hidden":false,"Custom":"Texture Out","Exposed":"texture out"}]},{"uuid":"1e17e479-3513-44c8-a8a7-017be9f6ac8a","ObjectName":"LFO","id":10,"Metadata":{"ScriptingName":"LFO","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-642.91,855.609],"Size":[276.0,105.0],"Loops":false,"FoldMode":0,"Inlets":[{"uuid":"5554eb67-bcc8-45ab-8ec2-37a3f191aa64","ObjectName":"Inlet","id":0,"Hidden":true,"Custom":"Freq.","Exposed":"freq.","Value":{"Float":1.0},"Init":{"Float":1.0},"Domain":{"Float":{"Min":0.009999999776482582,"Max":100.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"Ampl.","Exposed":"ampl.","Value":{"Float":0.5},"Init":{"Float":0.5},"Domain":{"Float":{"Min":0.0,"Max":2.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":2,"Hidden":true,"Custom":"Offset","Exposed":"offset","Value":{"Float":0.5},"Init":{"Float":0.5},"Domain":{"Float":{"Min":-1.0,"Max":1.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":3,"Hidden":true,"Custom":"Jitter","Exposed":"jitter","Value":{"Float":0.0},"Init":{"Float":0.0},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},{"uuid":"82427d27-084a-4ab6-9c4e-db83929a1200","ObjectName":"Inlet","id":4,"Hidden":true,"Custom":"Phase","Exposed":"phase","Value":{"Float":0.0},"Init":{"Float":0.0},"Domain":{"Float":{"Min":-1.0,"Max":1.0}}},{"uuid":"8b1d76c4-3838-4ac0-9b9c-c12bc5db8e8a","ObjectName":"Inlet","id":5,"Hidden":true,"Custom":"Waveform","Exposed":"waveform","Value":{"String":"Saw"},"Init":{"String":"Sin"},"Domain":{"String":{"Values":["Sin","Triangle","Saw","Square","SampleAndHold","Noise1","Noise2","Noise3"]}},"Values":["Sin","Triangle","Saw","Square","SampleAndHold","Noise1","Noise2","Noise3"],"Pixmaps":[":/icons/wave_sin_off.png",":/icons/wave_sin_on.png",":/icons/wave_triangle_off.png",":/icons/wave_triangle_on.png",":/icons/wave_saw_off.png",":/icons/wave_saw_on.png",":/icons/wave_square_off.png",":/icons/wave_square_on.png",":/icons/wave_sample_and_hold_off.png",":/icons/wave_sample_and_hold_on.png",":/icons/wave_noise1_off.png",":/icons/wave_noise1_on.png",":/icons/wave_noise2_off.png",":/icons/wave_noise2_on.png",":/icons/wave_noise3_off.png",":/icons/wave_noise3_on.png"]},{"uuid":"485680cc-b8b9-4a01-acc7-3e8334bdc016","ObjectName":"Inlet","id":6,"Hidden":true,"Custom":"Quantification","Exposed":"quantification","Value":{"Float":1.0},"Init":{"Float":0.25},"Domain":{"Float":{"Values":[0.0,1.0,0.5,0.25,0.125,0.0625,0.03125,0.015625,0.75,0.375,0.1875,0.09375,0.046875]}},"Values":[["None",{"Float":0.0}],["Whole",{"Float":1.0}],["Half",{"Float":0.5}],["4th",{"Float":0.25}],["8th",{"Float":0.125}],["16th",{"Float":0.0625}],["32th",{"Float":0.03125}],["64th",{"Float":0.015625}],["Dotted Half",{"Float":0.75}],["Dotted 4th",{"Float":0.375}],["Dotted 8th",{"Float":0.1875}],["Dotted 16th",{"Float":0.09375}],["Dotted 32th",{"Float":0.046875}]],"FolderPort":"","FileExtensions":""}],"Outlets":[{"uuid":"cff96158-cc72-46d7-99dc-b6038171375b","ObjectName":"Outlet","id":0,"Hidden":false,"Custom":"Out","Exposed":"out"}]},{"uuid":"7be51631-fb4b-4152-9ca7-86fdafa8a989","ObjectName":"Display","id":11,"Metadata":{"ScriptingName":"Text Box","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-338.8613569752987,-515.3768161947862],"Size":[507.0,242.0],"Loops":false,"FoldMode":0,"Inlets":[{"uuid":"9ae797ea-d94c-4792-acec-9ec1932bae5d","ObjectName":"Inlet","id":0,"Hidden":true,"Custom":"in","Exposed":"in","Value":{"String":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><meta charset=\"utf-8\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\nhr { height: 1px; border-width: 0; }\nli.unchecked::marker { content: \"\\2610\"; }\nli.checked::marker { content: \"\\2612\"; }\n</style></head><body style=\" font-family:'Ubuntu'; font-size:12px; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">ossia score supports video mapping in multiple ways.</p>\n<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">A first way to do this is through the rect-mapper object,</p>\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">which allows to map and position multiple textures in a single viewport.</p>\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br />To open the mapping interface, click the small &quot;Show/hide UI&quot; button<br />on the header of the rect-mapper object.<br /><br />It supports:<br />- shape deformation for quads<br />- soft edges<br />- 8 potential input textures and as many mapped objects as you want<br />- blend modes &amp; masking<br />- creating custom polygons: doubl-click in the background to start a custom polygon shape.<br />- free-hand drawing: hold alt / option and start moving in the background<br /><br />If the rendering is too slow, decrease &quot;subdivisions&quot; which is how good the quality of the<br />mapping subdivisions will be</p></body></html>"},"Init":{"String":"(Double-click to edit)"},"Domain":{}}],"Outlets":[]},{"uuid":"74ca45ff-92c9-44a0-8f1a-754dea05ee1b","ObjectName":"gfxProcess","id":13,"Metadata":{"ScriptingName":"ColorGrid","Comment":"","Color":"Transparent1","Label":"","Touched":true},"Duration":10584000000,"Height":300.0,"StartOffset":0,"LoopDuration":10584000000,"Pos":[-340.8437644370682,142.98000512093333],"Size":[228.0,238.0],"Loops":false,"FoldMode":2,"Vertex":"","Fragment":"/*{\n\t\"CREDIT\" : \"ColorGrid by iq\",\n\t\"CATEGORIES\" : [\n\t\t\"ci\"\n\t],\n\t\"DESCRIPTION\": \"\",\n\t\"INPUTS\": [\n\t\t{\n\t\t\t\"NAME\": \"inputImage\",\n\t\t\t\"TYPE\" : \"image\"\n\t\t},\n\t\t{\n\t\t\t\"NAME\": \"iZoom\",\n\t\t\t\"TYPE\" : \"float\",\n\t\t\t\"MIN\" : 0.0,\n\t\t\t\"MAX\" : 1.0,\n\t\t\t\"DEFAULT\" : 1.0\n\t\t},\n\t\t{\n\t\t\t\"NAME\": \"iSteps\",\n\t\t\t\"TYPE\" : \"float\",\n\t\t\t\"MIN\" : 2.0,\n\t\t\t\"MAX\" : 75.0,\n\t\t\t\"DEFAULT\" : 19.0\n\t\t},\n\t\t{\n\t\t\t\"NAME\" :\"iMouse\",\n\t\t\t\"TYPE\" : \"point2D\",\n\t\t\t\"DEFAULT\" : [0.0, 0.0],\n\t\t\t\"MAX\" : [640.0, 480.0],\n\t\t\t\"MIN\" : [0.0, 0.0]\n\t\t},\n\t\t{\n\t\t\t\"NAME\": \"iColor\", \n\t\t\t\"TYPE\" : \"color\", \n\t\t\t\"DEFAULT\" : [\n\t\t\t\t0.9, \n\t\t\t\t0.6, \n\t\t\t\t0.0, \n\t\t\t\t1.0\n\t\t\t]\n\t\t}\n\t]\n}\n*/\n// https://www.shadertoy.com/view/4dBSRK\nvoid main(void) \n{\n\tvec2  px = 4.0*(-RENDERSIZE.xy + 2.0*gl_FragCoord.xy)/RENDERSIZE.y;\n\tfloat id = 0.5 + 0.5*cos(TIME + sin(dot(floor(px+0.5),vec2(113.1,17.81)))*43758.545);\n\tvec3 co = 0.5 + 0.5*cos(TIME + 3.5*id + vec3(0.0,1.57,3.14) );\n\tvec2 pa = smoothstep( 0.0, 0.2, id*(0.5 + 0.5*cos(6.2831*px)) );\n\tgl_FragColor = vec4( co*pa.x*pa.y, 1.0 );\n}\n","Root":"<LIBRARY>:packages/default/Presets/GLSL_shaders/sophia-digital-art/ColorGrid.fs","Inlets":[{"uuid":"5ac86198-2d03-4830-9e41-a6d529922d29","ObjectName":"Inlet","id":0,"Hidden":false,"Custom":"inputImage","Exposed":"inputimage","Format":0,"Filter":2,"AddressMode":1},{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":1,"Hidden":true,"Custom":"iZoom","Exposed":"izoom","Value":{"Float":1.0},"Init":{"Float":1.0},"Domain":{"Float":{"Min":0.0,"Max":1.0}}},{"uuid":"af2b4fc3-aecb-4c15-a5aa-1c573a239925","ObjectName":"Inlet","id":2,"Hidden":true,"Custom":"iSteps","Exposed":"isteps","Value":{"Float":19.0},"Init":{"Float":19.0},"Domain":{"Float":{"Min":2.0,"Max":75.0}}},{"uuid":"0adbbdda-fda4-451e-91cc-1da731bde9d5","ObjectName":"Inlet","id":3,"Hidden":true,"Custom":"iMouse","Exposed":"imouse","Address":"Window:/cursor/absolute","Value":{"Vec2f":[0.0,0.0]},"Init":{"Vec2f":[0.0,0.0]},"Domain":{"Vec2f":{"Min":[0.0,0.0],"Max":[640.0,480.0],"Values":[[],[]]}},"Integral":false},{"uuid":"8f38638e-9f9f-48b0-ae36-1cba86ef5703","ObjectName":"Inlet","id":4,"Hidden":true,"Custom":"iColor","Exposed":"icolor","Value":{"Vec4f":[0.8999999761581421,0.6000000238418579,0.0,1.0]},"Init":{"Vec4f":[0.8999999761581421,0.6000000238418579,0.0,1.0]},"Domain":{}}],"Outlets":[{"uuid":"f1c71046-b754-49a5-8e66-d01374773dfc","ObjectName":"Outlet","id":1,"Hidden":false,"Custom":"Texture Out","Exposed":"texture out"}]}],"SmallViewRack":[{"Processes":[1,2,9,10,11,13],"Process":null,"Height":200.0,"Nodal":true}],"FullViewRack":[{"Process":0,"Nodal":true}],"DefaultDuration":10584000000,"MinDuration":10584000000,"MaxDuration":11113200000,"GuiDuration":11642400000,"Speed":1.0,"Rigidity":false,"MinNull":false,"MaxInf":true,"Signatures":[[0,[4,4]]],"StartState":0,"EndState":1,"StartDate":0,"HeightPercentage":0.0,"NodalSlotHeight":100.0,"QuantizationRate":-1.0,"Zoom":7525985.295716857,"Center":4688568325,"NodalCenter":[-62.29286443574675,-235.70785530386217],"NodalScale":1.4226030725599796,"ViewMode":1,"SmallViewShown":true,"HasSignature":true},"StartTimeNode":{"ObjectName":"Scenario::TimeSyncModel","id":0,"Metadata":{"ScriptingName":"Sync.start","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":0,"Events":[0],"MusicalSync":-1.0,"AutoTrigger":false,"Start":true,"Active":false,"Expression":" { true == false } "},"EndTimeNode":{"ObjectName":"Scenario::TimeSyncModel","id":1,"Metadata":{"ScriptingName":"Sync.end","Comment":"","Color":"Gray","Label":"","Touched":true},"Date":10584000000,"Events":[1],"MusicalSync":-1.0,"AutoTrigger":false,"Start":false,"Active":true,"Expression":" { true == false } "},"StartEvent":{"ObjectName":"Scenario::EventModel","id":0,"Metadata":{"ScriptingName":"Event.start","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":0,"States":[0],"Condition":"","Date":0,"Offset":0},"EndEvent":{"ObjectName":"Scenario::EventModel","id":1,"Metadata":{"ScriptingName":"Event.end","Comment":"","Color":"Emphasis4","Label":"","Touched":true},"TimeNode":1,"States":[1],"Condition":"","Date":10584000000,"Offset":0},"StartState":{"ObjectName":"Scenario::StateModel","id":0,"Metadata":{"ScriptingName":"State.start","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":0,"PreviousConstraint":null,"NextConstraint":0,"HeightPercentage":0.0,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]},"Controls":[],"StateProcesses":[]},"EndState":{"ObjectName":"Scenario::StateModel","id":1,"Metadata":{"ScriptingName":"State.end","Comment":"","Color":"Base1","Label":"","Touched":true},"Event":1,"PreviousConstraint":0,"NextConstraint":null,"HeightPercentage":0.0,"Messages":{"Name":"","Accessors":[],"Unit":"none","Previous":[],"Following":[],"User":null,"Priorities":[1,2,0]},"Controls":[],"StateProcesses":[]}},"Speed":1.0,"Cables":[{"ObjectName":"Process::Cable","id":1,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"gfxProcess","ObjectId":2},{"ObjectName":"Outlet","ObjectId":1}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Javascript","ObjectId":1},{"ObjectName":"Inlet","ObjectId":0}]},{"ObjectName":"Process::Cable","id":11,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"gfxProcess","ObjectId":13},{"ObjectName":"Outlet","ObjectId":1}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Javascript","ObjectId":1},{"ObjectName":"Inlet","ObjectId":1}]},{"ObjectName":"Process::Cable","id":8,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"gfxProcess","ObjectId":9},{"ObjectName":"Outlet","ObjectId":1}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"Javascript","ObjectId":1},{"ObjectName":"Inlet","ObjectId":2}]},{"ObjectName":"Process::Cable","id":9,"Type":0,"Source":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"LFO","ObjectId":10},{"ObjectName":"Outlet","ObjectId":0}],"Sink":[{"ObjectName":"Scenario::ScenarioDocumentModel","ObjectId":1},{"ObjectName":"Scenario::BaseScenario","ObjectId":0},{"ObjectName":"Scenario::IntervalModel","ObjectId":0},{"ObjectName":"gfxProcess","ObjectId":9},{"ObjectName":"Inlet","ObjectId":4}]}],"BusIntervals":[]},"Plugins":[{"uuid":"aac1402f-6851-4a1a-ab50-5f213acd1262","Name":"video mapping rect","Author":"","Description":"Maps and positions several textures in a single viewport with the rect-mapper object.","Url":"","Created":"2026-09-08T14:24:28.656Z","LastSaved":"2026-09-08T14:44:35.169Z","Thumbnail":"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","AutomaticThumbnail":true},{"uuid":"1f923578-08c3-49be-9ba9-69c144ee2e32","Refresh":false,"Reconnect":false,"MidiRatio":1.0},{"uuid":"6e610e1f-9de2-4c36-90dd-0ef570002a21","RootNode":{},"Children":[{"Device":{"Name":"Window","Protocol":"5a181207-7d40-4ad8-814e-879fcdf8cc31","Mode":0,"Outputs":[],"InputWidth":1920,"InputHeight":1080,"SwapchainFlag":0,"SwapchainFormat":0},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"String":""},"Domain":{"String":{}},"Name":"screen"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[100.0,100.0]},"Domain":{},"Name":"position"}},{"Address":{"ClipMode":"Free","RepetitionFilter":false,"Value":{},"Domain":{},"Name":"cursor"},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{"Float":{"Min":0.0,"Max":1.0}},"Name":"scaled"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{"Float":{"Min":0.0,"Max":1.0}},"Name":"gl"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{"Vec2f":{"Min":[0.0,0.0],"Max":[1280.0,270.0],"Values":[[],[]]}},"Name":"absolute"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Bool":false},"Domain":{},"Name":"visible"}}]},{"Address":{"ClipMode":"Free","RepetitionFilter":false,"Value":{},"Domain":{},"Name":"tablet"},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{"Float":{"Min":0.0,"Max":1.0}},"Name":"scaled"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{"Vec2f":{"Min":[0.0,0.0],"Max":[1280.0,270.0],"Values":[[],[]]}},"Name":"absolute"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Int":0},"Domain":{},"Name":"z"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{},"Name":"pressure"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{"Float":{"Min":-1.0,"Max":1.0}},"Name":"tangential"}},{"Address":{"ioType":"<->","ClipMode":"Free","Unit":"angle.degree","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{"Float":{"Min":-180.0,"Max":180.0}},"Name":"rotation"}},{"Address":{"ioType":"<->","ClipMode":"Free","Unit":"angle.degree","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{"Float":{"Min":-60.0,"Max":60.0}},"Name":"tilt_x"}},{"Address":{"ioType":"<->","ClipMode":"Free","Unit":"angle.degree","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{"Float":{"Min":-60.0,"Max":60.0}},"Name":"tilt_y"}}]},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[1280.0,720.0]},"Domain":{},"Name":"size"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Vec2f":[0.0,0.0]},"Domain":{},"Extended":{"description":"Set to [0, 0] to use the viewport's size"},"Name":"rendersize"}},{"Address":{"ClipMode":"Free","RepetitionFilter":false,"Value":{},"Domain":{},"Name":"key"},"Children":[{"Address":{"ClipMode":"Free","RepetitionFilter":false,"Value":{},"Domain":{},"Name":"press"},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Int":0},"Domain":{},"Name":"code"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"String":""},"Domain":{},"Name":"text"}}]},{"Address":{"ClipMode":"Free","RepetitionFilter":false,"Value":{},"Domain":{},"Name":"release"},"Children":[{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Int":0},"Domain":{},"Name":"code"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"String":""},"Domain":{},"Name":"text"}}]}]},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Bool":false},"Domain":{},"Name":"fullscreen"}},{"Address":{"ioType":"<->","ClipMode":"Free","RepetitionFilter":false,"Value":{"Float":0.0},"Domain":{},"Name":"fps"}}]}]},{"uuid":"05e72689-e02c-4c9d-a0bf-fe84c32d3d96","Data":""}],"Version":4,"Commit":"1d56cfd1f967adb450195c915818a98bf869c047","Tag":"3.8.2"}