Permalink
Browse files

small fixes to core, updated tweenjs wrapper, updated blockly wrapper.

updated the threejs blockly demo with animated translations and colors using tween.js
  • Loading branch information...
hartsantler committed Oct 25, 2013
1 parent fa4c513 commit 5b9de1fed9481d8aeea32a7f4d4aa1c891879f26
View
@@ -49,11 +49,28 @@ def initialize_blockly( blockly_id='blocklyDiv', toolbox_id='toolbox', on_change
e.appendChild(v)
nb = document.createElement('block')
if input['default_value'] is not None:
nb.setAttribute('type', 'math_number') ## TODO support other types
t = document.createElement('title')
t.setAttribute('name', 'NUM')
t.appendChild( document.createTextNode(input['default_value']) )
nb.appendChild(t)
default_value = input['default_value']
if typeof(default_value) == 'boolean':
nb.setAttribute('type', 'logic_boolean')
t = document.createElement('title')
t.setAttribute('name', 'BOOL')
## Blockly is picky about these keywords, passing "True" or "true" will show 'true' in the UI but give you False for the actual value!
if default_value:
t.appendChild( document.createTextNode('TRUE') )
else:
t.appendChild( document.createTextNode('FALSE') )
nb.appendChild(t)
else:
nb.setAttribute('type', 'math_number') ## TODO support other types
t = document.createElement('title')
t.setAttribute('name', 'NUM')
t.appendChild( document.createTextNode(default_value) )
nb.appendChild(t)
elif input['name'].startswith('color'): ## this is hackish, but it works
nb.setAttribute('type', 'colour_picker')
else:
nb.setAttribute('type', 'logic_null')
v.appendChild(nb)
@@ -129,7 +146,10 @@ def javascript_callback(self, jsfunc): ## decorator
defs = jsfunc.kwargs_signature
for i in range(arr.length):
name = arr[i]
self.add_input_value( name, default_value=defs[name] )
if defs[name] is null: ## special case: null creates a non-dynamic "slot" input statement
self.add_input_statement( name )
else:
self.add_input_value( name, default_value=defs[name] )
return jsfunc
def callback(self, jsfunc): ## decorator
@@ -315,35 +335,58 @@ def generator(block):
args.push( a )
i += 1
## input statements are used for dynamic updates
if block.pythonjs_object:
if block.pythonjs_object: ## input statements are used for dynamic updates
wrapper = block.pythonjs_object[...]
print 'block.pythonjs_object.wrapper', wrapper
i = 0
while i < input_statements.length:
input = input_statements[i][...]
attr = wrapper[ input['name'] ]
if attr:
js = Blockly.JavaScript.statementToCode(block, input['name'])
if input['callback']:
input['callback']( wrapper, attr, eval(js) )
else:
print 'ERROR - input is missing callback', input
print('block.pythonjs_object - update-dynamic: code to eval')
print(js)
if js.length:
if input['callback']:
print 'callback', input['callback'].NAME
input['callback']( wrapper, attr, eval(js) )
else:
print 'ERROR - input is missing callback', input
i += 1
i = 0
while i < input_slots.length:
input = input_slots[i][...]
a = Blockly.Python.statementToCode(block, input['name'])
if a.length:
args.push(input['name'] + '=' +a)
i += 1
if external_javascript_function:
i = 0
while i < input_slots.length:
input = input_slots[i][...]
a = Blockly.JavaScript.statementToCode(block, input['name'])
if a.length:
args.push( a )
else:
args.push( "null" )
i += 1
else:
i = 0
while i < input_slots.length:
input = input_slots[i][...]
a = Blockly.Python.statementToCode(block, input['name'])
if a.length:
args.push(input['name'] + '=' +a)
i += 1
if external_function:
code += external_function + '(' + ','.join(args) + ')'
elif external_javascript_function:
## TODO what about pure javascript functions?
if is_statement and block.parentBlock_: ## TODO request Blockly API change: "parentBlock_" to "parentBlock"
print 'is_statement with parent block - OK'
code += external_javascript_function + '( [' + ','.join(args) + '] )' ## calling from js a pyjs function
print code
elif block.parentBlock_: ## TODO request Blockly API change: "parentBlock_" to "parentBlock"
print 'with parent block - OK'
code += external_javascript_function + '( [' + ','.join(args) + '] )' ## calling from js a pyjs function
print code
else: ## TODO this should be a simple series of statements?
for a in args:
View
@@ -35,38 +35,83 @@ def update(self):
class Tween:
'''
This wrapper class for TWEEN.Tween is slightly different that the Tween.js API,
time is given in seconds, and the callbacks: on_start, on_complete, and on_update
time is given in seconds, the callbacks: on_start, on_complete, and on_update
will pass as the first argument this high level wrapper self, and source,
note that source is not the raw javascript object. Otherwise the API is the same.
note that source is not the raw javascript object. A Tween can be initialized
without a source, and then later set_source can be called. It is safe to set
a target before setting a source as well, using set_target.
A helper method retarget, is provided that can change the target of this Tween
while keeping your callbacks intact. You only call retarget when this Tween is
currently active. This solves a problem of the Tween.js API where dynamically
setting the target inplace only works if the values are greater than the state
of the current source or greater than the previous target.
Note:
Notes:
on_start_js, on_update_js, on_complete_js
these should only be used if you know what you are doing.
the tween.js API will not allow changing the tween duration time
after tween.to has been called. Can we call tween.to multiple times?
'''
def __init__(self, ob, on_start=None, on_update=None, on_complete=None, on_start_js=None, on_update_js=None, on_complete_js=None ):
print 'tween.init', ob, ob[...]
self.source = ob
def __init__(self, source=None, on_start=None, on_update=None, seconds=None, delay=None, repeat=None, yoyo=False, on_complete=None, on_start_js=None, on_update_js=None, on_complete_js=None ):
self.source = source
self.source_is_raw = False
self.on_start = on_start
self.on_update = on_update
self.on_complete = on_complete
self.seconds = None
self.seconds_remaining = None
self._on_start_js = on_start_js
self._on_update_js = on_update_js
self._on_complete_js = on_complete_js
self.seconds = seconds
self.seconds_remaining = seconds
self.active = True
self.started = False
self.target = None
self.target_is_raw = False
self.target_armed = False
self.delay = delay
self.repeat = repeat
self.yoyo = yoyo
if source:
self.set_source( source )
def set_source(self, source=None, source_js=None):
'''
Sometimes we need to create the Tween without a source, and then later set the source,
for example: this helps with integration with GoogleBlockly where we need to create the
Tween instance first with a target and later set a source.
Use source_js to pass a raw javascript object to use as source, only use this if you
know what your doing.
'''
if source:
self.source = source
source = source[...] ## unwrap
elif source_js:
self.source = source_js
self.source_is_raw = True
source = source_js
else:
raise TypeError
print '--tween set_source', source
on_start_method = self._on_start
on_update_method = self._on_update
on_complete_method = self._on_complete
on_start_js = self._on_start_js
on_update_js = self._on_update_js
on_complete_js = self._on_complete_js
with javascript:
tween = new( TWEEN.Tween( ob[...] ) )
tween = new( TWEEN.Tween( source ) )
if on_start_js:
tween.onStart( on_start_js )
else:
@@ -82,9 +127,40 @@ def __init__(self, ob, on_start=None, on_update=None, on_complete=None, on_start
else:
tween.onComplete( lambda : on_complete_method([this]) )
##print 'tween._valuesStart', tween._valuesStart ## this variable is really private
self[...] = tween
if self.target and not self.target_armed and self.seconds:
if self.target_is_raw:
self.to( target_js=self.target, seconds=self.seconds )
else:
self.to( target=self.target, seconds=self.seconds )
def set_target(self, target=None, target_js=None ):
if target:
self.target = target
elif target_js:
self.target = target_js
self.target_is_raw = True
else:
raise TypeError
if self.target_armed: ## redirect target
if self.target_is_raw:
self.to( target_js=self.target, seconds=self.seconds )
else:
self.to( target=self.target, seconds=self.seconds )
def set_seconds(self, seconds=1.0):
self.seconds = seconds
self.seconds_remaining = seconds
if self.started and self.target:
if self.target_is_raw:
self.to( target_js=self.target, seconds=seconds)
else:
self.to( target=self.target, seconds=seconds)
#############################################
def _on_start(self, jsob):
print '-on-start', jsob
@@ -94,7 +170,12 @@ def _on_start(self, jsob):
def _on_update(self, jsob, delta):
print '-on-update', jsob, delta
if Number.isNaN(delta):
TweenManager.paused = True
raise TypeError
self.seconds_remaining = self.seconds - (self.seconds * delta)
if self.on_update:
self.on_update( self, self.source, delta )
@@ -106,15 +187,39 @@ def _on_complete(self, jsob):
#############################################
def to(self, vec, seconds=1.0):
def to(self, target=None, target_js=None, seconds=1.0):
print 'TWEEN.TO', target, target_js, seconds
if seconds is None:
raise TypeError
self.seconds = seconds
self.seconds_remaining = seconds
print '--tween.to', vec, vec[...]
if target:
self.target = target
target = target[...]
elif target_js:
self.target = target_js
self.target_is_raw = True
target = target_js
else:
raise TypeError
self.target_armed = True
with javascript:
self[...].to( vec[...], seconds*1000 )
self[...].to( target, seconds*1000 )
def start(self):
print '--starting tweeen'
## set these in case they were set from __init__
if self.yoyo: self.set_yoyo( self.yoyo )
if self.delay: self.set_delay( self.delay )
if self.repeat: self.set_repeat( self.repeat )
if self.target and not self.target_armed:
if self.target_is_raw:
self.to( target_js=self.target, seconds=self.seconds )
else:
self.to( target=self.target, seconds=self.seconds )
with javascript:
self[...].start()
@@ -125,23 +230,27 @@ def stop(self):
with javascript:
self[...].stop()
def delay(self, seconds):
def set_delay(self, seconds):
self.delay = seconds
with javascript:
self[...].delay( seconds*1000 )
def repeat(self, amount):
def set_repeat(self, amount):
self.repeat = amount
with javascript:
self[...].repeat( amount )
def yoyo(self, yoyo):
def set_yoyo(self, yoyo):
self.yoyo = yoyo
with javascript:
self[...].yoyo( yoyo )
def easing(self, easing ):
## TODO test these
def set_easing(self, easing ):
with javascript:
self[...].easing( easing )
def interpolation(self, interp):
def set_interpolation(self, interp):
with javascript:
self[...].interpolation( interp )
View
@@ -1,4 +1,4 @@
// PythonScript Runtime - regenerated on: Thu Oct 24 02:55:58 2013
// PythonScript Runtime - regenerated on: Fri Oct 25 00:18:26 2013
var jsrange = function(num) {
"Emulates Python's range function";
var i, r;
@@ -874,7 +874,7 @@ def visit_Call(self, node):
a = ','.join(args)
return '%s( %s )' %( self.visit(node.func), a )
if isinstance(node.func, Name) and node.func.id in ('JS', 'toString', 'JSObject', 'JSArray', 'var', 'instanceof'):
if isinstance(node.func, Name) and node.func.id in ('JS', 'toString', 'JSObject', 'JSArray', 'var', 'instanceof', 'typeof'):
args = list( map(self.visit, node.args) ) ## map in py3 returns an iterator not a list
if node.func.id == 'var':
for k in node.keywords:
@@ -943,7 +943,15 @@ def visit_Call(self, node):
elif name in self._classes or name in self._builtins:
return 'get_attribute(%s, "__call__")( JSArray(), JSObject() )' %name
else: ## this could be a dangerous optimization ##
else:
## this a slightly dangerous optimization,
## because if the user is trying to create an instance of some class
## and that class is define in an external binding,
## and they forgot to put "from mylibrary import *" in their script (an easy mistake to make)
## then this fails to call __call__ to initalize the instance,
## and throws a confusing error:
## Uncaught TypeError: Property 'SomeClass' of object [object Object] is not a function
## TODO - remove this optimization, or provide the user with a better error message.
return '%s( JSArray(), JSObject() )' %name
def visit_Lambda(self, node):
Oops, something went wrong.

0 comments on commit 5b9de1f

Please sign in to comment.