sugarlabs / musicblocks-v4

A complete overhaul of Music Blocks
http://www.sugarlabs.org/music-blocks/
GNU Affero General Public License v3.0
77 stars 106 forks source link

Project Builder Framework #55

Closed ksraj123 closed 3 years ago

ksraj123 commented 3 years ago

Architecture

Untitled

Models for Blocks

Model for one block

Model for Stacks of Blocks

An array of isolated block stack entities such as top level blocks like start, action, define temperament, widget blocks etc or just random group of blocks.

meganindya commented 3 years ago

Looks good. Try to list the variables that the model and view classes need to maintain, methods in each to communicate certain things with the other, and methods the block component as a whole needs in order to communicate (bidirectionally) with the monitor.

You can follow the component pattern for handling syntax as in the engine. Basically, you'd have one component that deals with one individual block of any of the available block types supported (syntaxElement in engine, Block in MB v3), another component that deals with the arrangement of several blocks (AST in engine, Blocks in MB v3), and some helper components like syntaxElementFactory in engine which deals with instantiating new individual blocks, and syntaxHandler in engine which deals with supervising and bookkeeping. The monitor only talks to the supervisor component, the rest are private to the component as a whole and are handled internally.

All these together will comprise the Block Framework. The engine isn't concerned with how the blocks look or where they are positioned but handles intricate challenges regarding the attachment-detachment of blocks. The prior are your client-side framework objectives, the latter are engine-side framework objectives.

walterbender commented 3 years ago

A few things:

In the block Backend Model, we will want to have default values for the block and default arguments for any block arguments. We also want to store the generic labels for the block, which would be translated and rendered in the Viewer. The Client Model will hold the current value of a block; how that is rendered is still up to the Viewer.

In MB 3, the value stored in a block is also used to reconstruct the overall MB environment when a project is loaded, e.g., the position, heading, pen attributes, etc. of a turtle are saved in the corresponding Start block. Image and Sample blocks can end up with very unwieldy payloads.

We may want to revisit this in terms of how we save/restore state (but we will want some level of backward compatibility).

The position of the block would be in the Viewer.

I am on the fence regarding the block connections: in MB 3, the connections are in the individual blocks; Not sure that makes sense. That could all live in the Blocks Model.

ksraj123 commented 3 years ago

Backend Model for Different Types of Blocks

Value Blocks

Value blocks connect as arguments to other blocks, no block connects to top or bottom of value blocks.

category: // One of Value, Flow, Clamp etc
type: // unique for every block type, ex - heading, number, rightpos, width, text, solfege etc 
title: // a title to be displayed in the block body
helpString: 
tooltip: // displayed when hovered over the blocks
color: // color of the block
output: // present only for Value and Left Blocks which connect as arguments to other blocks,
        // denotes what kind of value they return number, solfege, string, boolean etc.
defaultValue:
click: // menu | input | null - what happens on clicking on the value block, clicking on solfege block makes a menu appear, clicking on text or number box lets us type the input
menuOptions: // if this key is present and not null then clicking on the block makes a menu appear options for which are specified here, view handles exactly what kind of menu to show

Examples -

category: "value",
type: "solfege",
title: null,
helpString: "Pitch can be specified in terms of do re mi fa sol la ti.",
tooltip: "Solfege",
colour: "#7cd622",
output: "solfege",
defaultValue: "sol",
click: "menu",
menuOptions: [
    ["do", "re", "mi", "fa", "sol", "la", "ti"],
    [
        {tooltip: "double sharp", label: DOUBLESHARP},
        {tooltip: "sharp", label: SHARP},
        {tooltip: "natural", label: NATURAL},
        {tooltip: "flat", label: FLAT},
        {tooltip: "double flat", label: DOUBLEFLAT}
    ],
    ["8", "7", "6", "5", "4", "3", "2", "1"]
]

Screenshot 2021-06-09 at 2.14.40 PM.png

category: "value",
type: "heading",
title: "heading",
helpString: "The Heading block returns the orientation of the mouse.",
tooltip: "Heading",
colour: "#92a9ff",
output: "number",
defaultValue: null,
click: null

Screenshot 2021-06-09 at 2.30.03 PM.png

category: "value",
type: "number",
title: null,
helpString: "The Number block holds a number.",
tooltip: "Number",
colour: "#fe6ea1",
output: "number",
defaultValue: 0,
click: "input"

Left Blocks

Left block take value blocks or other left blocks as arguments and give an output to the block they are connected to. No other blocks could be attached to top and bottom of left blocks.

category: 
type: 
title: 
helpString: 
tooltip: 
color: 
output: 
args: [ // an aray of arguments, one element for every argument exepected
    {
      types: // an array containing types of value blocks that can be attached or any
      input: // type of input expected? number, solfege, any
      name:  // name using which this argument would be reffered to
      title:   // title for the arg to be displayed in block's body
      constraints: // additioanl constratins like minValue, maxValue or one of could be applied on arg depending upon the situation
    }
]

Examples -

Screenshot 2021-06-09 at 3.18.41 PM.png

