-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathPrimitives.as
More file actions
209 lines (184 loc) · 8.12 KB
/
Primitives.as
File metadata and controls
209 lines (184 loc) · 8.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
* Scratch Project Editor and Player
* Copyright (C) 2014 Massachusetts Institute of Technology
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Primitives.as
// John Maloney, April 2010
//
// Miscellaneous primitives. Registers other primitive modules.
// Note: A few control structure primitives are implemented directly in Interpreter.as.
package primitives {
import flash.utils.Dictionary;
import blocks.*;
import interpreter.*;
import scratch.ScratchSprite;
import translation.Translator;
public class Primitives {
private const MaxCloneCount:int = 300;
protected var app:Scratch;
protected var interp:Interpreter;
private var counter:int;
public function Primitives(app:Scratch, interpreter:Interpreter) {
this.app = app;
this.interp = interpreter;
}
public function addPrimsTo(primTable:Dictionary):void {
// operators
primTable["+"] = function(b:*):* { return interp.numarg(b, 0) + interp.numarg(b, 1) };
primTable["-"] = function(b:*):* { return interp.numarg(b, 0) - interp.numarg(b, 1) };
primTable["*"] = function(b:*):* { return interp.numarg(b, 0) * interp.numarg(b, 1) };
primTable["/"] = function(b:*):* { return interp.numarg(b, 0) / interp.numarg(b, 1) };
primTable["randomFrom:to:"] = primRandom;
primTable["<"] = function(b:*):* { return compare(interp.arg(b, 0), interp.arg(b, 1)) < 0 };
primTable["="] = function(b:*):* { return compare(interp.arg(b, 0), interp.arg(b, 1)) == 0 };
primTable[">"] = function(b:*):* { return compare(interp.arg(b, 0), interp.arg(b, 1)) > 0 };
primTable["&"] = function(b:*):* { return interp.arg(b, 0) && interp.arg(b, 1) };
primTable["|"] = function(b:*):* { return interp.arg(b, 0) || interp.arg(b, 1) };
primTable["not"] = function(b:*):* { return !interp.arg(b, 0) };
primTable["abs"] = function(b:*):* { return Math.abs(interp.numarg(b, 0)) };
primTable["sqrt"] = function(b:*):* { return Math.sqrt(interp.numarg(b, 0)) };
primTable["concatenate:with:"] = function(b:*):* { return ("" + interp.arg(b, 0) + interp.arg(b, 1)).substr(0, 10240); };
primTable["letter:of:"] = primLetterOf;
primTable["stringLength:"] = function(b:*):* { return String(interp.arg(b, 0)).length };
primTable["%"] = primModulo;
primTable["rounded"] = function(b:*):* { return Math.round(interp.numarg(b, 0)) };
primTable["computeFunction:of:"] = primMathFunction;
// clone
primTable["createCloneOf"] = primCreateCloneOf;
primTable["deleteClone"] = primDeleteClone;
primTable["whenCloned"] = interp.primNoop;
// testing (for development)
primTable["NOOP"] = interp.primNoop;
primTable["COUNT"] = function(b:*):* { return counter };
primTable["INCR_COUNT"] = function(b:*):* { counter++ };
primTable["CLR_COUNT"] = function(b:*):* { counter = 0 };
new LooksPrims(app, interp).addPrimsTo(primTable);
new MotionAndPenPrims(app, interp).addPrimsTo(primTable);
new SoundPrims(app, interp).addPrimsTo(primTable);
new VideoMotionPrims(app, interp).addPrimsTo(primTable);
addOtherPrims(primTable);
}
protected function addOtherPrims(primTable:Dictionary):void {
new SensingPrims(app, interp).addPrimsTo(primTable);
new ListPrims(app, interp).addPrimsTo(primTable);
primTable[">"] = function(b:*){return primTable["doIf"]};
}
private function primRandom(b:Block):Number {
var n1:Number = interp.numarg(b, 0);
var n2:Number = interp.numarg(b, 1);
var low:Number = (n1 <= n2) ? n1 : n2;
var hi:Number = (n1 <= n2) ? n2 : n1;
if (low == hi) return low;
// if both low and hi are ints, truncate the result to an int
var ba1:BlockArg = b.args[0] as BlockArg;
var ba2:BlockArg = b.args[1] as BlockArg;
var int1:Boolean = ba1 ? ba1.numberType == BlockArg.NT_INT : int(n1) == n1;
var int2:Boolean = ba2 ? ba2.numberType == BlockArg.NT_INT : int(n2) == n2;
if (int1 && int2)
return low + int(Math.random() * ((hi + 1) - low));
return (Math.random() * (hi - low)) + low;
}
private function primLetterOf(b:Block):String {
var s:String = interp.arg(b, 1);
var i:int = interp.numarg(b, 0) - 1;
if ((i < 0) || (i >= s.length)) return "";
return s.charAt(i);
}
private function primModulo(b:Block):Number {
var n:Number = interp.numarg(b, 0);
var modulus:Number = interp.numarg(b, 1);
var result:Number = n % modulus;
if (result / modulus < 0) result += modulus;
return result;
}
private function primMathFunction(b:Block):Number {
var op:* = interp.arg(b, 0);
var n:Number = interp.numarg(b, 1);
switch(op) {
case "abs": return Math.abs(n);
case "floor": return Math.floor(n);
case "ceiling": return Math.ceil(n);
case "int": return n - (n % 1); // used during alpha, but removed from menu
case "sqrt": return Math.sqrt(n);
case "sin": return Math.sin((Math.PI * n) / 180);
case "cos": return Math.cos((Math.PI * n) / 180);
case "tan": return Math.tan((Math.PI * n) / 180);
case "asin": return (Math.asin(n) * 180) / Math.PI;
case "acos": return (Math.acos(n) * 180) / Math.PI;
case "atan": return (Math.atan(n) * 180) / Math.PI;
case "ln": return Math.log(n);
case "log": return Math.log(n) / Math.LN10;
case "e ^": return Math.exp(n);
case "10 ^": return Math.pow(10, n);
}
return 0;
}
private static const emptyDict:Dictionary = new Dictionary();
private static var lcDict:Dictionary = new Dictionary();
public static function compare(a1:*, a2:*):int {
// This is static so it can be used by the list "contains" primitive.
var n1:Number = Interpreter.asNumber(a1);
var n2:Number = Interpreter.asNumber(a2);
// X != X is faster than isNaN()
if (n1 != n1 || n2 != n2) {
// Suffix the strings to avoid properties and methods of the Dictionary class (constructor, hasOwnProperty, etc)
if (a1 is String && emptyDict[a1]) a1 += '_';
if (a2 is String && emptyDict[a2]) a2 += '_';
// at least one argument can't be converted to a number: compare as strings
var s1:String = lcDict[a1];
if(!s1) s1 = lcDict[a1] = String(a1).toLowerCase();
var s2:String = lcDict[a2];
if(!s2) s2 = lcDict[a2] = String(a2).toLowerCase();
return s1.localeCompare(s2);
} else {
// compare as numbers
if (n1 < n2) return -1;
if (n1 == n2) return 0;
if (n1 > n2) return 1;
}
return 1;
}
private function primCreateCloneOf(b:Block):void {
var objName:String = interp.arg(b, 0);
var proto:ScratchSprite = app.stagePane.spriteNamed(objName);
if ('_myself_' == objName) proto = interp.activeThread.target;
if (!proto) return;
if (app.runtime.cloneCount > MaxCloneCount) return;
var clone:ScratchSprite = new ScratchSprite();
if (proto.parent == app.stagePane)
app.stagePane.addChildAt(clone, app.stagePane.getChildIndex(proto));
else
app.stagePane.addChild(clone);
clone.initFrom(proto, true);
clone.objName = proto.objName;
clone.isClone = true;
for each (var stack:Block in clone.scripts) {
if (stack.op == "whenCloned") {
interp.startThreadForClone(stack, clone);
}
}
app.runtime.cloneCount++;
}
private function primDeleteClone(b:Block):void {
var clone:ScratchSprite = interp.targetSprite();
if ((clone == null) || (!clone.isClone) || (clone.parent == null)) return;
if (clone.bubble && clone.bubble.parent) clone.bubble.parent.removeChild(clone.bubble);
clone.parent.removeChild(clone);
app.interp.stopThreadsFor(clone);
app.runtime.cloneCount--;
}
}}