Bangle.js2: Add Unistroke object, and 'Bangle.stroke' event

This commit is contained in:
Gordon Williams 2021-11-03 16:54:53 +00:00
parent fc2e2b882b
commit 65a79b053a
10 changed files with 377 additions and 1008 deletions

View File

@ -76,6 +76,7 @@
Fix break scoping error in nested for loops (fix #2084)
Fix memory leak when rejecting pre-resolved promise (test_promise11.js)
Fix parser errors in arrow functions (fix #2067)
Bangle.js2: Add Unistroke object, and 'Bangle.stroke' event
2v10 : Bangle.js: Improved HRM calculations - swapped autocorrelation for bandpass filter
Bangle.js: Significantly improved step counting algorithm using bandpass filter (fix #1846)

View File

@ -64,6 +64,7 @@ info = {
'SOURCES += libs/misc/heartrate.c',
'SOURCES += libs/misc/hrm_vc31.c',
'SOURCES += libs/misc/unistroke.c',
'WRAPPERSOURCES += libs/misc/jswrap_unistroke.c',
'DEFINES += -DESPR_BANGLE_UNISTROKE=1',
'SOURCES += libs/banglejs/banglejs2_storage_default.c',
'DEFINES += -DESPR_STORAGE_INITIAL_CONTENTS=1', #

View File

@ -347,6 +347,37 @@ Emitted when the touchscreen is pressed
}
Emitted when the touchscreen is dragged or released
*/
/*JSON{
"type" : "event",
"class" : "Bangle",
"name" : "stroke",
"params" : [["event","JsVar","Object of form `{xy:Uint8Array([x1,y1,x2,y2...])}` containing touch coordinates"]],
"ifdef" : "BANGLEJS2"
}
Emitted when the touchscreen is dragged for a large enough distance to count as a gesture.
If Bangle.strokes is defined and populated with data from `Unistroke.new`, the `event` argument will also
contain a `stroke` field containing the most closely matching stroke name.
For example:
```
Bangle.strokes = {
up : Unistroke.new(new Uint8Array([57, 151, ... 158, 137])),
alpha : Unistroke.new(new Uint8Array([161, 55, ... 159, 161])),
};
Bangle.on('stroke',o=>{
print(o.stroke);
g.clear(1).drawPoly(o.xy);
});
// Might print something like
{
"xy": new Uint8Array([149, 50, ... 107, 136]),
"stroke": "alpha"
}
```
*/
#define ACCEL_HISTORY_LEN 50 ///< Number of samples of accelerometer history
@ -467,6 +498,14 @@ unsigned char touchX, touchY; ///< current touch event coordinates
unsigned char lastTouchX, lastTouchY; ///< last touch event coordinates - updated when JSBT_DRAG is fired
bool touchPts, lastTouchPts; ///< whether a fnger is currently touching or not
unsigned char touchType; ///< variable to differentiate press, long press, double press
typedef enum {
TG_SWIPE_NONE,
TG_SWIPE_LEFT,
TG_SWIPE_RIGHT,
TG_SWIPE_UP,
TG_SWIPE_DOWN,
} TouchGestureType;
TouchGestureType touchGesture; /// is JSBT_SWIPE is set, what happened?
#endif
#ifdef PRESSURE_I2C
@ -700,23 +739,22 @@ typedef enum {
JSBT_HRM_DATA = 1<<16, ///< Heart rate data is ready for analysis
JSBT_CHARGE_EVENT = 1<<17, ///< we need to fire a charging event
JSBT_STEP_EVENT = 1<<18, ///< we've detected a step via the pedometer
JSBT_SWIPE_LEFT = 1<<19, ///< swiped left over touchscreen
JSBT_SWIPE_RIGHT = 1<<20, ///< swiped right over touchscreen
JSBT_SWIPE_UP = 1<<21, ///< swiped left over touchscreen (Bangle 2 only)
JSBT_SWIPE_DOWN = 1<<22, ///< swiped right over touchscreen (Bangle 2 only)
JSBT_SWIPE_MASK = JSBT_SWIPE_LEFT | JSBT_SWIPE_RIGHT | JSBT_SWIPE_UP | JSBT_SWIPE_DOWN,
JSBT_TOUCH_LEFT = 1<<23, ///< touch lhs of touchscreen
JSBT_TOUCH_RIGHT = 1<<24, ///< touch rhs of touchscreen
JSBT_SWIPE = 1<<19, ///< swiped over touchscreen, info in touchGesture
JSBT_TOUCH_LEFT = 1<<20, ///< touch lhs of touchscreen
JSBT_TOUCH_RIGHT = 1<<21, ///< touch rhs of touchscreen
JSBT_TOUCH_MASK = JSBT_TOUCH_LEFT | JSBT_TOUCH_RIGHT,
#ifdef TOUCH_DEVICE
JSBT_DRAG = 1<<25,
JSBT_DRAG = 1<<22,
#endif
JSBT_TWIST_EVENT = 1<<26, ///< Watch was twisted
JSBT_FACE_UP = 1<<27, ///< Watch was turned face up/down (faceUp holds the actual state)
JSBT_ACCEL_INTERVAL_DEFAULT = 1<<28, ///< reschedule accelerometer poll handler to default speed
JSBT_ACCEL_INTERVAL_POWERSAVE = 1<<29, ///< reschedule accelerometer poll handler to powersave speed
JSBT_HRM_INSTANT_DATA = 1<<30, ///< Instant heart rate data
JSBT_HEALTH = 1<<31, ///< New 'health' event
#if ESPR_BANGLE_UNISTROKE
JSBT_STROKE = 1<<23, // a gesture has been made on the touchscreen
#endif
JSBT_TWIST_EVENT = 1<<24, ///< Watch was twisted
JSBT_FACE_UP = 1<<25, ///< Watch was turned face up/down (faceUp holds the actual state)
JSBT_ACCEL_INTERVAL_DEFAULT = 1<<26, ///< reschedule accelerometer poll handler to default speed
JSBT_ACCEL_INTERVAL_POWERSAVE = 1<<27, ///< reschedule accelerometer poll handler to powersave speed
JSBT_HRM_INSTANT_DATA = 1<<28, ///< Instant heart rate data
JSBT_HEALTH = 1<<29, ///< New 'health' event
} JsBangleTasks;
JsBangleTasks bangleTasks;
@ -800,34 +838,6 @@ void lcd_flip(JsVar *parent, bool all) {
#endif
}
static char clipi8(int x) {
if (x<-128) return -128;
if (x>127) return 127;
return (char)x;
}
static int twosComplement(int val, unsigned char bits) {
if (val & ((unsigned int)1 << (bits - 1)))
val -= (unsigned int)1 << bits;
return val;
}
// quick integer square root
// https://stackoverflow.com/questions/31117497/fastest-integer-square-root-in-the-least-amount-of-instructions
static unsigned short int int_sqrt32(unsigned int x) {
unsigned short int res=0;
unsigned short int add= 0x8000;
int i;
for(i=0;i<16;i++) {
unsigned short int temp=res | add;
unsigned int g2=temp*temp;
if (x>=g2)
res=temp;
add>>=1;
}
return res;
}
/// Clear the given health state back to defaults
static void healthStateClear(HealthState *health) {
memset(health, 0, sizeof(HealthState));
@ -1376,12 +1386,14 @@ bool btnTouchHandler() {
if ((touchLastState2==TS_RIGHT && touchLastState==TS_BOTH && state==TS_LEFT) ||
(touchLastState==TS_RIGHT && state==1)) {
touchStatus |= TS_SWIPED;
bangleTasks |= JSBT_SWIPE_LEFT;
touchGesture = TG_SWIPE_LEFT;
bangleTasks |= JSBT_SWIPE;
}
if ((touchLastState2==TS_LEFT && touchLastState==TS_BOTH && state==TS_RIGHT) ||
(touchLastState==TS_LEFT && state==TS_RIGHT)) {
touchStatus |= TS_SWIPED;
bangleTasks |= JSBT_SWIPE_RIGHT;
touchGesture = TG_SWIPE_RIGHT;
bangleTasks |= JSBT_SWIPE;
}
if (!state) {
if (touchLastState && !(touchStatus&TS_SWIPED)) {
@ -1428,6 +1440,9 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) {
// ignore if locked
if (bangleFlags & JSBF_LOCKED) return;
int dx = tx-touchX;
int dy = ty-touchY;
touchX = tx;
touchY = ty;
touchPts = pts;
@ -1436,17 +1451,21 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) {
switch (gesture) { // gesture
case 0:break; // no gesture
case 1: // slide down
bangleTasks |= JSBT_SWIPE_DOWN;
break;
touchGesture = TG_SWIPE_DOWN;
bangleTasks |= JSBT_SWIPE;
break;
case 2: // slide up
bangleTasks |= JSBT_SWIPE_UP;
break;
touchGesture = TG_SWIPE_UP;
bangleTasks |= JSBT_SWIPE;
break;
case 3: // slide left
bangleTasks |= JSBT_SWIPE_LEFT;
break;
touchGesture = TG_SWIPE_LEFT;
bangleTasks |= JSBT_SWIPE;
break;
case 4: // slide right
bangleTasks |= JSBT_SWIPE_RIGHT;
break;
touchGesture = TG_SWIPE_RIGHT;
bangleTasks |= JSBT_SWIPE;
break;
case 5: // single click
if (touchX<80) bangleTasks |= JSBT_TOUCH_LEFT;
else bangleTasks |= JSBT_TOUCH_RIGHT;
@ -1470,8 +1489,11 @@ void touchHandlerInternal(int tx, int ty, int pts, int gesture) {
// ensure we don't sleep if touchscreen is being used
inactivityTimer = 0;
#if ESPR_BANGLE_UNISTROKE
unistroke_touch(touchX, touchY, touchPts);
if (unistroke_touch(touchX, touchY, dx, dy, touchPts)) {
bangleTasks |= JSBT_STROKE;
}
#endif
jshHadEvent();
}
lastGesture = gesture;
@ -3500,11 +3522,12 @@ bool jswrap_banglejs_idle() {
inactivityTimer = 0;
}
}
if (bangleTasks & JSBT_SWIPE_MASK) {
if (bangleTasks & JSBT_SWIPE) {
JsVar *o[2] = {
jsvNewFromInteger((bangleTasks & JSBT_SWIPE_LEFT)?-1:((bangleTasks & JSBT_SWIPE_RIGHT)?1:0)),
jsvNewFromInteger((bangleTasks & JSBT_SWIPE_UP)?-1:((bangleTasks & JSBT_SWIPE_DOWN)?1:0)),
jsvNewFromInteger((touchGesture==TG_SWIPE_LEFT)?-1:((touchGesture==TG_SWIPE_RIGHT)?1:0)),
jsvNewFromInteger((touchGesture==TG_SWIPE_UP)?-1:((touchGesture==TG_SWIPE_DOWN)?1:0)),
};
touchGesture = TG_SWIPE_NONE;
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"swipe", o, 2);
jsvUnLockMany(2,o);
}
@ -3542,6 +3565,15 @@ bool jswrap_banglejs_idle() {
lastTouchY = touchY;
lastTouchPts = touchPts;
}
#endif
#if ESPR_BANGLE_UNISTROKE
if (bangleTasks & JSBT_STROKE) {
JsVar *o = unistroke_getEventVar();
if (o) {
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"stroke", &o, 1);
jsvUnLock(o);
}
}
#endif
jsvUnLock(bangle);
bangleTasks = JSBT_NONE;

View File

@ -0,0 +1,50 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Unistroke handling functionality
* ----------------------------------------------------------------------------
*/
#include "jswrap_unistroke.h"
#include "unistroke.h"
/*JSON{
"type" : "staticmethod",
"class" : "Unistroke",
"name" : "new",
"ifdef" : "BANGLEJS2",
"generate" : "jswrap_unistroke_new",
"params" : [
["xy","JsVar","An array of interleaved XY coordinates"]
],
"return" : ["JsVar","A string of data representing this unistroke"]
}
Create a new Unistroke based on XY coordinates
*/
JsVar *jswrap_unistroke_new(JsVar *xy) {
return unistroke_convert(xy);
}
/*JSON{
"type" : "staticmethod",
"class" : "Unistroke",
"name" : "recognise",
"ifdef" : "BANGLEJS2",
"generate" : "jswrap_unistroke_recognise",
"params" : [
["strokes","JsVar","An object of named strokes : `{arrow:..., circle:...}`"],
["xy","JsVar","An array of interleaved XY coordinates"]
],
"return" : ["JsVar","The key name of the matched stroke"]
}
Recognise based on an object of named strokes, and a list of XY coordinates
*/
JsVar *jswrap_unistroke_recognise(JsVar *strokes, JsVar *xy) {
return unistroke_recognise_xy(strokes, xy);
}

View File

@ -0,0 +1,21 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Unistroke handling functionality
* ----------------------------------------------------------------------------
*/
#include "jsutils.h"
#include "jsvar.h"
/// Create a new Unistroke based on XY coordinates
JsVar *jswrap_unistroke_new(JsVar *xy);
/// Recognise based on an object of named strokes, and a list of XY coordinates
JsVar *jswrap_unistroke_recognise(JsVar *strokes, JsVar *xy);

View File

@ -67,16 +67,32 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
/* TODO for Bangle.js
* Move 'points' to an array of int8
*
*/
#include <math.h>
#include <stdint.h>
#include <float.h>
#include <alloca.h>
#include "jsinteractive.h"
#define NUMPOINTS 64
#define NUMPOINTS 32
#define SQUARESIZE 176
#define PI 3.141592f
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
// DollarRecognizer constants
//
const float Diagonal = sqrtf(SQUARESIZE * SQUARESIZE + SQUARESIZE * SQUARESIZE);
const float AngleRange = 45.0f * PI / 180.0f;
const float AnglePrecision = 2.0f * PI / 180.0f;
const float Phi = 0.5f * (-1.0f + sqrtf(5.0f)); // Golden Ratio
//
// Point class
//
@ -87,7 +103,6 @@ typedef struct {
float X,Y,width,height;
} Rectangle;
typedef struct {
char name[8];
Point points[NUMPOINTS];
} Unistroke;
@ -107,125 +122,60 @@ float PathLength(Point *points, int pointsLen);
float Distance(Point p1, Point p2);
float Deg2Rad(float d);
/*Unistroke newUnistroke(char *name, Point* points) {
}*/
//
// Unistroke class: a unistroke template
//
/*function Unistroke(name, points) // constructor
{
this.Name = name;
this.Points = Resample(points, NUMPOINTS);
var radians = IndicativeAngle(this.Points);
this.Points = RotateBy(this.Points, -radians);
this.Points = ScaleTo(this.Points, SQUARESIZE);
this.Points = TranslateTo(this.Points, Origin);
this.Vector = Vectorize(this.Points); // for Protractor
}*/
//
// Result class
//
/*void newResult(name, score, ms) { // constructor
this.Name = name;
this.Score = score;
this.Time = ms;
}*/
//
// DollarRecognizer constants
//
const Point Origin = {0,0};
const float Diagonal = sqrtf(SQUARESIZE * SQUARESIZE + SQUARESIZE * SQUARESIZE);
const float AngleRange = 45.0 * PI / 180.0f;
const float AnglePrecision = 2.0 * PI / 180.0f;
const float Phi = 0.5 * (-1.0 + sqrtf(5.0)); // Golden Ratio
//
// DollarRecognizer class
//
void DollarRecognizer() // constructor
{
//
// one built-in unistroke per gesture type
//
/*this.Unistrokes = new Array(NumUnistrokes);
this.Unistrokes[0] = newUnistroke("triangle", new Array(new Point(137,139),new Point(135,141),new Point(133,144),new Point(132,146),new Point(130,149),new Point(128,151),new Point(126,155),new Point(123,160),new Point(120,166),new Point(116,171),new Point(112,177),new Point(107,183),new Point(102,188),new Point(100,191),new Point(95,195),new Point(90,199),new Point(86,203),new Point(82,206),new Point(80,209),new Point(75,213),new Point(73,213),new Point(70,216),new Point(67,219),new Point(64,221),new Point(61,223),new Point(60,225),new Point(62,226),new Point(65,225),new Point(67,226),new Point(74,226),new Point(77,227),new Point(85,229),new Point(91,230),new Point(99,231),new Point(108,232),new Point(116,233),new Point(125,233),new Point(134,234),new Point(145,233),new Point(153,232),new Point(160,233),new Point(170,234),new Point(177,235),new Point(179,236),new Point(186,237),new Point(193,238),new Point(198,239),new Point(200,237),new Point(202,239),new Point(204,238),new Point(206,234),new Point(205,230),new Point(202,222),new Point(197,216),new Point(192,207),new Point(186,198),new Point(179,189),new Point(174,183),new Point(170,178),new Point(164,171),new Point(161,168),new Point(154,160),new Point(148,155),new Point(143,150),new Point(138,148),new Point(136,148)));
this.Unistrokes[1] = newUnistroke("x", new Array(new Point(87,142),new Point(89,145),new Point(91,148),new Point(93,151),new Point(96,155),new Point(98,157),new Point(100,160),new Point(102,162),new Point(106,167),new Point(108,169),new Point(110,171),new Point(115,177),new Point(119,183),new Point(123,189),new Point(127,193),new Point(129,196),new Point(133,200),new Point(137,206),new Point(140,209),new Point(143,212),new Point(146,215),new Point(151,220),new Point(153,222),new Point(155,223),new Point(157,225),new Point(158,223),new Point(157,218),new Point(155,211),new Point(154,208),new Point(152,200),new Point(150,189),new Point(148,179),new Point(147,170),new Point(147,158),new Point(147,148),new Point(147,141),new Point(147,136),new Point(144,135),new Point(142,137),new Point(140,139),new Point(135,145),new Point(131,152),new Point(124,163),new Point(116,177),new Point(108,191),new Point(100,206),new Point(94,217),new Point(91,222),new Point(89,225),new Point(87,226),new Point(87,224)));
this.Unistrokes[2] = newUnistroke("rectangle", new Array(new Point(78,149),new Point(78,153),new Point(78,157),new Point(78,160),new Point(79,162),new Point(79,164),new Point(79,167),new Point(79,169),new Point(79,173),new Point(79,178),new Point(79,183),new Point(80,189),new Point(80,193),new Point(80,198),new Point(80,202),new Point(81,208),new Point(81,210),new Point(81,216),new Point(82,222),new Point(82,224),new Point(82,227),new Point(83,229),new Point(83,231),new Point(85,230),new Point(88,232),new Point(90,233),new Point(92,232),new Point(94,233),new Point(99,232),new Point(102,233),new Point(106,233),new Point(109,234),new Point(117,235),new Point(123,236),new Point(126,236),new Point(135,237),new Point(142,238),new Point(145,238),new Point(152,238),new Point(154,239),new Point(165,238),new Point(174,237),new Point(179,236),new Point(186,235),new Point(191,235),new Point(195,233),new Point(197,233),new Point(200,233),new Point(201,235),new Point(201,233),new Point(199,231),new Point(198,226),new Point(198,220),new Point(196,207),new Point(195,195),new Point(195,181),new Point(195,173),new Point(195,163),new Point(194,155),new Point(192,145),new Point(192,143),new Point(192,138),new Point(191,135),new Point(191,133),new Point(191,130),new Point(190,128),new Point(188,129),new Point(186,129),new Point(181,132),new Point(173,131),new Point(162,131),new Point(151,132),new Point(149,132),new Point(138,132),new Point(136,132),new Point(122,131),new Point(120,131),new Point(109,130),new Point(107,130),new Point(90,132),new Point(81,133),new Point(76,133)));
this.Unistrokes[3] = newUnistroke("circle", new Array(new Point(127,141),new Point(124,140),new Point(120,139),new Point(118,139),new Point(116,139),new Point(111,140),new Point(109,141),new Point(104,144),new Point(100,147),new Point(96,152),new Point(93,157),new Point(90,163),new Point(87,169),new Point(85,175),new Point(83,181),new Point(82,190),new Point(82,195),new Point(83,200),new Point(84,205),new Point(88,213),new Point(91,216),new Point(96,219),new Point(103,222),new Point(108,224),new Point(111,224),new Point(120,224),new Point(133,223),new Point(142,222),new Point(152,218),new Point(160,214),new Point(167,210),new Point(173,204),new Point(178,198),new Point(179,196),new Point(182,188),new Point(182,177),new Point(178,167),new Point(170,150),new Point(163,138),new Point(152,130),new Point(143,129),new Point(140,131),new Point(129,136),new Point(126,139)));
this.Unistrokes[4] = newUnistroke("check", new Array(new Point(91,185),new Point(93,185),new Point(95,185),new Point(97,185),new Point(100,188),new Point(102,189),new Point(104,190),new Point(106,193),new Point(108,195),new Point(110,198),new Point(112,201),new Point(114,204),new Point(115,207),new Point(117,210),new Point(118,212),new Point(120,214),new Point(121,217),new Point(122,219),new Point(123,222),new Point(124,224),new Point(126,226),new Point(127,229),new Point(129,231),new Point(130,233),new Point(129,231),new Point(129,228),new Point(129,226),new Point(129,224),new Point(129,221),new Point(129,218),new Point(129,212),new Point(129,208),new Point(130,198),new Point(132,189),new Point(134,182),new Point(137,173),new Point(143,164),new Point(147,157),new Point(151,151),new Point(155,144),new Point(161,137),new Point(165,131),new Point(171,122),new Point(174,118),new Point(176,114),new Point(177,112),new Point(177,114),new Point(175,116),new Point(173,118)));
this.Unistrokes[5] = newUnistroke("caret", new Array(new Point(79,245),new Point(79,242),new Point(79,239),new Point(80,237),new Point(80,234),new Point(81,232),new Point(82,230),new Point(84,224),new Point(86,220),new Point(86,218),new Point(87,216),new Point(88,213),new Point(90,207),new Point(91,202),new Point(92,200),new Point(93,194),new Point(94,192),new Point(96,189),new Point(97,186),new Point(100,179),new Point(102,173),new Point(105,165),new Point(107,160),new Point(109,158),new Point(112,151),new Point(115,144),new Point(117,139),new Point(119,136),new Point(119,134),new Point(120,132),new Point(121,129),new Point(122,127),new Point(124,125),new Point(126,124),new Point(129,125),new Point(131,127),new Point(132,130),new Point(136,139),new Point(141,154),new Point(145,166),new Point(151,182),new Point(156,193),new Point(157,196),new Point(161,209),new Point(162,211),new Point(167,223),new Point(169,229),new Point(170,231),new Point(173,237),new Point(176,242),new Point(177,244),new Point(179,250),new Point(181,255),new Point(182,257)));
this.Unistrokes[6] = newUnistroke("zig-zag", new Array(new Point(307,216),new Point(333,186),new Point(356,215),new Point(375,186),new Point(399,216),new Point(418,186)));
this.Unistrokes[7] = newUnistroke("arrow", new Array(new Point(68,222),new Point(70,220),new Point(73,218),new Point(75,217),new Point(77,215),new Point(80,213),new Point(82,212),new Point(84,210),new Point(87,209),new Point(89,208),new Point(92,206),new Point(95,204),new Point(101,201),new Point(106,198),new Point(112,194),new Point(118,191),new Point(124,187),new Point(127,186),new Point(132,183),new Point(138,181),new Point(141,180),new Point(146,178),new Point(154,173),new Point(159,171),new Point(161,170),new Point(166,167),new Point(168,167),new Point(171,166),new Point(174,164),new Point(177,162),new Point(180,160),new Point(182,158),new Point(183,156),new Point(181,154),new Point(178,153),new Point(171,153),new Point(164,153),new Point(160,153),new Point(150,154),new Point(147,155),new Point(141,157),new Point(137,158),new Point(135,158),new Point(137,158),new Point(140,157),new Point(143,156),new Point(151,154),new Point(160,152),new Point(170,149),new Point(179,147),new Point(185,145),new Point(192,144),new Point(196,144),new Point(198,144),new Point(200,144),new Point(201,147),new Point(199,149),new Point(194,157),new Point(191,160),new Point(186,167),new Point(180,176),new Point(177,179),new Point(171,187),new Point(169,189),new Point(165,194),new Point(164,196)));
this.Unistrokes[8] = newUnistroke("left square bracket", new Array(new Point(140,124),new Point(138,123),new Point(135,122),new Point(133,123),new Point(130,123),new Point(128,124),new Point(125,125),new Point(122,124),new Point(120,124),new Point(118,124),new Point(116,125),new Point(113,125),new Point(111,125),new Point(108,124),new Point(106,125),new Point(104,125),new Point(102,124),new Point(100,123),new Point(98,123),new Point(95,124),new Point(93,123),new Point(90,124),new Point(88,124),new Point(85,125),new Point(83,126),new Point(81,127),new Point(81,129),new Point(82,131),new Point(82,134),new Point(83,138),new Point(84,141),new Point(84,144),new Point(85,148),new Point(85,151),new Point(86,156),new Point(86,160),new Point(86,164),new Point(86,168),new Point(87,171),new Point(87,175),new Point(87,179),new Point(87,182),new Point(87,186),new Point(88,188),new Point(88,195),new Point(88,198),new Point(88,201),new Point(88,207),new Point(89,211),new Point(89,213),new Point(89,217),new Point(89,222),new Point(88,225),new Point(88,229),new Point(88,231),new Point(88,233),new Point(88,235),new Point(89,237),new Point(89,240),new Point(89,242),new Point(91,241),new Point(94,241),new Point(96,240),new Point(98,239),new Point(105,240),new Point(109,240),new Point(113,239),new Point(116,240),new Point(121,239),new Point(130,240),new Point(136,237),new Point(139,237),new Point(144,238),new Point(151,237),new Point(157,236),new Point(159,237)));
this.Unistrokes[9] = newUnistroke("right square bracket", new Array(new Point(112,138),new Point(112,136),new Point(115,136),new Point(118,137),new Point(120,136),new Point(123,136),new Point(125,136),new Point(128,136),new Point(131,136),new Point(134,135),new Point(137,135),new Point(140,134),new Point(143,133),new Point(145,132),new Point(147,132),new Point(149,132),new Point(152,132),new Point(153,134),new Point(154,137),new Point(155,141),new Point(156,144),new Point(157,152),new Point(158,161),new Point(160,170),new Point(162,182),new Point(164,192),new Point(166,200),new Point(167,209),new Point(168,214),new Point(168,216),new Point(169,221),new Point(169,223),new Point(169,228),new Point(169,231),new Point(166,233),new Point(164,234),new Point(161,235),new Point(155,236),new Point(147,235),new Point(140,233),new Point(131,233),new Point(124,233),new Point(117,235),new Point(114,238),new Point(112,238)));
this.Unistrokes[10] = newUnistroke("v", new Array(new Point(89,164),new Point(90,162),new Point(92,162),new Point(94,164),new Point(95,166),new Point(96,169),new Point(97,171),new Point(99,175),new Point(101,178),new Point(103,182),new Point(106,189),new Point(108,194),new Point(111,199),new Point(114,204),new Point(117,209),new Point(119,214),new Point(122,218),new Point(124,222),new Point(126,225),new Point(128,228),new Point(130,229),new Point(133,233),new Point(134,236),new Point(136,239),new Point(138,240),new Point(139,242),new Point(140,244),new Point(142,242),new Point(142,240),new Point(142,237),new Point(143,235),new Point(143,233),new Point(145,229),new Point(146,226),new Point(148,217),new Point(149,208),new Point(149,205),new Point(151,196),new Point(151,193),new Point(153,182),new Point(155,172),new Point(157,165),new Point(159,160),new Point(162,155),new Point(164,150),new Point(165,148),new Point(166,146)));
this.Unistrokes[11] = newUnistroke("delete", new Array(new Point(123,129),new Point(123,131),new Point(124,133),new Point(125,136),new Point(127,140),new Point(129,142),new Point(133,148),new Point(137,154),new Point(143,158),new Point(145,161),new Point(148,164),new Point(153,170),new Point(158,176),new Point(160,178),new Point(164,183),new Point(168,188),new Point(171,191),new Point(175,196),new Point(178,200),new Point(180,202),new Point(181,205),new Point(184,208),new Point(186,210),new Point(187,213),new Point(188,215),new Point(186,212),new Point(183,211),new Point(177,208),new Point(169,206),new Point(162,205),new Point(154,207),new Point(145,209),new Point(137,210),new Point(129,214),new Point(122,217),new Point(118,218),new Point(111,221),new Point(109,222),new Point(110,219),new Point(112,217),new Point(118,209),new Point(120,207),new Point(128,196),new Point(135,187),new Point(138,183),new Point(148,167),new Point(157,153),new Point(163,145),new Point(165,142),new Point(172,133),new Point(177,127),new Point(179,127),new Point(180,125)));
this.Unistrokes[12] = newUnistroke("left curly brace", new Array(new Point(150,116),new Point(147,117),new Point(145,116),new Point(142,116),new Point(139,117),new Point(136,117),new Point(133,118),new Point(129,121),new Point(126,122),new Point(123,123),new Point(120,125),new Point(118,127),new Point(115,128),new Point(113,129),new Point(112,131),new Point(113,134),new Point(115,134),new Point(117,135),new Point(120,135),new Point(123,137),new Point(126,138),new Point(129,140),new Point(135,143),new Point(137,144),new Point(139,147),new Point(141,149),new Point(140,152),new Point(139,155),new Point(134,159),new Point(131,161),new Point(124,166),new Point(121,166),new Point(117,166),new Point(114,167),new Point(112,166),new Point(114,164),new Point(116,163),new Point(118,163),new Point(120,162),new Point(122,163),new Point(125,164),new Point(127,165),new Point(129,166),new Point(130,168),new Point(129,171),new Point(127,175),new Point(125,179),new Point(123,184),new Point(121,190),new Point(120,194),new Point(119,199),new Point(120,202),new Point(123,207),new Point(127,211),new Point(133,215),new Point(142,219),new Point(148,220),new Point(151,221)));
this.Unistrokes[13] = newUnistroke("right curly brace", new Array(new Point(117,132),new Point(115,132),new Point(115,129),new Point(117,129),new Point(119,128),new Point(122,127),new Point(125,127),new Point(127,127),new Point(130,127),new Point(133,129),new Point(136,129),new Point(138,130),new Point(140,131),new Point(143,134),new Point(144,136),new Point(145,139),new Point(145,142),new Point(145,145),new Point(145,147),new Point(145,149),new Point(144,152),new Point(142,157),new Point(141,160),new Point(139,163),new Point(137,166),new Point(135,167),new Point(133,169),new Point(131,172),new Point(128,173),new Point(126,176),new Point(125,178),new Point(125,180),new Point(125,182),new Point(126,184),new Point(128,187),new Point(130,187),new Point(132,188),new Point(135,189),new Point(140,189),new Point(145,189),new Point(150,187),new Point(155,186),new Point(157,185),new Point(159,184),new Point(156,185),new Point(154,185),new Point(149,185),new Point(145,187),new Point(141,188),new Point(136,191),new Point(134,191),new Point(131,192),new Point(129,193),new Point(129,195),new Point(129,197),new Point(131,200),new Point(133,202),new Point(136,206),new Point(139,211),new Point(142,215),new Point(145,220),new Point(147,225),new Point(148,231),new Point(147,239),new Point(144,244),new Point(139,248),new Point(134,250),new Point(126,253),new Point(119,253),new Point(115,253)));
this.Unistrokes[14] = newUnistroke("star", new Array(new Point(75,250),new Point(75,247),new Point(77,244),new Point(78,242),new Point(79,239),new Point(80,237),new Point(82,234),new Point(82,232),new Point(84,229),new Point(85,225),new Point(87,222),new Point(88,219),new Point(89,216),new Point(91,212),new Point(92,208),new Point(94,204),new Point(95,201),new Point(96,196),new Point(97,194),new Point(98,191),new Point(100,185),new Point(102,178),new Point(104,173),new Point(104,171),new Point(105,164),new Point(106,158),new Point(107,156),new Point(107,152),new Point(108,145),new Point(109,141),new Point(110,139),new Point(112,133),new Point(113,131),new Point(116,127),new Point(117,125),new Point(119,122),new Point(121,121),new Point(123,120),new Point(125,122),new Point(125,125),new Point(127,130),new Point(128,133),new Point(131,143),new Point(136,153),new Point(140,163),new Point(144,172),new Point(145,175),new Point(151,189),new Point(156,201),new Point(161,213),new Point(166,225),new Point(169,233),new Point(171,236),new Point(174,243),new Point(177,247),new Point(178,249),new Point(179,251),new Point(180,253),new Point(180,255),new Point(179,257),new Point(177,257),new Point(174,255),new Point(169,250),new Point(164,247),new Point(160,245),new Point(149,238),new Point(138,230),new Point(127,221),new Point(124,220),new Point(112,212),new Point(110,210),new Point(96,201),new Point(84,195),new Point(74,190),new Point(64,182),new Point(55,175),new Point(51,172),new Point(49,170),new Point(51,169),new Point(56,169),new Point(66,169),new Point(78,168),new Point(92,166),new Point(107,164),new Point(123,161),new Point(140,162),new Point(156,162),new Point(171,160),new Point(173,160),new Point(186,160),new Point(195,160),new Point(198,161),new Point(203,163),new Point(208,163),new Point(206,164),new Point(200,167),new Point(187,172),new Point(174,179),new Point(172,181),new Point(153,192),new Point(137,201),new Point(123,211),new Point(112,220),new Point(99,229),new Point(90,237),new Point(80,244),new Point(73,250),new Point(69,254),new Point(69,252)));
this.Unistrokes[15] = newUnistroke("pigtail", new Array(new Point(81,219),new Point(84,218),new Point(86,220),new Point(88,220),new Point(90,220),new Point(92,219),new Point(95,220),new Point(97,219),new Point(99,220),new Point(102,218),new Point(105,217),new Point(107,216),new Point(110,216),new Point(113,214),new Point(116,212),new Point(118,210),new Point(121,208),new Point(124,205),new Point(126,202),new Point(129,199),new Point(132,196),new Point(136,191),new Point(139,187),new Point(142,182),new Point(144,179),new Point(146,174),new Point(148,170),new Point(149,168),new Point(151,162),new Point(152,160),new Point(152,157),new Point(152,155),new Point(152,151),new Point(152,149),new Point(152,146),new Point(149,142),new Point(148,139),new Point(145,137),new Point(141,135),new Point(139,135),new Point(134,136),new Point(130,140),new Point(128,142),new Point(126,145),new Point(122,150),new Point(119,158),new Point(117,163),new Point(115,170),new Point(114,175),new Point(117,184),new Point(120,190),new Point(125,199),new Point(129,203),new Point(133,208),new Point(138,213),new Point(145,215),new Point(155,218),new Point(164,219),new Point(166,219),new Point(177,219),new Point(182,218),new Point(192,216),new Point(196,213),new Point(199,212),new Point(201,211)));
} */
//
// The $1 Gesture Recognizer API begins here -- 3 methods: Recognize(), AddGesture(), and DeleteUserGestures()
//
/*
void Recognize(Point *points, int pointsLen, bool useProtractor) {
var candidate = new Unistroke("", points);
int u = -1;
float b = FLT_MAX;
for (var i = 0; i < this.Unistrokes.length; i++) { // for each unistroke template
float d;
if (useProtractor)
d = OptimalCosineDistance(this.Unistrokes[i].Vector, candidate.Vector); // Protractor
else
d = DistanceAtBestAngle(candidate.Points, this.Unistrokes[i], -AngleRange, +AngleRange, AnglePrecision); // Golden Section Search (original $1)
if (d < b) {
b = d; // best (least) distance
u = i; // unistroke index
}
/*void dumpPts(const char *n, Point *points, int pointCount) {
jsiConsolePrintf("%s\n",n);
for (int i=0;i<pointCount;i++) {
jsiConsolePrintf(" %d: %f,%f\n", i, points[i].X, points[i].Y);
}
return (u == -1) ? new Result("No match.", 0.0) : new Result(this.Unistrokes[u].Name, useProtractor ? (1.0 - b) : (1.0 - b / (0.5 * Diagonal)));
}*/
// A unistroke template -
Unistroke newUnistroke(Point *points, int pointCount) {
Unistroke t;
Resample(t.points, NUMPOINTS, points, pointCount);
float radians = IndicativeAngle(t.points, NUMPOINTS);
RotateBy(t.points, t.points, NUMPOINTS, -radians);
ScaleTo(t.points, t.points, NUMPOINTS, SQUARESIZE);
Point Origin = {0,0};
TranslateTo(t.points, t.points, NUMPOINTS, Origin);
//t.Vector = Vectorize(this.Points); // for Protractor
return t;
}
this.AddGesture = function(name, points)
{
this.Unistrokes[this.Unistrokes.length] = new Unistroke(name, points); // append new unistroke
var num = 0;
for (var i = 0; i < this.Unistrokes.length; i++) {
if (this.Unistrokes[i].Name == name)
num++;
}
return num;
void uint8ToPoints(Point *points, const uint8_t *xy, int xyCount) {
for (int i=0;i<xyCount;i++) {
points[i].X = xy[i*2];
points[i].Y = xy[i*2 + 1];
}
this.DeleteUserGestures = function()
{
this.Unistrokes.length = NumUnistrokes; // clear any beyond the original set
return NumUnistrokes;
}*/
}
Unistroke newUnistroke8(const uint8_t *xy, int xyCount) {
Point points[NUMPOINTS];
uint8ToPoints(points, xy, xyCount);
return newUnistroke(points, xyCount);
}
//
// Private helper functions from here on down
//
void Resample(Point *dst, int n, Point *points, int pointsLen) // points is damaged by calling Resample
{
float I = PathLength(points, pointsLen) / (n - 1); // interval length
float I = PathLength(points, pointsLen) / (n - 1.0f); // interval length
float D = 0.0;
int dstLen = 0;
dst[dstLen++] = points[0];
for (int i = 1; i < pointsLen; i++)
{
float d = Distance(points[i-1], points[i]);
if ((D + d) >= I)
{
if ((D + d) >= I) {
float qx = points[i-1].X + ((I - D) / d) * (points[i].X - points[i-1].X);
float qy = points[i-1].Y + ((I - D) / d) * (points[i].Y - points[i-1].Y);
Point q = {qx, qy};
dst[dstLen++] = q; // append new point 'q'
points[i]=q; // insert 'q' at position i in points s.t. 'q' will be the next i
points[i-1]=q;i--; // insert 'q' at position i in points s.t. 'q' will be the next i
D = 0.0;
}
else D += d;
} else D += d;
}
if (dstLen == n - 1) {// sometimes we fall a rounding-error short of adding the last point, so add it if so
Point q = {points[pointsLen- 1].X, points[pointsLen - 1].Y };
@ -235,13 +185,13 @@ void Resample(Point *dst, int n, Point *points, int pointsLen) // points is dama
float IndicativeAngle(Point *points, int pointsLen)
{
Point c = Centroid(points, pointsLen);
return atan2(c.Y - points[0].Y, c.X - points[0].X);
return (float)atan2(c.Y - points[0].Y, c.X - points[0].X);
}
void RotateBy(Point *dst, Point *points, int pointsLen, float radians) // rotates points around centroid
{
Point c = Centroid(points, pointsLen);
float fcos = cos(radians);
float fsin = sin(radians);
float fcos = (float)cos(radians);
float fsin = (float)sin(radians);
for (int i = 0; i < pointsLen; i++) {
dst[i].X = (points[i].X - c.X) * fcos - (points[i].Y - c.Y) * fsin + c.X;
dst[i].Y = (points[i].X - c.X) * fsin + (points[i].Y - c.Y) * fcos + c.Y;
@ -283,14 +233,14 @@ float OptimalCosineDistance(float *v1, float *v2, int vLen) // for Protractor
a += v1[i] * v2[i] + v1[i+1] * v2[i+1];
b += v1[i] * v2[i+1] - v1[i+1] * v2[i];
}
float angle = atan(b / a);
return acos(a * cos(angle) + b * sin(angle));
float angle = (float)atan(b / a);
return (float)acos(a * cos(angle) + b * sin(angle));
}
float DistanceAtBestAngle(Point *points, int pointsLen, Point *T, float a, float b, float threshold)
{
float x1 = Phi * a + (1.0 - Phi) * b;
float x1 = Phi * a + (1.0f - Phi) * b;
float f1 = DistanceAtAngle(points, pointsLen, T, x1);
float x2 = (1.0 - Phi) * a + Phi * b;
float x2 = (1.0f - Phi) * a + Phi * b;
float f2 = DistanceAtAngle(points, pointsLen, T, x2);
while (fabs(b - a) > threshold)
{
@ -298,13 +248,13 @@ float DistanceAtBestAngle(Point *points, int pointsLen, Point *T, float a, float
b = x2;
x2 = x1;
f2 = f1;
x1 = Phi * a + (1.0 - Phi) * b;
x1 = Phi * a + (1.0f - Phi) * b;
f1 = DistanceAtAngle(points, pointsLen, T, x1);
} else {
a = x1;
x1 = x2;
f1 = f2;
x2 = (1.0 - Phi) * a + Phi * b;
x2 = (1.0f - Phi) * a + Phi * b;
f2 = DistanceAtAngle(points, pointsLen, T, x2);
}
}
@ -349,7 +299,7 @@ float PathDistance(Point *pts1, Point *pts2, int pointsLen)
}
float PathLength(Point *points, int pointsLen)
{
float d = 0.0;
float d = 0.0f;
for (int i = 1; i < pointsLen; i++)
d += Distance(points[i - 1], points[i]);
return d;
@ -358,7 +308,7 @@ float Distance(Point p1, Point p2)
{
float dx = p2.X - p1.X;
float dy = p2.Y - p1.Y;
return sqrtf(dx * dx + dy * dy);
return sqrtf((dx*dx) + (dy*dy));
}
float Deg2Rad(float d) {
return d * PI / 180.0f;
@ -369,34 +319,39 @@ float Deg2Rad(float d) {
Point touchHistory[NUMPOINTS];
uint8_t touchHistoryLen; // how many points in touchHistory
Point touchSum; // sum of points so far
bool touchLastPressed; // Is a finger on the screen? If not, next press we should start from scratch
uint8_t touchCnt; // how many touches are in touchSum
uint8_t touchDivisor; // how many touch points do we sum to get the next point?
int touchDistance; // distance dragged
/// initialise stroke detection
void unistroke_init() {
touchHistoryLen = 0;
touchDivisor = 1;
touchLastPressed = false;
}
/// Called when a touch event occurs
void unistroke_touch(int x, int y, int pts) {
#ifdef LCD_WIDTH
/// Called when a touch event occurs, returns 'true' if an event should be created (by calling unistroke_getEventVar)
bool unistroke_touch(int x, int y, int dx, int dy, int pts) {
if (!pts) { // touch released
/*jsiConsolePrintf("[");
for (int i=0;i<touchHistoryLen;i++) {
jsiConsolePrintf("%d,%d,", (int)touchHistory[i].X, (int)touchHistory[i].Y);
}
jsiConsolePrintf("]\n");*/
touchLastPressed = false;
return touchDistance > LCD_WIDTH/2; // fire off an event IF the distance is large amount
}
// now finger is definitely pressed
if (!touchLastPressed) {
// reset everything if this is the first time
touchHistoryLen = 0;
touchDivisor = 1;
touchSum.X = 0;
touchSum.Y = 0;
touchCnt = 0;
return;
touchDistance = 0;
touchLastPressed = true;
}
// append to touch distance
touchDistance += int_sqrt32((dx*dx)+(dy*dy));
// store the sum of touch points
touchSum.X += x;
@ -423,6 +378,90 @@ void unistroke_touch(int x, int y, int pts) {
touchSum.Y = 0;
touchCnt = 0;
}
return false;
}
#endif
/// Convert an array containing XY values to a unistroke var
JsVar *unistroke_convert(JsVar *xy) {
uint8_t points8[NUMPOINTS*2];
unsigned int bytes = jsvIterateCallbackToBytes(xy, points8, sizeof(points8));
int pointCount = bytes/2;
Unistroke uni = newUnistroke8(points8, pointCount);
return jsvNewStringOfLength(sizeof(uni), (char *)&uni);
}
/// recognise a single stroke
float unistroke_recognise_one(JsVar *strokeVar, Unistroke *candidate) {
float d = FLT_MAX;
JSV_GET_AS_CHAR_ARRAY(strokePtr, strokeLen, strokeVar)
if (strokePtr && strokeLen==sizeof(Unistroke)) {
Unistroke *uni = (Unistroke*)strokePtr;
/*if (useProtractor)
d = OptimalCosineDistance(this.Unistrokes[i].Vector, candidate.Vector); // Protractor
else*/
d = DistanceAtBestAngle(candidate->points, NUMPOINTS, uni->points, -AngleRange, +AngleRange, AnglePrecision); // Golden Section Search (original $1)
}
jsvUnLock(strokeVar);
return d;
}
/// Given an object containing values created with unistroke_convert, compare against a unistroke
JsVar *unistroke_recognise(JsVar *strokes, Unistroke *candidate) {
JsVar *u = 0;
float b = FLT_MAX;
JsvObjectIterator it;
jsvObjectIteratorNew(&it, strokes);
while (jsvObjectIteratorHasValue(&it)) { // for each unistroke template
JsVar *strokeVar = jsvObjectIteratorGetValue(&it);
float d = unistroke_recognise_one(strokeVar, candidate);
if (d < b) {
b = d; // best (least) distance
jsvUnLock(u);
u = jsvObjectIteratorGetKey(&it); // unistroke index
}
jsvUnLock(strokeVar);
jsvObjectIteratorNext(&it);
}
// CERTAINTY = useProtractor ? (1.0 - b) : (1.0 - b / (0.5 * Diagonal))
return u ? jsvAsStringAndUnLock(u) : 0;
}
/// Given an object containing values created with unistroke_convert, compare against an array containing XY values
JsVar *unistroke_recognise_xy(JsVar *strokes, JsVar *xy) {
uint8_t points8[NUMPOINTS*2];
unsigned int bytes = jsvIterateCallbackToBytes(xy, points8, sizeof(points8));
int pointCount = bytes/2;
Unistroke uni = newUnistroke8(points8, pointCount);
return unistroke_recognise(strokes, &uni);
}
JsVar *unistroke_getEventVar() {
if (!touchHistoryLen) return 0;
JsVar *o = jsvNewObject();
if (!o) return 0;
JsVar *a = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, touchHistoryLen*2);
if (a) {
JsVar *s = jsvGetArrayBufferBackingString(a,NULL);
size_t n=0;
for (int i=0;i<touchHistoryLen;i++) {
jsvSetCharInString(s, n++, (char)touchHistory[i].X, false);
jsvSetCharInString(s, n++, (char)touchHistory[i].Y, false);
}
jsvUnLock(s);
jsvObjectSetChildAndUnLock(o, "xy", a);
}
JsVar *bangle = jsvObjectGetChild(execInfo.root, "Bangle", 0);
if (bangle) {
JsVar *strokes = jsvObjectGetChild(bangle, "strokes", 0);
if (jsvIsObject(strokes)) {
Unistroke uni = newUnistroke(touchHistory, touchHistoryLen);
jsvObjectSetChildAndUnLock(o, "stroke", unistroke_recognise(strokes, &uni));
}
jsvUnLock2(bangle, strokes);
}
return o;
}

View File

@ -15,5 +15,14 @@
/// initialise stroke detection
void unistroke_init();
/// Called when a touch event occurs
void unistroke_touch(int x, int y, int pts);
/// Called when a touch event occurs, returns 'true' if an event should be created (by calling unistroke_getEventVar)
bool unistroke_touch(int x, int y, int dx, int dy, int pts);
/// Called when a touch event occurs, and returns the data that should be passed in the event
JsVar *unistroke_getEventVar();
/// Convert an array containing XY values to a unistroke var
JsVar *unistroke_convert(JsVar *xy);
/// Given an object containing values created with unistroke_convert, compare against an array containing XY values
JsVar *unistroke_recognise_xy(JsVar *strokes, JsVar *xy);

View File

@ -946,3 +946,33 @@ void srand(unsigned int seed) {
rand_m_w = (seed&0xFFFF) | (seed<<16);
rand_m_z = (seed&0xFFFF0000) | (seed>>16);
}
/// Clip X between -128 and 127
char clipi8(int x) {
if (x<-128) return -128;
if (x>127) return 127;
return (char)x;
}
/// Convert the given value to a signed integer assuming it has the given number of bits
int twosComplement(int val, unsigned char bits) {
if (val & ((unsigned int)1 << (bits - 1)))
val -= (unsigned int)1 << bits;
return val;
}
// quick integer square root
// https://stackoverflow.com/questions/31117497/fastest-integer-square-root-in-the-least-amount-of-instructions
unsigned short int int_sqrt32(unsigned int x) {
unsigned short int res=0;
unsigned short int add= 0x8000;
int i;
for(i=0;i<16;i++) {
unsigned short int temp=res | add;
unsigned int g2=temp*temp;
if (x>=g2)
res=temp;
add>>=1;
}
return res;
}

View File

@ -144,8 +144,12 @@ typedef uint32_t JsfWord;
//typedef unsigned char bool;
#endif
#ifndef DBL_MIN
#define DBL_MIN 2.2250738585072014e-308
#endif
#ifndef DBL_MAX
#define DBL_MAX 1.7976931348623157e+308
#endif
/* Number of JS Variables allowed and JS Variable reference format.
@ -575,6 +579,15 @@ int rand();
/// a rand() replacement that doesn't need malloc (!!!)
void srand(unsigned int seed);
/// Clip X between -128 and 127
char clipi8(int x);
/// Convert the given value to a signed integer assuming it has the given number of bits
int twosComplement(int val, unsigned char bits);
/// quick integer square root
unsigned short int int_sqrt32(unsigned int x);
/** get the amount of free stack we have, in bytes */
size_t jsuGetFreeStack();

View File

@ -1,827 +0,0 @@
// From https://gist.github.com/randunel/8983226
// It's very unlikely this will ever pass completely, but it's good to have
// IMO it should be split into separate tests
var tests = 0;
var testsPass = 0;
// From V8 and SpiderMonkey
function shouldBe(item, value) {
tests++;
if (eval(item) !== eval(value)) {
console.log('ERROR:', item, 'should be', value, 'but found', '' + eval(item));
} else testsPass++;
}
function shouldBeUndefined(item) {
tests++;
if (eval(item) !== undefined) {
console.log('ERROR:', item, 'should be undefined but found', '' + eval(item));
} else testsPass++;
}
function shouldBeTrue(item) {
tests++;
if (eval(item) !== true) {
console.log('ERROR:', item, 'should be true but found', '' + eval(item));
} else testsPass++;
}
function shouldBeFalse(item) {
tests++;
if (eval(item) !== false) {
console.log('ERROR:', item, 'should be false but found', '' + eval(item));
} else testsPass++;
}
function shouldBeOfType(msg, val, type) {
tests++;
if (typeof(val) !== type)
console.log('ERROR:', msg + ": value has type " + typeof(val) + " , not:" + type);
else testsPass++;
}
function shouldBeVal(msg, val, expected) {
tests++;
if (val !== expected)
console.log('ERROR:', msg + ": value is " + val + " , not:" + expected);
else testsPass++;
}
shouldBe("Array().length", "0");
shouldBe("(new Array()).length", "0");
shouldBe("(new Array(3)).length", "3");
shouldBe("(new Array(11, 22)).length", "2");
shouldBe("(new Array(11, 22))[0]", "11");
shouldBe("Array(11, 22)[1]", "22");
shouldBeUndefined("(new Array(11, 22))[3]");
shouldBe("String(new Array(11, 22))", "'11,22'");
shouldBe("var a = []; a[0] = 33; a[0]", "33");
shouldBe("var a = []; a[0] = 33; a.length", "1");
shouldBe("Array().toString()", "''");
shouldBe("Array(3).toString()", "',,'");
shouldBe("Array(11, 22).toString()", "'11,22'");
shouldBe("[1,2,3,4].slice(1, 3).toString()", "'2,3'");
shouldBe("[1,2,3,4].slice(-3, -1).toString()", "'2,3'");
shouldBe("[1,2].slice(-9, 0).length", "0");
shouldBe("[1,2].slice(1).toString()", "'2'");
shouldBe("[1,2].slice().toString()", "'1,2'");
shouldBe("(new Array('a')).length", "1");
shouldBe("(new Array('a'))[0]", "'a'");
shouldBeUndefined("(new Array('a'))[1]");
shouldBe("Array('a').length", "1");
shouldBe("Array('a')[0]", "'a'");
shouldBe("String(Array())", "''");
shouldBe("String(Array('a','b'))", "'a,b'");
shouldBe("[].length", "0");
shouldBe("['a'].length", "1");
shouldBe("['a'][0]", "'a'");
shouldBe("['a',undefined,'c'][1]", "undefined");
shouldBe("1 in ['a',undefined,'c']", "true");
var arrayWithDeletion = ['a','b','c'];
delete arrayWithDeletion[1];
shouldBe("1 in arrayWithDeletion", "false");
function forInSum(_a) {
var s = '';
for (var i in _a)
s += _a[i];
return s;
}
shouldBe("forInSum([])", "''");
shouldBe("forInSum(Array())", "''");
shouldBe("forInSum(Array('a'))", "'a'");
var a0 = [];
shouldBe("forInSum(a0)", "''");
var a1 = [ 'a' ];
shouldBe("forInSum(a1)", "'a'");
shouldBe("String(['a', 'b', 'c'].splice(1, 2, 'x', 'y'))", "'b,c'");
var arr = new Array('a','b','c');
var propnames = [];
for (var i in arr)
propnames.push(i);
shouldBe("propnames.length","3");
shouldBe("propnames[0]","'0'");
shouldBe("propnames[1]","'1'");
shouldBe("propnames[2]","'2'");
var h = "a\xefc";
var u = "a\u1234c";
var z = "\x00";
shouldBe("h.charCodeAt(1)", "239");
shouldBe("u.charCodeAt(1)", "4660");
shouldBeTrue("isNaN(NaN)");
shouldBeTrue("isNaN('NaN')");
shouldBeFalse("isNaN('1')");
// all should return NaN because 1st char is non-number
shouldBe('isNaN(parseInt("Hello", 8))', "true");
shouldBe('isNaN(parseInt("FFF", 10))', "true");
shouldBe('isNaN(parseInt(".5", 10))', "true");
shouldBeTrue("isNaN(parseInt())");
shouldBeTrue("isNaN(parseInt(''))");
shouldBeTrue("isNaN(parseInt(' '))");
shouldBeTrue("isNaN(parseInt('a'))");
shouldBe("parseInt(1)", "1");
shouldBe("parseInt(1234567890123456)", "1234567890123456");
shouldBe("parseInt(1.2)", "1");
shouldBe("parseInt(' 2.3')", "2");
shouldBe("parseInt('0x10')", "16");
shouldBe("parseInt('11', 0)", "11");
shouldBe("parseInt('F', 16)", "15");
shouldBeTrue("isNaN(parseInt('10', 40))");
shouldBe("parseInt('3x')", "3");
shouldBe("parseInt('3 x')", "3");
shouldBeTrue("isNaN(parseInt('Infinity'))");
// all should return 15
shouldBe('parseInt("15")', "15");
shouldBe('parseInt("015")', "15"); // ES5 prohibits parseInt from handling octal, see annex E.
shouldBe('parseInt("0xf")', "15");
shouldBe('parseInt("15", 0)', "15");
shouldBe('parseInt("15", 10)', "15");
shouldBe('parseInt("F", 16)', "15");
shouldBe('parseInt("17", 8)', "15");
shouldBe('parseInt("15.99", 10)', "15");
shouldBe('parseInt("FXX123", 16)', "15");
shouldBe('parseInt("1111", 2)', "15");
shouldBe('parseInt("15*3", 10)', "15");
// this should be 0
shouldBe('parseInt("0x7", 10)', "0");
shouldBe('parseInt("1x7", 10)', "1");
shouldBeTrue("isNaN(parseFloat())");
shouldBeTrue("isNaN(parseFloat(''))");
shouldBeTrue("isNaN(parseFloat(' '))");
shouldBeTrue("isNaN(parseFloat('a'))");
shouldBe("parseFloat(1)", "1");
shouldBe("parseFloat(' 2.3')", "2.3");
shouldBe("parseFloat('3.1 x', 3)", "3.1");
shouldBe("parseFloat('3.1x', 3)", "3.1");
shouldBeFalse("delete NaN");
shouldBeFalse("delete Infinity");
shouldBeFalse("delete undefined");
shouldBe("isNaN(Number('a'))", "true");
shouldBe("isNaN(new Number('a'))", "true");
shouldBe("isNaN(Number.NaN)", "true");
shouldBe("Number.NEGATIVE_INFINITY", "-Infinity");
shouldBe("Number.POSITIVE_INFINITY", "Infinity");
shouldBe("(1).toString()", "'1'");
shouldBe("typeof (1).toString()", "'string'");
shouldBe("(10).toString(16)", "'a'");
shouldBe("(8.5).toString(16)", "'8.8'");
shouldBe("(-8.5).toString(16)", "'-8.8'");
shouldBe("Number.POSITIVE_INFINITY.toString(16)", "'Infinity'");
shouldBe("Number.NEGATIVE_INFINITY.toString(16)", "'-Infinity'");
shouldBe("Number.MAX_VALUE.toString(2).length", "23");
shouldBe("typeof 1", "'number'");
shouldBe("typeof (new Object())", "'object'");
shouldBe("var o = new Object(); o.x = 11; o.x;", "11");
shouldBe("typeof (new Object())", "'object'");
shouldBe("String(new Object())", "'[object Object]'");
shouldBe("(new Object()).toString()", "'[object Object]'");
function Square(x) {
this.x = x;
}
new Square(0); // create prototype
function Square_area() { return this.x * this.x; }
Square.prototype.area = Square_area;
var s = new Square(3);
shouldBe("s.area()", "9");
function Item(name) {
this.name = name;
}
function Book(name, author){
this.base = Item; // set Item constructor as method of Book object
this.base(name); // set the value of name property
this.author = author;
}
Book.prototype = new Item();
var b = new Book("a book", "Fred"); // create object instance
//edebug(e"b.name"));
shouldBe("b.name", "'a book'");
shouldBe("b.author", "'Fred'"); // outpus "Fred"
shouldBe("delete Array.prototype", "false");
shouldBe("var i = 1; i", "1");
shouldBe("j = k = 2", "2");
shouldBeUndefined("var i; i");
// compound assignments
shouldBe("var i = 1; i <<= 2", "4");
shouldBe("var i = 8; i >>= 1", "4");
shouldBe("var i = 1; i >>= 2", "0");
shouldBe("var i = -8; i >>= 24", "-1");
shouldBe("var i = 8; i >>>= 2", "2");
shouldBe("var i = -8; i >>>= 24", "255");
var i = 1;
function foo() {
i = 2;
return;
i = 3;
}
shouldBe("foo(), i", "2");
// value completions take precedence
var val = eval("11; { }");
shouldBe("val", "11");
val = eval("12; ;");
shouldBe("val", "12");
val = eval("13; if(false);");
shouldBe("val", "13");
val = eval("14; function f() {}");
shouldBe("val", "14");
val = eval("15; var v = 0");
shouldBe("val", "15");
shouldBe("true ? 1 : 2", "1");
shouldBe("false ? 1 : 2", "2");
shouldBe("'abc' ? 1 : 2", "1");
shouldBe("null ? 1 : 2", "2");
shouldBe("undefined ? 1 : 2", "2");
var asd = 1;
if ( undefined )
asd = 2;
shouldBe("/*var asd=1;if (undefined) asd = 2;*/ asd", "1");
shouldBe("a = 1; delete a;", "true");
shouldBe("delete nonexistant;", "true");
shouldBe("delete NaN", "false");
f = "global";
function test() {
shouldBeOfType("Function declaration takes effect at entry", f, "function");
for (var i = 0; i < 3; ++i) {
if (i == 0)
shouldBeOfType("Decl not yet overwritten", f, 'function');
else
shouldBeOfType("Decl already overwritten", f, 'number');
f = 3;
shouldBeVal("After assign ("+i+")", f, 3);
function f() {};
shouldBeVal("function decls have no execution content", f, 3);
f = 5;
shouldBeVal("After assign #2 ("+i+")", f, 5);
}
}
test();
var count = 0;
do {
count++;
} while (count < 10);
shouldBe("count", "10");
count = 0;
for (var i = 0; i < 10; i++) {
if (i == 5)
break;
count++;
}
shouldBe("count", "5");
count = 0;
for (i = 0; i < 10; i++) {
count++;
}
shouldBe("count", "10");
obj = new Object();
obj.a = 11;
obj.b = 22;
properties = "";
for ( prop in obj ) {
properties += (prop + "=" + obj[prop] + ";");
}
shouldBe("properties", "'a=11;b=22;'");
obj.y = 33;
obj.x = 44;
properties = "";
for ( prop in obj )
properties += prop;
arr = new Array;
arr[0] = 100;
arr[1] = 101;
list = "";
for ( var j in arr ) {
list += "[" + j + "]=" + arr[j] + ";";
}
shouldBe("list","'[0]=100;[1]=101;'");
list = "";
for (var a = [1,2,3], length = a.length, i = 0; i < length; i++) {
list += a[i];
}
shouldBe("list", "'123'");
var negativeZero = Math.atan2(-1, Infinity); // ### any nicer way?
function isNegativeZero(n) {
return n == 0 && 1 / n < 0;
}
// self tests
shouldBeTrue("isNegativeZero(negativeZero)");
shouldBeFalse("isNegativeZero(0)");
// Constants
shouldBe("String()+Math.E", "'2.718281828459045'");
shouldBe("String()+Math.LN2", "'0.6931471805599453'");
shouldBe("String()+Math.LN10", "'2.302585092994046'");
shouldBe("String()+Math.LOG2E", "'1.4426950408889634'");
shouldBe("String()+Math.LOG10E", "'0.4342944819032518'");
shouldBe("String()+Math.PI", "'3.141592653589793'");
shouldBe("String()+Math.SQRT1_2", "'0.7071067811865476'");
shouldBe("String()+Math.SQRT2", "'1.4142135623730951'");
shouldBe("String()+Number.NaN", "'NaN'");
shouldBe("String()+Number.NEGATIVE_INFINITY", "'-Infinity'");
shouldBe("String()+Number.POSITIVE_INFINITY", "'Infinity'");
shouldBe("Math.abs(-5)", "5");
shouldBe("Math.acos(0)", "Math.PI/2");
shouldBe("Math.acos(1)", "0");
shouldBe("Math.ceil(1.1)", "2");
shouldBe("String()+Math.sqrt(2)", "String()+Math.SQRT2");
shouldBe("Math.ceil(1.6)", "2");
shouldBe("Math.round(0)", "0");
shouldBeFalse("isNegativeZero(Math.round(0))");
shouldBeTrue("isNegativeZero(Math.round(negativeZero))");
shouldBe("Math.round(0.2)", "0");
shouldBeTrue("isNegativeZero(Math.round(-0.2))");
shouldBeTrue("isNegativeZero(Math.round(-0.5))");
shouldBe("Math.round(1.1)", "1");
shouldBe("Math.round(1.6)", "2");
shouldBe("Math.round(-3.5)", "-3");
shouldBe("Math.round(-3.6)", "-4");
shouldBeTrue("isNaN(Math.round())");
shouldBeTrue("isNaN(Math.round(NaN))");
shouldBe("Math.round(-Infinity)", "-Infinity");
shouldBe("Math.round(Infinity)", "Infinity");
shouldBe("Math.round(99999999999999999999.99)", "100000000000000000000");
shouldBe("Math.round(-99999999999999999999.99)", "-100000000000000000000");
shouldBe("Math.log(Math.E*Math.E)", "2");
shouldBeTrue("isNaN(Math.log(NaN))");
shouldBeTrue("isNaN(Math.log(-1))");
shouldBe("Math.log(1)", "0");
var obj = {};
obj.a = 1;
obj.b = 2;
list="";
for ( var i in obj ) { list += i + ','; }
shouldBe("list","'a,b,'");
list="";
for ( var i in Math ) { list += i + ','; }
shouldBe("list","''");
Math.myprop=true; // adding a custom property to the math object (why not?)
list="";
for ( var i in Math ) { list += i + ','; }
shouldBe("list","'myprop,'");
shouldBeTrue("!undefined");
shouldBeTrue("!null");
shouldBeTrue("!!true");
shouldBeTrue("!false");
shouldBeTrue("!!1");
shouldBeTrue("!0");
shouldBeTrue("!!'a'");
shouldBeTrue("!''");
// unary plus
shouldBe("+9", "9");
shouldBe("var i = 10; +i", "10");
// negation
shouldBe("-11", "-11");
shouldBe("var i = 12; -i", "-12");
// increment
shouldBe("var i = 0; ++i;", "1");
shouldBe("var i = 0; ++i; i", "1");
shouldBe("var i = 0; i++;", "0");
shouldBe("var i = 0; i++; i", "1");
shouldBe("var i = true; i++", "1");
shouldBe("var i = true; i++; i", "2");
// decrement
shouldBe("var i = 0; --i;", "-1");
shouldBe("var i = 0; --i; i", "-1");
shouldBe("var i = 0; i--;", "0");
shouldBe("var i = 0; i--; i", "-1");
shouldBe("var i = true; i--", "1");
shouldBe("var i = true; i--; i", "0");
var one = 1;
var two = 2;
var twentyFour = 24;
shouldBe("1 << two", "4");
shouldBe("8 >> one", "4");
shouldBe("1 >> two", "0");
shouldBe("-8 >> twentyFour", "-1");
shouldBe("8 >>> two", "2");
shouldBe("-8 >>> twentyFour", "255");
shouldBe("(-2200000000 >> one) << one", "2094967296");
shouldBe("Infinity >> one", "0");
shouldBe("Infinity << one", "0");
shouldBe("Infinity >>> one", "0");
shouldBe("NaN >> one", "0");
shouldBe("NaN << one", "0");
shouldBe("NaN >>> one", "0");
shouldBe("888.1 >> one", "444");
shouldBe("888.1 << one", "1776");
shouldBe("888.1 >>> one", "444");
shouldBe("888.9 >> one", "444");
shouldBe("888.9 << one", "1776");
shouldBe("888.9 >>> one", "444");
shouldBe("Math.pow(2, 32) >> one", "0");
shouldBe("Math.pow(2, 32) << one", "0");
shouldBe("Math.pow(2, 32) >>> one", "0");
// addition
shouldBe("1+2", "3");
shouldBe("'a'+'b'", "'ab'");
shouldBe("'a'+2", "'a2'");
shouldBe("'2'+'-1'", "'2-1'");
shouldBe("true+'a'", "'truea'");
shouldBe("'a' + null", "'anull'");
shouldBe("true+1", "2");
shouldBe("false+null", "0");
// substraction
shouldBe("1-3", "-2");
shouldBe("isNaN('a'-3)", "true");
shouldBe("'3'-'-1'", "4");
shouldBe("'4'-2", "2");
shouldBe("true-false", "1");
shouldBe("false-1", "-1");
shouldBe("null-true", "-1");
// multiplication
shouldBe("2 * 3", "6");
shouldBe("true * 3", "3");
shouldBe("2 * '3'", "6");
// division
shouldBe("6 / 4", "1.5");
//shouldBe("true / false", "Inf");
shouldBe("'6' / '2'", "3");
shouldBeTrue("isNaN('x' / 1)");
shouldBeTrue("isNaN(1 / NaN)");
shouldBeTrue("isNaN(Infinity / Infinity)");
shouldBe("Infinity / 0", "Infinity");
shouldBe("-Infinity / 0", "-Infinity");
shouldBe("Infinity / 1", "Infinity");
shouldBe("-Infinity / 1", "-Infinity");
shouldBeTrue("1 / Infinity == +0");
shouldBeTrue("1 / -Infinity == -0"); // how to check ?
shouldBeTrue("isNaN(0/0)");
shouldBeTrue("0 / 1 === 0");
shouldBeTrue("0 / -1 === -0"); // how to check ?
shouldBe("1 / 0", "Infinity");
shouldBe("-1 / 0", "-Infinity");
// modulo
shouldBe("6 % 4", "2");
shouldBe("'-6' % 4", "-2");
shouldBe("2==2", "true");
shouldBe("1==2", "false");
shouldBe("1<2", "true");
shouldBe("1<=2", "true");
shouldBe("2<1", "false");
shouldBe("2<=1", "false");
shouldBe("2>1", "true");
shouldBe("2>=1", "true");
shouldBe("1>=2", "false");
shouldBe("1>2", "false");
shouldBeTrue("'abc' == 'abc'");
shouldBeTrue("'abc' != 'xyz'");
shouldBeTrue("true == true");
shouldBeTrue("false == false");
shouldBeTrue("true != false");
shouldBeTrue("'a' != null");
shouldBeTrue("'a' != undefined");
shouldBeTrue("null == null");
shouldBeTrue("null == undefined");
shouldBeTrue("undefined == undefined");
shouldBeTrue("NaN != NaN");
shouldBeTrue("true != undefined");
shouldBeTrue("true != null");
shouldBeTrue("false != undefined");
shouldBeTrue("false != null");
shouldBeTrue("'0' == 0");
shouldBeTrue("1 == '1'");
shouldBeTrue("NaN != NaN");
shouldBeTrue("NaN != 0");
shouldBeTrue("NaN != undefined");
shouldBeTrue("true == 1");
shouldBeTrue("true != 2");
shouldBeTrue("1 == true");
shouldBeTrue("false == 0");
shouldBeTrue("0 == false");
shouldBe("'abc' < 'abx'", "true");
shouldBe("'abc' < 'abcd'", "true");
shouldBe("'abc' < 'abc'", "false");
shouldBe("'abcd' < 'abcd'", "false");
shouldBe("'abx' < 'abc'", "false");
shouldBe("'abc' <= 'abc'", "true");
shouldBe("'abc' <= 'abx'", "true");
shouldBe("'abx' <= 'abc'", "false");
shouldBe("'abcd' <= 'abc'", "false");
shouldBe("'abc' <= 'abcd'", "true");
shouldBe("'abc' > 'abx'", "false");
shouldBe("'abc' > 'abc'", "false");
shouldBe("'abcd' > 'abc'", "true");
shouldBe("'abx' > 'abc'", "true");
shouldBe("'abc' > 'abcd'", "false");
shouldBe("'abc' >= 'abc'", "true");
shouldBe("'abcd' >= 'abc'", "true");
shouldBe("'abx' >= 'abc'", "true");
shouldBe("'abc' >= 'abx'", "false");
shouldBe("'abc' >= 'abx'", "false");
shouldBe("'abc' >= 'abcd'", "false");
shouldBeFalse("'abc' <= 0"); // #35246
shouldBeTrue("'' <= 0");
shouldBeTrue("' ' <= 0");
shouldBeTrue("null <= 0");
shouldBeFalse("0 <= 'abc'");
shouldBeTrue("0 <= ''");
shouldBeTrue("0 <= null");
shouldBeTrue("null <= null");
shouldBeTrue("6 < '52'");
shouldBeTrue("6 < '72'"); // #36087
shouldBeFalse("NaN < 0");
shouldBeFalse("NaN <= 0");
shouldBeFalse("NaN > 0");
shouldBeFalse("NaN >= 0");
// strict comparison ===
shouldBeFalse("0 === false");
shouldBeTrue("null === null");
shouldBeFalse("NaN === NaN");
shouldBeTrue("0.0 === 0");
shouldBeTrue("'abc' === 'abc'");
shouldBeFalse("'a' === 'x'");
shouldBeFalse("1 === '1'");
shouldBeFalse("'1' === 1");
shouldBeTrue("true === true");
shouldBeTrue("false === false");
shouldBeFalse("true === false");
shouldBeTrue("Math === Math");
shouldBeFalse("Math === Boolean");
shouldBeTrue("Infinity === Infinity");
shouldBe("0 !== 0", "false");
shouldBe("0 !== 1", "true");
shouldBe("typeof undefined", "'undefined'");
shouldBe("typeof null", "'object'");
shouldBe("typeof true", "'boolean'");
shouldBe("typeof false", "'boolean'");
shouldBe("typeof 1", "'number'");
shouldBe("typeof 'a'", "'string'");
shouldBe("typeof shouldBe", "'function'");
shouldBe("typeof Number.NaN", "'number'");
shouldBe("11 && 22", "22");
shouldBe("null && true", "null");
shouldBe("11 || 22", "11");
shouldBe("null || 'a'", "'a'");
shouldBeUndefined("void 1");
shouldBeTrue("1 in [1, 2]");
shouldBeFalse("3 in [1, 2]");
shouldBeTrue("'a' in { a:1, b:2 }");
// Make sure that for ... in reevaluates the scoping every time!
var P = { foo : 1, bar : 2, baz : 3 };
function testForIn() {
for (g in P) {
eval("var g;"); //Change the scope of g half-ways through the loop
}
}
testForIn();
shouldBe("g", "'foo'"); //Before the eval, g was in outer scope, but not after!
function testSwitch(v) {
var result = "";
switch (v) {
case 0: result += 'a';
case 1: result += 'b';
case 1: result += 'c';
case 2: result += 'd'; break;
}
return result;
}
shouldBe("testSwitch(0)", "'abcd'");
shouldBe("testSwitch(1)", "'bcd'"); // IE agrees, NS disagrees
shouldBe("testSwitch(2)", "'d'");
shouldBe("testSwitch(false)", "''");
function testSwitch4(v) {
var result = "";
switch (v) {
case 0: result += 'a'; result += 'b'; break;
}
return result;
};
shouldBe("testSwitch4(0)", "'ab'");
var myvar = 1;
function varInFunction() {
return (myvar == undefined);
var myvar = 2;
}
function varInVarList() {
return (myvar == undefined);
var a = 1, b = 0, myvar = 2;
}
function varListOrder() {
var tmp = 0;
var i = ++tmp, j = ++tmp;
return j == 2;
}
function varInBlock() {
return (myvar == undefined);
{
var myvar = 2;
}
}
function varInIf() {
return (myvar == undefined);
if (false)
var myvar = 2;
}
function varInElse() {
return (myvar == undefined);
if (true) {
}
else
var myvar = 2;
}
function varInDoWhile() {
return (myvar == undefined);
do
var myvar = 2;
while (false);
}
function varInWhile() {
return (myvar == undefined);
while (false)
var myvar = 2;
}
function varInFor() {
return (myvar == undefined);
var i;
for (i = 0; i < 0; i++)
var myvar = 2;
}
function varInForInitExpr() {
return (myvar == undefined);
for (var myvar = 2; i < 2; i++) {
}
}
function varInWith() {
return (myvar == undefined);
with (String)
var myvar = 2;
}
function varInCase() {
return (myvar == undefined);
switch (1) {
case 0: var myvar = 2;
case 1:
}
}
if (!varGlobal)
var varGlobal = 1;
shouldBe("varInFunction()","true");
shouldBe("varInVarList()","true");
shouldBe("varListOrder()","true");
shouldBe("varInBlock()","true");
shouldBe("varInIf()","true");
shouldBe("varInElse()","true");
shouldBe("varInDoWhile()","true");
shouldBe("varInWhile()","true");
shouldBe("varInFor()","true");
shouldBe("varInWith()","true");
shouldBe("varInCase()","true");
shouldBe("varInForInitExpr()","true");
shouldBe("varGlobal", "1");
var overrideVar = 1;
var overrideVar;
shouldBe("overrideVar", "1");
var overrideVar2 = 1;
var overrideVar2 = 2;
shouldBe("overrideVar2", "2");
console.log(testsPass+" out of "+tests+" passed");
result = testsPass == tests;