Search code examples
plantuml

Is it possible to specify starting item when rendering a PlantUML diagram?


I used a tool to auto-generate a puml file from a large codebase. I can render the whole thing as an svg, but is it possible to render only part of the puml file?

Specifically, I have an interface ICameraSetting and I want to map out all interactions with it. The svg of the whole codebase is too big to comfortably work with, so I would like to tell the PlantUML jar to read the puml file and create an svg with just the ICameraSetting interface plus anything that interacts with or depends on it.

I know I can generate an interactive svg and click on the ICameraSetting component, but that just dims the other components instead of removing them from the view, so it doesn't make the svg any easier to work with.


Solution

  • I still can't find a direct way to do this, but I managed to get something equivalent by editing the svg to add a little extra functionality. Clicking an element now adds the "removed" class to anything that isn't selected to apply a display:none style, then it transforms all selected elements so they are as close to top-left as possible, and resizes the svg to fit just the selected elements.

    Here's the code. I haven't included some of the boilerplate stuff or the elements, but it shouldn't be too difficult to add this to any interactive svg generated using PlantUML.

    <?xml version="1.0" encoding="us-ascii" standalone="no"?>
    <svg id="svgElement" ... >
        <defs id="defsElement">
            <style type="text/css"><![CDATA[
                .elem.removed, .link.removed {display: none;}
            ]]></style>
            ...
            <script>
                ...
                function main() {
    
                    let focusedSelect = true;
                    let svgEl = document.getElementById('svgElement');
                    let rootWidth = svgEl.style.width;
                    let rootHeight = svgEl.style.height;
                    var transformStyle = document.createElementNS("http://www.w3.org/2000/svg", 'style');
                    transformStyle.type = 'text/css';
                    document.getElementById('defsElement').appendChild(transformStyle);
    
                    ...
    
                    function clearSelected() {
                        selectedElems.forEach(item =&gt; {
                            item.classList.remove('selected');
                            if (focusedSelect) { item.classList.add('removed'); }
                        });
                        selectedElems = [];
    
                        selectedLinks.forEach(item =&gt; {
                            item.classList.remove('selected');
                            if (focusedSelect) { item.classList.add('removed'); }
                        });
                        selectedLinks = [];
                    }
    
                    function selectAll() {
                        svgEl.style.width = rootWidth;
                        svgEl.style.height = rootHeight;
                        svgEl.setAttribute('width', rootWidth);
                        svgEl.setAttribute('height', rootHeight);
                        svgEl.setAttribute('viewBox', '0 0 ' + parseInt(rootWidth) + ' ' + parseInt(rootHeight));
                        transformStyle.textContent = '';
    
                        selectedElemName = null;
    
                        selectedElems = Array.from(elems);
                        selectedElems.forEach(item =&gt; {
                            item.classList.add('selected');
                            item.classList.remove('removed');
                        });
    
                        selectedLinks = Array.from(links);
                        selectedLinks.forEach(item =&gt; {
                            item.classList.add('selected');
                            item.classList.remove('removed');
                        });
                    }
    
                    function selectElem(elemName) {
                        if (elemName === selectedElemName) {
                            selectAll();
                        } else {
                            clearSelected();
                            selectedElemName = elemName;
                    
                            elemsMap[elemName].classList.add('selected');
                            elemsMap[elemName].classList.remove('removed');
                            selectedElems.push(elemsMap[elemName]);
                    
                            linkedElems[elemName].forEach(linkedElem =&gt; {
                                selectedElems.push(linkedElem);
                                linkedElem.classList.remove('removed');
                                linkedElem.classList.add('selected');
                            });
                    
                            linkedLinks[elemName].forEach(linkedLink =&gt; {
                                selectedLinks.push(linkedLink);
                                linkedLink.classList.remove('removed');
                                linkedLink.classList.add('selected');
                            });
                    
                            if (focusedSelect) {
                                window.requestAnimationFrame(function () {
                                    let selectedRect = elemsMap[elemName].getBoundingClientRect();
                                    let leftmost = { x: selectedRect.left + window.scrollX, y: selectedRect.top + window.scrollY };
                                    let bottommost = { x: selectedRect.right + window.scrollX, y: selectedRect.bottom + window.scrollY };
                        
                                    linkedElems[elemName].forEach(linkedElem =&gt; {
                                        let rect = linkedElem.getBoundingClientRect();
                                        if (rect.left + window.scrollX &lt; leftmost.x) { leftmost.x = rect.left + window.scrollX; }
                                        if (rect.top + window.scrollY &lt; leftmost.y) { leftmost.y = rect.top + window.scrollY; }
                                        if (rect.right + window.scrollX &gt; bottommost.x) { bottommost.x = rect.right + window.scrollX; }
                                        if (rect.bottom + window.scrollY &gt; bottommost.y) { bottommost.y = rect.bottom + window.scrollY; }
                                    });
                        
                                    linkedLinks[elemName].forEach(linkedLink =&gt; {
                                        let rect = linkedLink.getBoundingClientRect();
                                        if (rect.left + window.scrollX &lt; leftmost.x) { leftmost.x = rect.left + window.scrollX; }
                                        if (rect.top + window.scrollY &lt; leftmost.y) { leftmost.y = rect.top + window.scrollY; }
                                        if (rect.right + window.scrollX &gt; bottommost.x) { bottommost.x = rect.right + window.scrollX; }
                                        if (rect.bottom + window.scrollY &gt; bottommost.y) { bottommost.y = rect.bottom + window.scrollY; }
                                    });
                        
                                    let rootRect = svgEl.getBoundingClientRect();
                                    let offset = { x: leftmost.x - (rootRect.left + window.scrollX), y: leftmost.y - (rootRect.top + window.scrollY) };
                                    let newSize = { x: bottommost.x - leftmost.x, y: bottommost.y - leftmost.y };
                        
                                    for (let i = 0; i &lt; transformStyle.sheet.cssRules.length; i++) { transformStyle.sheet.removeRule(i); }
                                    transformStyle.sheet.insertRule('.elem.selected, .link.selected { transform: translate(-' + offset.x + 'px, -' + offset.y + 'px); }', 0);
                                    svgEl.style.width = newSize.x + 'px';
                                    svgEl.style.height = newSize.y + 'px';
                                    svgEl.setAttribute('width', newSize.x + 'px');
                                    svgEl.setAttribute('height', newSize.y + 'px');
                                    svgEl.setAttribute('viewBox', '0 0 ' + newSize.x + ' ' + newSize.y);
                                    window.scrollTo(0,0);
                        
                                    window.requestAnimationFrame(function() {
                                        let selectedRectFinal = elemsMap[elemName].getBoundingClientRect();
                                        window.scrollTo(selectedRectFinal.x, selectedRectFinal.y);
                                    });
                                });
                            }
                        }
                        
                    }
    
                }
    
                ...
    
            </script>
        </defs>
        ...
    </svg>