Any block that returns a number could be connected to the random block.

category: "left",
type: "random",
title: "random",
helpString: "The Random block returns a random number.",
tooltip: "Random",
color: "#fe6ea1",
output: "number",
args: [
    {
      types: "any",
      input: "number",
      "name":  "min",
      title:   "min",
      constraints: null
    },
    {
      types: "any",
      input: "number",
      "name":  "max",
      title:   "max",
      constraints: null
    }
]

Screenshot 2021-06-09 at 3.30.25 PM.png

category: "left",
type: "division",
title: "/",
helpString: "The Divide block is used to divide.",
tooltip: "Division",
color: "#fe6ea1",
output: "number",
args: [
    {
      types: "any",
      input: "number",
      "name":  "dividend",
      title:   null,
      constraints: null
    },
    {
      types: "any",
      input: "number",
      "name":  "divisor",
      title:   null,
      constraints: {
             exclude: 0
          }
    }
]

Flow Blocks

Other Blocks could be connected to top and bottom of flow blocks, flow block may or may not take arguments.

category: 
type:
title: 
helpString: 
tooltip: 
color: 
args:
previousTypes: // an array containing types of block that could connect to top or any
nextTypes: // an array containing types of block tthat could connect to bottom or any

Screenshot 2021-06-09 at 3.25.33 PM.png

category: "flow",
type: "pitch",
title: "pitch",
helpString: "The Divide block is used to divide.",
tooltip: "Pitch",
color: "#7cd622",
args: [
    {
      types: ["solfege"],
      input: "solfege",
      "name":  "name",
      title:  "name",
      constraints: {
            oneOf: ["do", "re", "mi", "fa", "sol", "la", "ti"]
          }
    },
    {
      types: "any",
      input: "number",
      "name":  "octave",
      title:   "octave",
      constraints: {
             minValue: 1,
             minValue: 8
          }
    }
]
previousTypes: "any",
nextTypes: "any"

Clamp Blocks

Clamp blocks are blocks within which other blocks can be nested. Clamp Blocks may or may not take in arguments. We have two types of Clamp blocks in music block -

In the current version of music block, any Flow or FlowClamp block could be nested inside a Clamp Block. Below is an example of such absurd situation, inside a note block there should be one or more pitch blocks

Ideally there should be a way to prevent this from happening or at least warn the user about syntax issues.

category: 
type:
title: 
helpString: 
tooltip: 
color: 
args:
previousTypes:
nextTypes:
nestedTypes: // an array containing all the top level block types that could be nested inside the clamp block or any, if a block of any other type is nested then we will get a syntax warning

Example -

category: "clamp",
type: "note",
title:  "Note",
helpString: "The Note block is a container for one or more Pitch blocks. The Note block specifies the duration (note value) of its contents",
tooltip: "Note",
color: "#fe994f",
args: [
  {
     types: "any",
     input: "number",
     name:  "value",
     title:  "value",
     constraints: {
       minValue: 0       // duration of note can't be negative
     }
]
previousTypes: "any",
nextTypes: "any",
nestedTypes: ["pitch", "note"]
walterbender commented 3 years ago

This is a great start. A few more ideas re constraints:

We can support defaultValues, defaultTypes (e.g., int vs. float), defaultMin, defaulltMax

We may want to define type convertors too: shuld we try to convert ASCII 1 into int 1? Prob. yes for some blocks and no for others. It gets messy for the various representations of pitch.

Boxes are also problematic, but I think we decided to have different types of boxes for different types of content: shoe boxes, hat boxes, etc.

Where it gets a bit tricky is when we overload operators, such as using the plus block for arithmetic sums and string concatenation. We discussed using seperate blocks for that.

We'll ptob. break some backward vcompatibility with some of these decisions. C'est la vie.

liamnorm commented 3 years ago

This gets into graphics territory a bit, but I think it would be nice if arguments/children of a block could go inside their parent instead of attaching on the right like a node (where appropriate). This can make code more readable: for example, the division block would read [[1]/[4]], on one line. Plus, two-arg-in--one-arg-out left-blocks can cause segments of code to overlap if you don't use grey filler blocks.

I suppose the reason that this could affect how blocks are represented internally is the in-between labels. The arguments would still be functionally represented the same way, but there would need to be block labels that go between arguments. This representation of arguments would probably be best fit for simple operations like boolean logic, arithmetic, and concatenation. Using this style for other blocks like notes would sacrifice their argument labels.

ksraj123 commented 3 years ago

Sure, making blocks utilise the horizontal screen space in a more meaningful manner is on the list, perhaps we could have an entry on the backend model which signifies if a block takes in its arguments horizontally or vertically but this would mostly be handled by the view. Not sure if we should represent the filler blocks in our block stacks model as they are not a part of the execution, maybe the view would add them automatically if required. Thanks

meganindya commented 3 years ago

Btw, did you think of some generalised jargon for 'flow', 'clamp', 'turtle', etc.

meganindya commented 3 years ago

prototype built at sugarlabs/musicblocks-v4-builder-framework.