Microsoft MakeCode

Using arrays in the API - being passed in as RefCollections?

I am following the directions here

image

//% block="draw 3d lines with width $width|height $height|closed $closed|from 2d points $points"
//% width.defl=10
//% height.defl=10
//% closed.defl=true
//% points.defl="inner_shadow_block"
//% linePath.shadow="lists_create_with"
//% advanced=true
//% group="Advanced 3D Shapes"
 export function drawLinePath(width: number, height: number, closed: boolean, points: string[]): void 
{
  // points is actually a RefCollection
  const pointsArrayStr = points["data"].toString()  
 }

And the problem is the points array is coming in as a RefCollection, which I have to pick off the “data” property to actually get the contents.

Typescript is freaking out though. What’s the correct type for points?

Typescript error

   [run] cd sim; node ../node_modules/typescript/bin/tsc
api.ts(109,39): error TS7015: Element implicitly has an 'any' type because index expression is not of 
type 'number'.

***
*** Build failed: target build failed: Exit code: 2 from cd sim; node ../node_modules/typescript/bin/tsc
Error: Exit code: 2 from cd sim; node ../node_modules/typescript/bin/tsc
at ChildProcess.<anonymous> ( /myfolder/makecode/node_modules/pxt-core/built/nodeutil.js:85:24)
at ChildProcess.emit (events.js:310:20)
at maybeClose (internal/child_process.js:1021:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5)
***

Ah, you are defining your code in the simulator, right? Basically there are two “types” of code that make up the simulator for MakeCode:

  1. Library code - this is in the libs/ folder and is compiled using the MakeCode compiler. Code in this folder will also work on hardware
  2. Simulator code - this is in the sim/ folder and does not go through the MakeCode compiler. This code will only work in the browser (not on hardware)

The library code is not normal JavaScript; it gets compiled by the MakeCode compiler. The compiler spits out a “binary.js” file that runs in the browser to simulate the code. You can actually look at that file by going into the JavaScript view of a project and opening the file explorer below the simulator (it should be in the built/ section). This binary file uses different types than normal JS code that simulate the environment our code runs in on hardware. RefCollection is one of those types.

When you write simulator code, primitive types (strings, numbers, booleans) will all get passed straight down to the simulator code. More complex types are passed using the internal runtime types from MakeCode. These are RefObject, RefCollection, and RefAction; You can look at where they are defined here. However, it’s usually best to avoid using them altogether.

You should write as much code as possible inside the libs/ folder of your target. All of this code goes through the MakeCode compiler so there is no need to worry about any of the internal types. The only code that should go in the sim folder is code that needs to access browser APIs or is mocking out C++ code. On the MakeCode team, we typically follow these two rules:

  1. If the code defines a block, it goes in the libs/ folder
  2. Whenever possible, code in the sim/ folder should only take primitive types as arguments

If you need to pass a complex type to the simulator, you can usually break it down into a primitive type and then pass that to the simulator. For example, looks like you are converting that array of points into a string. You could structure your code like this:

In libs/myPackage/drawLine.ts

//% block="draw 3d lines with width $width|height $height|closed $closed|from 2d points $points"
//% width.defl=10
//% height.defl=10
//% closed.defl=true
//% points.defl="inner_shadow_block"
//% linePath.shadow="lists_create_with"
//% advanced=true
//% group="Advanced 3D Shapes"
 export function drawLinePath(width: number, height: number, closed: boolean, points: string[]): void 
{
  const pointsArrayStr = points.join(",");
  _drawLineCore(width, height, closed, pointsArrayStr);
 }

In sim/drawLine.ts:

//% 
 export function _drawLineCore(width: number, height: number, closed: boolean, points: string): void 
{
   // draw the line
 }

In this example, adding the //% above the function makes sure that it is usable from the libs/ directory. Adding an underscore to the beginning of the function names will make sure that the function is filtered out of intellisense, so you’ll be able to use it but users will never see it.

I hope that clears things up a little!

2 Likes

Legend! You’d suggested factoring it all out before. I will make the time to do this now. I have a theory this may also be why my color and icon //% are not being honoured either.

The way it’s structured is based off the pxt-sample repo - I just kept changing it until something came out.

JFo

We really need to update the pxt-sample repo to follow best practices. Thanks for the feedback!