Skip to content

Commit 3d171be

Browse files
committed
Fully capabable launcher
1 parent 1340199 commit 3d171be

File tree

4 files changed

+132
-35
lines changed

4 files changed

+132
-35
lines changed

base.py

+114-28
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/usr/bin/env python3
22

3-
from math import pi, sin, cos
3+
from math import pi, sin, cos, radians, hypot
44
from random import random, randint
5+
from collections import defaultdict
56

7+
from webcolors import hex_to_rgb
68
from hotqueue import HotQueue
79

810
import sys
@@ -36,6 +38,10 @@
3638
""")
3739
exit(0)
3840

41+
X_EXTENT = 32
42+
Y_EXTENT = 24
43+
Z_EXTENT = 18
44+
3945
def makeBoundaryBox(render, world):
4046
boundaryNode = BulletRigidBodyNode("Boundary")
4147
boundaryNode.setFriction(0)
@@ -47,12 +53,12 @@ def makeBoundaryBox(render, world):
4753
cubeNp.attachNewNode(cubeNode)
4854

4955
for pos, shape in (
50-
((0, 0, -18), Vec3(32, 24, 1)),
51-
((0, 0, 18), Vec3(32, 24, 1)),
52-
((0, -24, 0), Vec3(32, 1, 18)),
53-
((0, 24, 0), Vec3(32, 1, 18)),
54-
((-32, 0, 0), Vec3( 1, 24, 18)),
55-
(( 32, 0, 0), Vec3( 1, 24, 18)),
56+
((0, 0, -Z_EXTENT), Vec3(X_EXTENT, Y_EXTENT, 1)),
57+
((0, 0, Z_EXTENT), Vec3(X_EXTENT, Y_EXTENT, 1)),
58+
((0, -Y_EXTENT, 0), Vec3(X_EXTENT, 1, Z_EXTENT)),
59+
((0, Y_EXTENT, 0), Vec3(X_EXTENT, 1, Z_EXTENT)),
60+
((-X_EXTENT, 0, 0), Vec3( 1, Y_EXTENT, Z_EXTENT)),
61+
(( X_EXTENT, 0, 0), Vec3( 1, Y_EXTENT, Z_EXTENT)),
5662
):
5763
boundaryShape = BulletBoxShape(shape)
5864
boundaryNode.addShape(boundaryShape,
@@ -81,9 +87,12 @@ def __init__(self, parent, world, font=None):
8187
m[1][1] = 0.1
8288
self.textNode.setTransform(m)
8389

90+
self.halfExtents = (0, 0, 0)
91+
8492
self.rbNode = BulletRigidBodyNode("Text")
8593
self.rootNp = parent.attachNewNode(self.rbNode)
8694
self.textNp = self.rootNp.attachNewNode(self.textNode)
95+
self.textNp.setScale(1.8)
8796

8897
self.rbNode.setFriction(0)
8998
self.rbNode.setRestitution(1)
@@ -93,19 +102,32 @@ def __init__(self, parent, world, font=None):
93102
self.rbNode.setDeactivationEnabled(False)
94103
self.rbNode.setKinematic(True)
95104

96-
world.attachRigidBody(self.rbNode)
105+
world.attach(self.rbNode)
106+
self.world = world
107+
108+
def destroy(self):
109+
self.rootNp.detachNode()
110+
self.world.remove(self.rbNode)
97111

98112
def setText(self, value):
113+
if hasattr(self, "bulletShape") and self.bulletShape:
114+
self.rbNode.removeShape(self.bulletShape)
115+
self.bulletShape = None
116+
99117
self.textNode.setText(value)
118+
self.textNp.setPos(0, 0, 0)
119+
self.halfExtents = (0, 0, 0)
100120

101-
ul, lr = self.textNp.getTightBounds()
102-
halfExtents = (lr - ul) / 2
103-
self.textNp.setPos(-halfExtents - ul)
121+
if self.textNp.getTightBounds():
122+
ul, lr = self.textNp.getTightBounds()
123+
self.halfExtents = (lr - ul) / 2
124+
self.textNp.setPos(-self.halfExtents - ul)
125+
126+
self.bulletShape = BulletBoxShape(self.halfExtents)
127+
self.rbNode.addShape(self.bulletShape)
104128

105-
if hasattr(self, "bulletShape"):
106-
self.rbNode.removeShape(self.bulletShape)
107-
self.bulletShape = BulletBoxShape(halfExtents)
108-
self.rbNode.addShape(self.bulletShape)
129+
def getHalfExtents(self):
130+
return self.halfExtents
109131

110132
def setColor(self, color):
111133
self.textNp.setColor(color)
@@ -116,6 +138,12 @@ def setPos(self, pos):
116138
def setHpr(self, hpr):
117139
self.rootNp.setHpr(hpr)
118140

141+
def getPos(self):
142+
return self.rootNp.getPos()
143+
144+
def getHpr(self):
145+
return self.rootNp.getHpr()
146+
119147
def launch(self, linear, angular):
120148
self.rbNode.setKinematic(False)
121149
self.rbNode.setLinearVelocity(linear)
@@ -162,12 +190,12 @@ def __init__(self):
162190

163191
self.textNp = self.render.attachNewNode("TextNodes")
164192

165-
light = makeLight(2)
193+
light = makeLight(1)
166194
lightNp = render.attachNewNode(light)
167195
lightNp.setPos(24, -30, 12)
168196
self.textNp.setLight(lightNp)
169197

170-
light = makeLight(2)
198+
light = makeLight(1)
171199
lightNp = render.attachNewNode(light)
172200
lightNp.setPos(-24, -30, -12)
173201
self.textNp.setLight(lightNp)
@@ -189,6 +217,10 @@ def __init__(self):
189217
random() * 10 - 5)))
190218
self.accept('l', self.sampleLaunch)
191219

220+
self.launchers = defaultdict(
221+
lambda: LaunchableText(self.textNp, self.world, self.font))
222+
self.floaters = []
223+
192224
self.targets = []
193225
#self.createTarget("A", -2, 5, -1)
194226

@@ -232,27 +264,81 @@ def toggleGravity(self):
232264
print("New Gravity: ", self.world.getGravity())
233265

234266
def update(self, task):
235-
dt = globalClock.getDt()
236-
if not self.paused:
237-
self.world.doPhysics(dt)
238-
239267
msg = self.queue.get()
240268
processed = 0
241269
while msg:
242-
print(msg)
243-
if msg["props"]["text"]:
244-
if msg["action"] == "update":
245-
self.processUpdate(msg)
246-
elif msg["action"] == "launch":
247-
self.processUpdate(msg)
248-
self.processLaunch(msg)
270+
if msg["action"] == "leave":
271+
self.launchers[msg["client_id"]].destroy()
272+
del self.launchers[msg["client_id"]]
273+
elif msg["action"] == "update":
274+
self.processUpdate(msg)
275+
elif msg["action"] == "launch":
276+
self.processUpdate(msg)
277+
self.processLaunch(msg)
249278
processed += 1
250279
if processed < self.msgsPerFrameLimit:
251280
msg = self.queue.get()
252281
else:
253282
msg = None
283+
284+
dt = globalClock.getDt()
285+
if not self.paused:
286+
self.world.doPhysics(dt)
254287
return task.cont
255288

289+
def processUpdate(self, msg):
290+
props = msg["props"]
291+
text = self.launchers[msg["client_id"]]
292+
text.setText(props["text"])
293+
text.setColor(Vec3(*hex_to_rgb(props["color"])) / 256)
294+
295+
props["y"] *= -1
296+
props["z"] *= -1
297+
if abs(props["x"]) != 1 and abs(props["y"]) != 1:
298+
if abs(props["x"]) > abs(props["y"]):
299+
props["x"] = 1 if props["x"] > 0 else -1
300+
else:
301+
props["y"] = 1 if props["y"] > 0 else -1;
302+
303+
x = props["x"] * X_EXTENT * .95
304+
y = props["z"] * Y_EXTENT * .92
305+
z = props["y"] * Z_EXTENT * .90
306+
if abs(props["x"]) == 1:
307+
x += text.getHalfExtents()[0] * -props["x"]
308+
if abs(props["y"]) == 1:
309+
z += text.getHalfExtents()[0] * -props["y"]
310+
props["planarAngle"] += 90 * props["y"]
311+
text.setPos(Point3(x, y, z))
312+
313+
if abs(props["x"]) == 1:
314+
hpr = Vec3(props["zAngle"], 0, props["planarAngle"])
315+
else:
316+
hpr = Vec3(0, -props["zAngle"], props["planarAngle"])
317+
text.setHpr(hpr)
318+
319+
def processLaunch(self, msg):
320+
props = msg["props"]
321+
text = self.launchers[msg["client_id"]]
322+
323+
hpr = text.getHpr()
324+
if abs(props["x"]) == 1:
325+
velocity = Vec3(
326+
-props["x"] * cos(radians(hpr[0])) * cos(radians(hpr[2])),
327+
-props["x"] * sin(radians(hpr[0])),
328+
-props["x"] * -sin(radians(hpr[2])))
329+
else:
330+
velocity = Vec3(
331+
cos(radians(hpr[2])),
332+
-sin(radians(hpr[1])),
333+
-cos(radians(hpr[1])) * sin(radians(hpr[2])))
334+
335+
angular = Vec3(random(), random(), random())
336+
text.launch(velocity * (props["launchStrength"] * 10 + 3),
337+
angular * props["launchStrength"] * 0)
338+
339+
self.floaters.append(text)
340+
del self.launchers[msg["client_id"]]
341+
256342
def createTarget(self, name, x=0, y=0, z=0):
257343
shape = BulletSphereShape(0.5)
258344
ghost = BulletGhostNode('GhostTarget_'+name)

web/static/realtime.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ $(() => {
4141
let x = props.x = Math.clamp(props.x, -1, 1);
4242
let y = props.y = Math.clamp(props.y, -1, 1);
4343
let z = props.z = Math.clamp(props.z, -1, 1);
44-
props.planarAngle = Math.clamp(props.planarAngle, -85, 85);
45-
props.zAngle = Math.clamp(props.zAngle, -85, 85);
44+
props.planarAngle = Math.clamp(props.planarAngle, -89, 89);
45+
props.zAngle = Math.clamp(props.zAngle, -89, 89);
4646

4747
if (Math.abs(x) != 1 && Math.abs(y) != 1) {
4848
if (Math.abs(x) > Math.abs(y)) {
@@ -89,14 +89,19 @@ $(() => {
8989
updateArrow(true);
9090

9191
$("input").on("input", function() {
92+
if ($(this).prop("id") == "text" &&
93+
$(this).val().indexOf(" ") != -1) {
94+
alert("Please write only a single word!");
95+
return false;
96+
}
97+
9298
props[$(this).prop("id")] = $(this).val();
9399
updateArrow();
94100
});
95101

96102
let i = interact($('#interact-area')[0]).gesturable({
97103
listeners: {
98104
move (event) {
99-
console.log(event.scale);
100105
props.planarAngle += event.da;
101106
props.z += Math.log(event.scale) / 100;
102107
updateArrow();
@@ -115,7 +120,7 @@ $(() => {
115120
function updateLaunchStrength() {
116121
if (!$("#launch").data("launchStart")) { return false; }
117122
let dt = new Date().getTime() - $("#launch").data("launchStart");
118-
props.launchStrength = Math.clamp(dt / 5000, 0.1, 1);
123+
props.launchStrength = Math.clamp(Math.pow(dt / 5000, .5), 0.1, 1);
119124
$("#launch").css("transform", `scale(${props.launchStrength * 2})`);
120125
requestAnimationFrame(updateLaunchStrength);
121126
}
@@ -146,4 +151,6 @@ $(() => {
146151
$("#continueButton").prop("disabled", false);
147152
}, 2000);
148153
});
154+
155+
$("#continueButton").click(sendUpdate);
149156
});

web/templates/index.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ <h2 class="text-center mt-5 mb-4">Text Launcher</h2>
4444
<div class="form-group">
4545
<label for="zAngle">Z-Angle</label>
4646
<input type="range" class="form-control-range" id="zAngle"
47-
min="-85" max="85" value="0">
47+
min="-89" max="89" value="0">
4848
<div class="help-text">
4949
Adjust the front-back orientation of your launcher.</div>
5050
</div>
5151
<div class="form-group">
5252
<label for="planarAngle">Rotation</label>
5353
<input type="range" class="form-control-range" id="planarAngle"
54-
min="-85" max="85" value="0">
54+
min="-89" max="89" value="0">
5555
<div class="help-text">
5656
You can also use touch gestures to rotate your launcher.</div>
5757
</div>

web/web.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ def on_message(self, message):
2626
queue.put(data)
2727

2828
def on_close(self):
29-
pass
29+
data = {
30+
"action": "leave",
31+
"client_id": self.client_id
32+
}
33+
queue.put(data)
3034

3135
class MainHandler(tornado.web.RequestHandler):
3236
def get(self):

0 commit comments

Comments
 (0)