GSoC 2024 with Sugar Labs | Week 5 & 6 | Music Blocks (v4) project updates
Hi everyone, welcome to the GSoC’24 Blogs.
I am documenting my complete journey as a contributor for Google Summer of Code 2024 under Sugar Labs for the Music Blocks (v4) project – Masonry Framework.
To get more information about the project, you can read all my blogs here.
My last blog mentioned the Dummy stack and the workspace #409.
In this blog, I would like to share what I have done in the last 2 weeks and project updates.
Implementing the Stack Tree Structure
To manage the hierarchical arrangement of bricks, I designed and implemented a stack tree structure. Here’s a breakdown of the key elements in the code(#410) and their functionalities:
Code Explanation
- Interfaces and Classes:
 
The code defines interfaces (IStackNode and IStack) and classes (StackNode and Stack) to represent the hierarchical stack structure of bricks.
/**
 * @interface IStackNode
 * Represents a node in the stack structure.
 */
export interface IStackNode {
    /** The brick model associated with this node */
    brick: BrickModelData | BrickModelExpression | BrickModelStatement | BrickModelBlock;
    /** Child nodes of this node */
    children: IStackNode[];
}
/**
 * @interface IStack
 * Represents the stack structure for managing bricks.
 */
export interface IStack {
    /** Unique identifier for the stack */
    id: string;
    /** Root nodes of the stack */
    rootNodes: IStackNode[];
    /** Methods for stack operations */
    validate(): boolean;
    addNode(node: IStackNode, parentId?: string): void;
    removeNode(id: string): void;
    moveNode(id: string, newParentId: string, newIndex: number): void;
    collapse(id: string): void;
    expand(id: string): void;
    getValidationErrors(): string[];
}
- StackNode Class:
 
StackNode represents a node in the stack, holding a reference to a brick and its children.
/**
 * @class StackNode
 * Implements the IStackNode interface.
 */
class StackNode implements IStackNode {
    brick: BrickModelData | BrickModelExpression | BrickModelStatement | BrickModelBlock;
    children: IStackNode[];
    /**
     * Creates a new StackNode.
     * @param {BrickModelData | BrickModelExpression | BrickModelStatement | BrickModelBlock} brick - The brick model for this node.
     */
    constructor(
        brick: BrickModelData | BrickModelExpression | BrickModelStatement | BrickModelBlock,
    ) {
        this.brick = brick;
        this.children = [];
    }
}
- Stack Class:
 
Stack manages a collection of StackNodes, providing methods to add, remove, move, collapse, expand nodes, and validate the stack structure.
/**
 * @class Stack
 * Implements the IStack interface.
 */
class Stack implements IStack {
    id: string;
    rootNodes: IStackNode[];
    private _validationDisabled = false;
    /**
     * Creates a new Stack.
     * @param {string} id - The unique identifier for this stack.
     */
    constructor(id: string) {
        this.id = id;
        this.rootNodes = [];
    }
    validate(): boolean {
        if (this._validationDisabled) return true;
        return this.getValidationErrors().length === 0;
    }
- Adding a Node:
 
The addNode method allows adding a new node to the stack.
    addNode(node: IStackNode, parentId?: string): void {
        if (!parentId) {
            this.rootNodes.push(node);
        } else {
            const parent = this.findNode(parentId);
            if (parent && (parent.brick instanceof BrickModelBlock || parent.brick instanceof BrickModelExpression)) {
                parent.children.push(node);
                this.updateNestExtent(parent);
            } else {
                throw new Error('Parent node not found or cannot have children');
            }
        }
    }
- Removing a Node:
 
The removeNode method removes a node from the stack by its ID.
    removeNode(id: string): void {
        const remove = (nodes: IStackNode[]): boolean => {
            for (let i = 0; i < nodes.length; i++) {
                if (nodes[i].brick.uuid === id) {
                    nodes.splice(i, 1);
                    return true;
                }
                if (nodes[i].children.length > 0 && remove(nodes[i].children)) {
                    this.updateNestExtent(nodes[i]);
                    return true;
                }
            }
            return false;
        };
        remove(this.rootNodes);
    }
- Moving a Node:
 
The moveNode method moves a node to a new position in the stack.
    moveNode(id: string, newParentId: string, newIndex: number): void {
        const node = this.findNode(id);
        if (!node) throw new Error('Node not found');
        this.removeNode(id);
        const newParent = this.findNode(newParentId);
        if (!newParent) throw new Error('New parent node not found');
        if (!(newParent.brick instanceof BrickModelBlock) && !(newParent.brick instanceof BrickModelExpression)) {
            throw new Error('New parent cannot have children');
        }
        newParent.children.splice(newIndex, 0, node);
        this.updateNestExtent(newParent);
    }
- Collapsing and Expanding a Node:
 
The collapse and expand methods allow collapsing and expanding block nodes. ( I will update these to new names decided : fold and unfold.)
    collapse(id: string): void {
        const node = this.findNode(id);
        if (node && node.brick instanceof BrickModelBlock) {
            node.brick.collapsed = true;
            this.updateNestExtent(node);
        }
    }
    expand(id: string): void {
        const node = this.findNode(id);
        if (node && node.brick instanceof BrickModelBlock) {
            node.brick.collapsed = false;
            this.updateNestExtent(node);
        }
    }
- Validation Errors:
 
The getValidationErrors method returns an array of validation error messages.
Work done:
- Tree Structure Implementation: Successfully implemented a tree structure to manage the hierarchical arrangement of bricks in the stack.
 - Validation Logic: Implemented validation logic to ensure the integrity of connections and data types between bricks.
 - Stack Rendering: Rendered a stack of bricks by taking reference from #361.
 
Next Steps
- Implementing the bricks: Focus on implementing the bricks and completing the brick library and features (ex. argument/label in bricks etc)
 
Conclusion
These two weeks have been productive, and I am excited about the progress made on the stack tree structure. Stay tuned for more updates as I continue working on enhancing Music Blocks v4.
Also, don’t forget to drop a star on the project GitHub repo! ⭐
Link: https://github.com/sugarlabs/musicblocks-v4
You can connect with me on LinkedIn. Additionally, you can follow me on GitHub and Twitter, it will be appreciated.
Thank you for reading!
	




