-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
79a6387
commit c0623e1
Showing
66 changed files
with
15,229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,10 @@ | |
/tmp | ||
/out-tsc | ||
|
||
# Static web output | ||
/build | ||
.docusaurus | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"exclude": ["node_modules", "docs", "website"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Dependencies | ||
/node_modules | ||
|
||
# Production | ||
/build | ||
|
||
# Generated files | ||
.docusaurus | ||
.cache-loader | ||
|
||
# Misc | ||
.DS_Store | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
presets: [require.resolve("@docusaurus/core/lib/babel/preset")], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
--- | ||
title: Inputs to the draw function | ||
--- | ||
|
||
# Structure of `objects` argument in `draw` function | ||
|
||
Arguably the most significant parameter the user has to specify for the `draw` function is `objects`, representing | ||
the array of objects to be drawn. | ||
|
||
To be successfully rendered, the array must contain objects that strictly follow a specific structure. Every object | ||
must contain the following attributes: | ||
|
||
- `isClass` - `boolean`: denotes whether the object to be drawn is a user-defined class (or a stack-frame) or a built-in | ||
object. Pass true to draw a class or a stack-frame, and false to draw any built-in types. | ||
- `name` - `string`: denotes the type of the object to draw (if `isClass===true`, then this is the name of the | ||
corresponding class or stackframe). | ||
- If the user want to hardcode the coordinates (implying the `automation` parameter of `draw` is false), each object | ||
must include `x` and `y` attributes (for x-y coordinates). | ||
- `id` - `string`|`number`: denotes the id value of this object. If we are to draw a StackFrame, then this MUST be `null`. | ||
- `value` - `*`: denotes the value of the object. This could be anything, from an empty string to a JS object, | ||
which would be passed for the purpose of drawing a user-defined class object, a | ||
stackframe, or a dictionary. | ||
**Note that in such cases where we want to do draw a 'container' | ||
object (an object that contains other objects), we pass a _JS object_ where the keys are the | ||
attributes/variables and the values are the id's of the corresponding objects (not the | ||
objects themselves)**. | ||
- `stack_frame` - `boolean`: denotes whether a stack frame will be drawn or not. NOTE that this is only | ||
applicable if the object's `isClass` attribute is true (since the | ||
`MemoryModel.drawClass` covers both classes and stack-frames). By default, | ||
`stack_frame` is set to null (_which is false_). | ||
- `show_indexes` - `boolean`: This is applicable only when drawing tuples or lists (when drawSequence | ||
method will be used). It denotes whether the memory box of the underlying | ||
sequence will include indices (for sequences) or not. This | ||
has a default value of `false`, and it should be manually set to `true` | ||
only if the object corresponds to a sequence (list or | ||
tuple). | ||
- `style` - `object` | `array`: a JS object or array specifying the "style" of the object. See `style.md` for information | ||
on the required structure (also see `presets.md` for the full capabilities). | ||
|
||
### Examples | ||
|
||
```javascript | ||
{"isClass": true, "name": "__main__", "id": null, "value": {"lst1": 82, "lst2": 84, "p": 99, "d": 10, "t": 11}, "stack_frame": true}, | ||
{"name": "BLANK", "width": 100, "height": 200, "stack_frame" : true}, | ||
{"isClass": true, "name": "func", "id": null, "value": {"age": 12, "name": 17}, "stack_frame": true}, | ||
|
||
{"name": "BLANK", "width": 100, "height": 200, "stack_frame" : true} | ||
|
||
{"isClass": false, "name": "list", "id": 82, "value": [19, 43, 28, 49]} | ||
|
||
{"isClass": false, "name": "list", "id": 84, "value": [32, 10, 90, 57], "show_indexes": true} | ||
|
||
{"isClass": false, "name": "int", "id": 19, "value": 1969} | ||
|
||
{"isClass": false, "name": "bool", "id": 32, "value": true} | ||
|
||
{"name": "BLANK", "width": 200, "height": 100} | ||
|
||
{"isClass": false, "name": "str", "id": 43, "value": "David is cool"} | ||
|
||
{"isClass": false, "name": "tuple", "id": 11, "value": [82, 76]} | ||
|
||
{"isClass": false, "name": "set", "id": 90, "value": [36, 49, 64]} | ||
|
||
{"isClass": false, "name": "dict", "id": 10, "value": {"x": 81, "y": 100, "z": 121}} | ||
|
||
{"isClass": false, "name": "None", "id": 13, "value": "None", | ||
"style": { | ||
"text_value" : {"font-style": "italic"}, | ||
"box_id": {"fill": "red", "fillStyle": "dots"}, | ||
"box_type": {"fill": "red", "fillStyle": "solid"}, | ||
"box_container": {"fill":"black", "fillStyle": "solid"}} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
--- | ||
title: Automatic Layout | ||
--- | ||
|
||
# Automatic Layout | ||
|
||
## Discussion | ||
|
||
How does the automation of `drawAutomatedOtherItems` and `drawAutomatedStackFrames`, | ||
responsible for drawing all non-stackframe objects actually work? The algorithm | ||
is outlined below. | ||
|
||
Given a list of objects in the format described in `MemoryModel.drawAll` | ||
but without `x` and `y` coordinates as properties (as the point of | ||
`drawAutomatedOtherItems` and `drawAutomatedStackFrames` is to assign | ||
coordinates to each object) we separate these given objects into to collections as _stack frames_ and _other items_ | ||
by using the `seperateObjects` function. We pass these two lists to the functions aforementioned above. | ||
|
||
We equip the objects with their hypothetical | ||
dimensions, we sort them by height, and then begin equipping | ||
them with actual coordinates (of the top-left corner), | ||
dynamically moving to a new row and continuing if necessary. | ||
|
||
In particular, all stackframe object are passed to `drawAutomatedStackFrames`, which not only equips the objects | ||
the coordinates, but importantly also returns the total width of the stack-frame "section" in the canvas, which | ||
is then passed to `drawAutomatedOtherItems` as a way to tell it where the vertical division | ||
between the two sections takes place (in this way, the divider between the stack-frame section | ||
and the objects section is determined dynamically). | ||
|
||
Below we thoroughly describe the steps for each of the two functions: | ||
`drawAutomatedOtherItems` and `drawAutomatedStackFrames`. | ||
|
||
## `drawAutomatedStackFrames` | ||
|
||
1. First we make sure that no configuration options (padding etc.) | ||
remain undefined. To achieve this, we loop through the configurations | ||
attributes and check whether they have already been defined by the user. If not, | ||
we pass in the default configuration settings. | ||
|
||
2. Then, we initialize the accumulators which helps us to track minimum required canvas height, | ||
required width for drawing all stack frames and a collection of stack frames that will be drawn. | ||
|
||
3. Afterward, we iterate through the given stack-frames objects. Inside the loop, | ||
we first acquire the width and the height of the object. If the box is "BLANK", we obtain the height and width | ||
by accessing the attributes of the object. If not, we obtain the dimensions by using the `getSize` function. Refer | ||
to the function for details. | ||
|
||
4. Moreover, we update the width accumulator, if the box width is greater than the size stored in the accumulator. | ||
|
||
5. After that, we add the `x` and `y` attributes to the stack frames object (if it's not a BLANK box), and add the object | ||
the collection accumulator. | ||
|
||
6. At the end, we update the min required height by adding the height of the box to the accumulator and return | ||
the object. After the loop, we update the accumulator in accordance with the `configuration.padding`. | ||
|
||
7. At the end, we return a JS object that has the following format | ||
`{StackFrames: ..., requiredHeight: ..., requiredWidth: ...}` | ||
|
||
## `drawAutomatedOtherItems` | ||
|
||
1. Determine the size --width and height-- of the (outer) box of each object if it were to | ||
be drawn on canvas, and **equip the object with its calculated dimensions, by adding width and height properties**. This will tell us exactly how much space each | ||
object will require in the canvas. For this purpose I created the `getSize` function | ||
in the `automate.js` module, which accepts any object and returns the | ||
size it would have on canvas.\* | ||
|
||
2. Sort the list of objects by descending height of the objects | ||
(the height and weight properties were added to each object in | ||
STEP 1). The purpose of this step is to economize on the height | ||
of the canvas; **all “tall” boxes ought to be together in the first | ||
(few) “rows” of the canvas, so not all “rows” need to have a | ||
large vertical value**. This sorting allows us to do this because | ||
once you place an object in a new row, you know all remaining objects | ||
will be shorter that this object. | ||
|
||
3. Loop through the passed list of objects (which now is equipped | ||
with dimensions, and is sorted by descending height), and let the | ||
current object be denoted as `obj`: | ||
|
||
- If `obj` fits horizontally (which is determined by checking | ||
if the top-right coordinate of the last iteration's object, | ||
plus some padding, plus the width of `obj` is less than the | ||
width of the entire canvas), **equip `obj` with the (top-left) | ||
coordinates** it would take if it were drawn. | ||
- If `obj` does NOT fit horizontally, we move to a new row. | ||
The height of the new row will be the height of the current | ||
object that does not fit (plus some padding), `obj`, since (due to the sorting | ||
in step 2) `obj` is tallest amongst all remaining objects | ||
(so it just makes sense to the make this the height of | ||
the row itself). | ||
Again, **equip `obj` with the (top-left) coordinates** it | ||
would take if it were drawn (in this case, the x-coordinate | ||
would go back to the initial state since we moved to a new row, | ||
and the y-coordinate would increase by the height of the | ||
_previous_ row). | ||
|
||
4. Return the mutated list of objects. | ||
|
||
\*In case the object has `name="BLANK"`, it is assumed that the user has | ||
also provided a width and a height attribute corresponding to the desired | ||
width and height of the blank space. If such dimensions have not been provided, | ||
a related warning is printed and the object is skipped (no blank space is | ||
recorded). | ||
|
||
## Summary | ||
|
||
As a result, the caller of the function (may that be an actual user | ||
or another function) has a list of objects equipped with coordinates, | ||
and can use `drawAll` (assuming the existence of a MemoryModel object) | ||
to actually draw them. | ||
|
||
Thus, **the process of determining coordinates is now fully automated**. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
--- | ||
title: Style API | ||
--- | ||
|
||
# Style API | ||
|
||
A user is able to add custom styling to an object | ||
(of the list of objects to be drawn on canvas) by | ||
adding an attribute named `style` which maps to an object of the | ||
following format: | ||
|
||
```javascript | ||
style : { | ||
"text_id" : {}, | ||
"text_type" : {}, | ||
"text_value" : {}, | ||
"box_id" : {}, | ||
"box_type" : {}, | ||
"box_container" : {}, | ||
} | ||
``` | ||
|
||
The object has up to six attributes (the ones observed above), each | ||
corresponding to a particular component of an object with the potential | ||
to be styled. | ||
|
||
Also, the user can pass in an array for the style attribute. This array can be | ||
a mixture of presents (name of the preset in the string data format) and a user-defined | ||
Object. To better illustrate this, here is an example: | ||
|
||
```javascript | ||
["highlight", "hide_type", { text_id: { "font-style": "italic" } }]; | ||
``` | ||
|
||
Crucially, each of the attributes in the `style` object (if user passes in an Object) | ||
refer themselves to another object: | ||
|
||
- Text-related attributes can contain any attribute specified in the | ||
documentation of the text element for svg graphics, found on | ||
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text. | ||
- Box-related attributes can contain any styling attributes specified in the | ||
documentation of the rough library, found on https://roughjs.com/ and | ||
https://github.com/rough-stuff/rough/wiki. | ||
|
||
## Insights for the Implementation | ||
|
||
The user specifies a desired style for each of the objects in the list. | ||
|
||
### Merging | ||
|
||
First, there is a default object that contains all key-value pairs of style attributes | ||
that (be default) are common across objects of all types. This object is called `common_style`. | ||
|
||
Then we use a list objects to represent some styling properties specific to certain type categories | ||
(for instance, `text_value` for collections need to have the id color by default, which | ||
is not true for primitive types). The `deepmerge` library is used to merge the common styles | ||
mentioned above with these category-specific styles. | ||
|
||
Finally, any user-supplied styling attributes are merged to the current style object. | ||
This is the second and final merge. | ||
|
||
### Cascade | ||
|
||
The above "merging" procedure takes place inside the `drawAll` method (using the | ||
`populateDefaultStyle` helper function). | ||
|
||
Then, inside the `drawClass` and `drawObject` methods (which are called from within | ||
`drawALl`), for every invocation of `drawText` and `drawRect` (responsible for drawing | ||
text and boxes, respectively), the corresponding attribute of the object's style | ||
object is passed (for instance, in the case where `drawText` is called to draw the id value | ||
inside the id box, `style.text_id` is passed as an argument to `drawText`). | ||
|
||
Finally, inside drawText and drawRect, we loop through all the attributes of the | ||
passed style object, and apply each of those in the appropriate manner ( | ||
for drawRect, you simply pass the style when initializing a rectangle by doing | ||
`this.rough_svg.rectangle(x, y, width, height, style)`, and with the text element you | ||
use the DOM method `setAttribute` by doing `newElement.setAttribute(style_attribute, style[style_attribute])` | ||
). | ||
|
||
Thus, the style is cascaded down from user to drawText/drawRect as follows: | ||
|
||
user-defined style --> drawAll --> drawClass/drawObject --> drawText/drawRect | ||
|
||
## Examples | ||
|
||
### Example 1 | ||
|
||
`style: | ||
{"text_value" : {"font-style" : "italic"}, | ||
'box_id': {fill: 'blue', fillStyle: "dots"}} | ||
` | ||
In this example, it is important to note that for box styles | ||
(the ones which uses rough.js module), `fill` must be passed in if | ||
fillStyle argument will be passed in by the user. Otherwise, the behavior of the style will be in the default format | ||
(refer to the rough.js module for the default style). Also, it points outs that the user has the | ||
flexibility for defining the styles related to both boxes and text drawn on the canvas. | ||
|
||
### Example 2 | ||
|
||
`style: { | ||
"box_container": {fill:"green", fillStyle: "zigzag"}, | ||
"box_id": {fill: 'red', fillWeight: 3}, | ||
"box_type": {bowing: 8, stroke: "green", strokeWidth: 3}}` | ||
|
||
This example illustrates that our implementation for box styles are defined on | ||
three boxes: the container, the box that represents the id and the box that represents | ||
the data type. Therefore, the user can utilize the rough.js module for these three boxes that | ||
are drawn on the canvas (two for stack-frames, as they do not have ids associated with them) | ||
|
||
### Example 3 | ||
|
||
`style:{ | ||
"text_id" : {fill : "yellow", stroke: "green"}, | ||
"text_value": {"font-style" : "italic"}, | ||
"text_type": {"font-size: "xx-large"}}` | ||
|
||
This example shows that for each container (for each data that is represented on the canvas) | ||
there are the texts that the user can alter its style: the text representing the value and/or values, | ||
the text representing the id number of the data type (if it's not a stack-frame) and the text representing | ||
the type of the data. The user can pass in configurations based on the SVG documentation for modifying these texts | ||
simultaneously. |
Oops, something went wrong.