手順を導き出すTaskPath


・ダウンロードされる方はこちら。↓

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Logic Synthesizer 3D</title>
    <style>
        /* UI Theme */
        body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #050505; color: #cfcfcf; font-family: 'Segoe UI', monospace; }
        
        #menubar { height: 45px; background: #1a1a1a; display: flex; align-items: center; padding: 0 15px; border-bottom: 1px solid #333; box-shadow: 0 4px 10px rgba(0,0,0,0.5); z-index: 10; }
        .brand { font-weight: 700; color: #00e676; margin-right: 20px; font-size: 1.1em; letter-spacing: 1px; }
        
        .control-group { display: flex; align-items: center; gap: 10px; margin-right: 20px; }
        select, button { background: #333; border: 1px solid #444; color: #eee; padding: 6px 12px; border-radius: 4px; cursor: pointer; transition: 0.2s; }
        select:hover, button:hover { border-color: #00e676; color: #fff; }
        button.primary { background: #00e676; color: #000; font-weight: bold; border: none; }
        button.primary:hover { background: #00c853; }

        #container { display: flex; height: calc(100% - 45px); }

        /* Left Panel: Function DB */
        #left-panel { width: 280px; background: #111; border-right: 1px solid #222; display: flex; flex-direction: column; z-index: 5; }
        .panel-header { padding: 12px; background: #1a1a1a; font-weight: bold; border-bottom: 1px solid #222; color: #888; font-size: 0.8em; text-transform: uppercase; }
        #function-list { flex: 1; overflow-y: auto; padding: 5px; }
        .func-item { background: #1a1a1a; border: 1px solid #333; margin-bottom: 5px; padding: 8px; border-radius: 4px; font-size: 0.85em; }
        .func-name { color: #4db8ff; font-weight: bold; margin-bottom: 4px; }
        .func-io { display: flex; justify-content: space-between; color: #888; font-size: 0.8em; }
        .io-tag { background: #222; padding: 1px 4px; border-radius: 2px; }

        /* Center: 3D Canvas */
        #center-panel { flex: 1; position: relative; background: radial-gradient(circle at center, #1a1a1a 0%, #000 100%); }
        #canvas-container { width: 100%; height: 100%; outline: none; }
        
        /* Overlay Status */
        #status-overlay { position: absolute; bottom: 20px; left: 20px; color: #888; font-size: 0.9em; pointer-events: none; }

        /* Right Panel: Solution Details */
        #right-panel { width: 300px; background: #111; border-left: 1px solid #222; display: flex; flex-direction: column; overflow-y: auto; z-index: 5; }
        .step-item { padding: 15px; border-bottom: 1px solid #222; position: relative; }
        .step-num { position: absolute; top: 10px; right: 10px; font-size: 2em; color: #222; font-weight: bold; }
        .step-title { font-weight: bold; color: #eee; margin-bottom: 5px; }
        .step-desc { font-size: 0.85em; color: #aaa; line-height: 1.4; }
        .req-input { color: #ff9100; font-size: 0.85em; margin-top: 5px; }

        .hidden { display: none !important; }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>

    <div id="menubar">
        <div class="brand">LOGIC SYNTH 3D</div>
        
        <div class="control-group">
            <span style="font-size:0.8em; color:#888;">GOAL:</span>
            <select id="goal-select">
                </select>
            <button class="primary" onclick="app.solve()">AUTO SYNTHESIZE</button>
        </div>

        <div class="control-group">
            <button onclick="app.resetView()">Reset View</button>
        </div>
    </div>

    <div id="container">
        <div id="left-panel">
            <div class="panel-header">Function Database</div>
            <div id="function-list"></div>
        </div>

        <div id="center-panel">
            <div id="canvas-container"></div>
            <div id="status-overlay">Select a goal and click Synthesize to generate the logic tree.</div>
        </div>

        <div id="right-panel">
            <div class="panel-header">Execution Schedule</div>
            <div id="schedule-list">
                <div style="padding:20px; color:#555; text-align:center;">
                    No solution generated yet.
                </div>
            </div>
        </div>
    </div>

    <script>
        /**
         * 1. Data Definitions
         * Defining the "Types" and "Functions" available in the database.
         */
        const DATA_TYPES = {
            RAW_DATA: "Raw CSV",
            CLEAN_DATA: "Cleaned Data",
            SALES_LIST: "Sales List",
            DATE_RANGE: "Date Range",
            MONTHLY_REPORT: "Monthly Report",
            CHART_IMAGE: "Chart Image",
            PDF_REPORT: "PDF File",
            EMAIL_DRAFT: "Email Draft",
            SENT_STATUS: "Sent Status"
        };

        // The "Function Database"
        // cost: arbitrary unit of effort/time needed to run this function
        const FUNCTION_DB = [
            { id: "f1", name: "Load CSV", inputs: [], output: DATA_TYPES.RAW_DATA, cost: 1, desc: "User uploads a CSV file." },
            { id: "f2", name: "Remove Nulls", inputs: [DATA_TYPES.RAW_DATA], output: DATA_TYPES.CLEAN_DATA, cost: 2, desc: "Clean data rows." },
            { id: "f3", name: "Extract Sales", inputs: [DATA_TYPES.CLEAN_DATA], output: DATA_TYPES.SALES_LIST, cost: 2, desc: "Filter sales columns." },
            { id: "f4", name: "Input Date", inputs: [], output: DATA_TYPES.DATE_RANGE, cost: 1, desc: "User inputs start/end date." },
            { id: "f5", name: "Aggregate", inputs: [DATA_TYPES.SALES_LIST, DATA_TYPES.DATE_RANGE], output: DATA_TYPES.MONTHLY_REPORT, cost: 5, desc: "Sum sales by month." },
            { id: "f6", name: "Generate Chart", inputs: [DATA_TYPES.MONTHLY_REPORT], output: DATA_TYPES.CHART_IMAGE, cost: 3, desc: "Create a line chart." },
            { id: "f7", name: "Make PDF", inputs: [DATA_TYPES.MONTHLY_REPORT, DATA_TYPES.CHART_IMAGE], output: DATA_TYPES.PDF_REPORT, cost: 4, desc: "Combine text and chart." },
            { id: "f8", name: "Write Email", inputs: [DATA_TYPES.PDF_REPORT], output: DATA_TYPES.EMAIL_DRAFT, cost: 2, desc: "Draft email with attachment." },
            { id: "f9", name: "Send Email", inputs: [DATA_TYPES.EMAIL_DRAFT], output: DATA_TYPES.SENT_STATUS, cost: 1, desc: "Send via SMTP." },
            // Alternative paths (to cause branching choices)
            { id: "f10", name: "Quick Summary", inputs: [DATA_TYPES.RAW_DATA], output: DATA_TYPES.MONTHLY_REPORT, cost: 10, desc: "Rough summary directly from raw data (Low quality)." },
            { id: "f11", name: "Direct Print", inputs: [DATA_TYPES.CHART_IMAGE], output: DATA_TYPES.SENT_STATUS, cost: 5, desc: "Print and mail physically." }
        ];

        /**
         * 2. The Logic Engine (Planner)
         * Uses Backward Chaining + Simple Heuristic to find the best path.
         */
        class LogicSolver {
            constructor(db) {
                this.db = db;
            }

            // Find a function that produces a specific output type
            findProviders(outputType) {
                return this.db.filter(f => f.output === outputType);
            }

            // Main recursive solver
            // Returns a tree node: { function: funcObj, inputs: [childNodes], totalCost: n }
            solve(targetType, visited = new Set()) {
                // Find all candidates
                const candidates = this.findProviders(targetType);
                
                if (candidates.length === 0) return null; // No way to make this

                let bestPath = null;
                let minCost = Infinity;

                for (const func of candidates) {
                    if (visited.has(func.id)) continue; // Avoid cycles

                    const newVisited = new Set(visited);
                    newVisited.add(func.id);

                    // Solve for inputs
                    const inputPaths = [];
                    let possible = true;
                    let currentCost = func.cost;

                    for (const inputType of func.inputs) {
                        // Recursively find how to make this input
                        const subPath = this.solve(inputType, newVisited);
                        
                        if (subPath) {
                            inputPaths.push(subPath);
                            currentCost += subPath.totalCost;
                        } else {
                            // Can't make this input? 
                            // Check if it's a "User Input" (functions with 0 inputs in DB are sources)
                            // But wait, our DB defines sources as functions with inputs:[]
                            // So if subPath is null, it means we have NO function to make it.
                            // In this DB design, if inputType isn't produced by anyone, it's impossible.
                            possible = false; 
                            break;
                        }
                    }

                    if (possible) {
                        if (currentCost < minCost) {
                            minCost = currentCost;
                            bestPath = {
                                func: func,
                                inputs: inputPaths,
                                totalCost: currentCost
                            };
                        }
                    }
                }

                return bestPath;
            }

            // Flatten the tree into a scheduled list (Topological Sort ish)
            flatten(node, list = [], depth = 0) {
                if (!node) return;
                
                // Add dependencies first
                node.inputs.forEach(n => this.flatten(n, list, depth + 1));
                
                // Add self if not already in list
                if (!list.find(i => i.func.id === node.func.id)) {
                    // Assign a "Layer" for visualization (Max depth of dependencies)
                    // Simplified: We just push to list. The index in list is roughly execution order.
                    const layer = list.length; 
                    list.push({ ...node.func, layer: layer });
                }
                return list;
            }
        }

        /**
         * 3. Application Controller
         */
        class App {
            constructor() {
                this.solver = new LogicSolver(FUNCTION_DB);
                this.viz = new Visualizer();
                this.initUI();
            }

            initUI() {
                // Populate Function List
                const fList = document.getElementById('function-list');
                FUNCTION_DB.forEach(f => {
                    const el = document.createElement('div');
                    el.className = 'func-item';
                    el.innerHTML = `
                        <div class="func-name">${f.name}</div>
                        <div class="func-io">
                            <span>IN: ${f.inputs.length ? f.inputs.join(', ') : 'User'}</span>
                            <span class="io-tag">OUT: ${f.output}</span>
                        </div>
                    `;
                    fList.appendChild(el);
                });

                // Populate Goal Select
                const gSelect = document.getElementById('goal-select');
                Object.values(DATA_TYPES).forEach(type => {
                    const opt = document.createElement('option');
                    opt.value = type;
                    opt.innerText = type;
                    if(type === DATA_TYPES.SENT_STATUS) opt.selected = true; // Default goal
                    gSelect.appendChild(opt);
                });

                this.viz.init();
            }

            solve() {
                const goal = document.getElementById('goal-select').value;
                const solutionTree = this.solver.solve(goal);

                if (!solutionTree) {
                    alert("No valid path found to generate this output.");
                    return;
                }

                // Convert tree to linear schedule
                const schedule = this.solver.flatten(solutionTree);
                
                // Visualize
                this.renderSchedule(schedule);
                this.viz.renderGraph(schedule, solutionTree);
            }

            renderSchedule(schedule) {
                const container = document.getElementById('schedule-list');
                container.innerHTML = '';
                
                schedule.forEach((step, index) => {
                    const el = document.createElement('div');
                    el.className = 'step-item';
                    el.innerHTML = `
                        <div class="step-num">${index + 1}</div>
                        <div class="step-title">${step.name}</div>
                        <div class="step-desc">${step.desc}</div>
                        ${step.inputs.length === 0 ? '<div class="req-input">⚠️ Waiting for User Input</div>' : ''}
                    `;
                    container.appendChild(el);
                });
            }

            resetView() {
                this.viz.resetCamera();
            }
        }

        /**
         * 4. 3D Visualization (Layout Engine)
         */
        class Visualizer {
            constructor() {
                this.container = document.getElementById('canvas-container');
                this.scene = null;
                this.meshMap = new Map();
            }

            init() {
                this.scene = new THREE.Scene();
                this.scene.background = new THREE.Color(0x050505);
                
                // Camera
                this.camera = new THREE.PerspectiveCamera(60, this.container.clientWidth/this.container.clientHeight, 0.1, 1000);
                this.camera.position.set(0, 10, 30);

                // Renderer
                this.renderer = new THREE.WebGLRenderer({antialias: true});
                this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
                this.container.appendChild(this.renderer.domElement);

                // Controls
                this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
                this.controls.enableDamping = true;

                // Light
                const light = new THREE.DirectionalLight(0xffffff, 1);
                light.position.set(10, 20, 10);
                this.scene.add(light);
                this.scene.add(new THREE.AmbientLight(0x404040));

                // Grid
                const grid = new THREE.GridHelper(100, 20, 0x222222, 0x111111);
                this.scene.add(grid);

                // Resize
                window.addEventListener('resize', () => {
                    this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
                    this.camera.updateProjectionMatrix();
                    this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
                });

                this.animate();
            }

            // Render the scheduled graph
            renderGraph(flatList, tree) {
                // Clear old
                this.meshMap.forEach(m => this.scene.remove(m));
                this.meshMap.clear();
                this.scene.children.filter(c => c.type === "Line" || c.type === "Sprite").forEach(c => this.scene.remove(c));

                // Calculate Layers for 3D positioning (X axis = Time/Step)
                // We re-calculate depth properly for layout
                const depthMap = new Map();
                const setDepth = (node, d) => {
                    depthMap.set(node.func.id, Math.max(d, depthMap.get(node.func.id) || 0));
                    node.inputs.forEach(n => setDepth(n, d - 1));
                };
                
                // Helper to walk the tree object
                const walkTree = (node, d) => {
                    setDepth(node, d); // Assign abstract depth
                };
                
                // Start from the goal (rightmost)
                walkTree(tree, 0);

                // Normalize depths so start is 0
                const minD = Math.min(...depthMap.values());
                flatList.forEach(f => {
                    f._x = (depthMap.get(f.id) - minD) * 8; // Spacing X
                    f._z = 0; // Will adjust Z to avoid overlap
                });

                // Resolve overlaps in Z (same layer)
                const layerCounts = {};
                flatList.forEach(f => {
                    if(!layerCounts[f._x]) layerCounts[f._x] = 0;
                    f._z = layerCounts[f._x] * 6 - (layerCounts[f._x]*3); // Crude spacing
                    if (layerCounts[f._x] % 2 !== 0) f._z *= -1; // Zigzag
                    layerCounts[f._x]++;
                });

                // Draw Nodes
                const geo = new THREE.BoxGeometry(3, 1, 2);
                flatList.forEach(f => {
                    const color = f.inputs.length === 0 ? 0xff9100 : 0x00e676; // Orange for Input, Green for Process
                    const mat = new THREE.MeshLambertMaterial({color: color});
                    const mesh = new THREE.Mesh(geo, mat);
                    mesh.position.set(f._x - 20, 0.5, f._z); // Offset to center
                    
                    this.scene.add(mesh);
                    this.meshMap.set(f.id, mesh);

                    // Label
                    this.addLabel(f.name, mesh.position);
                });

                // Draw Links (Dependencies)
                const lineMat = new THREE.LineBasicMaterial({color: 0x4db8ff});
                
                // We need to draw lines based on inputs described in the flat list
                // But the flat list functions don't reference the *specific instances* in the tree easily
                // So we loop the flat list and find providers in the flat list
                flatList.forEach(consumer => {
                    const consumerMesh = this.meshMap.get(consumer.id);
                    
                    // Who provides my inputs?
                    consumer.inputs.forEach(inputType => {
                        // In this simple model, we look for the node in flatList that outputs this type
                        // Note: If multiple output same type, LogicSolver picked one. 
                        // For visualization simplicity, we just find the one that exists in our schedule.
                        const provider = flatList.find(p => p.output === inputType && p._x < consumer._x);
                        if (provider) {
                            const providerMesh = this.meshMap.get(provider.id);
                            
                            const pts = [];
                            pts.push(providerMesh.position.clone());
                            // Add a "elbow" point for nicer routing? For now straight line.
                            pts.push(consumerMesh.position.clone());
                            
                            const g = new THREE.BufferGeometry().setFromPoints(pts);
                            this.scene.add(new THREE.Line(g, lineMat));
                        }
                    });
                });
            }

            addLabel(text, pos) {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = 256; canvas.height = 64;
                ctx.font = "Bold 40px Arial";
                ctx.fillStyle = "white";
                ctx.textAlign = "center";
                ctx.fillText(text, 128, 48);
                const tex = new THREE.CanvasTexture(canvas);
                const mat = new THREE.SpriteMaterial({map:tex});
                const sp = new THREE.Sprite(mat);
                sp.position.copy(pos);
                sp.position.y += 1.5;
                sp.scale.set(6, 1.5, 1);
                this.scene.add(sp);
            }

            resetCamera() {
                this.camera.position.set(0, 10, 30);
                this.controls.reset();
            }

            animate() {
                requestAnimationFrame(() => this.animate());
                this.controls.update();
                this.renderer.render(this.scene, this.camera);
            }
        }

        const app = new App();

    </script>
</body>
</html>

いいなと思ったら応援しよう!

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
手順を導き出すTaskPath|古井和雄
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1