Box2D b2.ContactListener strangeness - javascript
I've been using Jonas Wagner's JS Box2D port and am running into a strange problem with shape userdata. I've setup my entity to have a collision shape as well as a secondary 'foot' sensor shape to determine when my object is on solid ground. The definition looks a little like this:
var bodyDef = new b2.BodyDef();
bodyDef.position.Set(
(this.pos.x + this.size.x / 2) * b2.SCALE,
(this.pos.y + this.size.y / 2) * b2.SCALE
);
this.body = ig.world.CreateBody(bodyDef);
var shapeDef = new b2.PolygonDef();
shapeDef.SetAsBox(
this.size.x / 2 * b2.SCALE,
this.size.y / 2 * b2.SCALE
);
shapeDef.density = 0.85;
shapeDef.filter.groupIndex = -1;
this.body.CreateShape(shapeDef);
var footShapeDef = new b2.PolygonDef();
footShapeDef.SetAsOrientedBox(
3 * b2.SCALE,
3 * b2.SCALE,
new b2.Vec2(0,7*b2.SCALE),
0
);
footShapeDef.isSensor = true;
footShapeDef.density = 0.85;
footShapeDef.filter.groupIndex = -1;
var footShape = this.body.CreateShape(footShapeDef);
footShape.SetUserData("feet");
this.body.SetMassFromShapes();
The idea here being that I can detect when my foot sensor stops colliding with entities. Everything is working as expected and my b2.ContactListener is correctly reporting when objects stop colliding with my foot sensor. The problem is that the userData I'm assigning to my foot shape isn't being correctly reported.
As you can see below, the point object returned in my b2.ContactListener's Remove callback clearly contains a shape (shape2) with it's m_userData attribute set to 'feet'. However, querying the shape2 object directly reports it's m_userData as null.
I've included a screenshot of Safari's debug console performing the console.log shown below. What's going on here?!
var listener = new b2.ContactListener();
listener.Remove = function(point)
{
var p = point;
var s1 = p.shape1;
var s2 = p.shape2;
console.log(p, s1, s2);
}
Related
Three.js draw ellipse from point list
I’m coding a solar system model with Three.js. I have a function that calculates the planet position given the day, however I’m not able to draw the correct orbit of the planet (elliptic) starting from a list of points obtained from that function.I googled a lot but I haven’t found an example, how can I do this? EDIT: these are my functions, one for drawing the ellipse and one for moving the planets function drawOrbitTest(orbit) { var material = new THREE.LineBasicMaterial({ color: 0xffffff }); var points = []; for (var i = 0; i < orbit.orbitPeriod; i += 7) { //basically what i'm trying to do here is calculating the position of the planet //with an increment of one week for cycle if (orbit.orbitPeriod - i <= 7) { var endPoint = calcPosition(orbit, Date.now() + 86400 * 1000 * orbit.orbitPeriod); points.push(new THREE.Vector3(endPoint.x * AU, endPoint.y * AU, endPoint.z * AU)); break; } else { var middlePoint = calcPosition(orbit, Date.now() + 86400 * 1000 * i); points.push(new THREE.Vector3(middlePoint.x * AU, middlePoint.y * AU, middlePoint.z * AU)); } } var shape = new THREE.Shape(points); var geomShape = new THREE.ShapeBufferGeometry(shape); var material = new THREE.LineBasicMaterial(orbit.color); var ellipse = new THREE.Line(geomShape, material); scene.add(ellipse); } and function rotateOnEllipse(obj, date) { var res = calcPosition(obj.orbit, date); obj.mesh.position.x = res.x * AU; obj.mesh.position.y = res.y * AU; obj.mesh.position.z = res.z * AU; } EDIT 2: I followed #prisoner849 suggestion, now it works, the planets move on their orbits. However, the orbit now is more like a "ring" than an ellipse; why is it drawn like that?
The issue in the rendering of the line was caused by a bug in the function calcPosition(orbit,date) (it was giving back slightly different points if called with date and date+orbitPeriod). #prisoner849's suggestion worked for me
Calculating the angle between a velocity (particles motion) and a line
So I am creating a simulation of a bouncing ball, and the user can place lines the ball can collide with on the canvas by dragging from one point to another. There are essentially four lines that can be created: So the object that stores a line is defined as such: export interface pathSection { xfrom: number; yfrom: number; xto: number; yto: number; length: number; } The first and third lines in the image for example dont give the same value from Math.atan2(yto - yfrom, xto - from); So given the (relative) complexity of the surfaces, I need to find the angle between a moving object and that surface at the point of collision: The ball strikes the surface at an angle a, which is what I want! However I am having trouble finding the angle between the two vectors. This is what I understood would work: var dx = this.path[index_for_path_section].xfrom - this.path[index_for_path_section].xto; var dy = this.path[index_for_path_section].yfrom - this.path[index_for_path_section].yto; var posX = this.particle.pos.x; var posY = this.particle.pos.y; var posNextX = posX + this.particle.v.x; var posNextY = posY + this.particle.v.y; var angleOfRamp = Math.atan2(dy, dx); var angleOfvelocity = Math.atan2(posNextY - posY, posNextX - posX); var angleBetween = angleOfRamp - angleOfvelocity; This is then used to calculate the speed of the object after the collision: var spd = Math.sqrt(this.particle.v.x * this.particle.v.x + this.particle.v.y * this.particle.v.y); var restitution = this.elasticity / 100; this.particle.v.x = restitution * spd * Math.cos(angleBetween); this.particle.v.y = restitution * spd * Math.sin(angleBetween); However the angle calculated is around -4.5 Pi, about -90 degrees for the object directly down and the surface at what looks to be around 45-60 degrees… The red arrow shows the path of the object moving through the surface - the white dots show where a collision has been detected between the surface and the object. Any help on how to get the correct and usable angle between the two velocity and the line would be appreciated! Note I have tried utilizing this solution, but have struggled to adapt it to my own work.
So it took me some time, and I am not 100% sure still of why it works because I think im finding the JavaScript angles system a bit tricky, but: var dx = this.path[collided].xfrom - this.path[collided].xto; var dy = this.path[collided].yfrom - this.path[collided].yto; var spd = Math.sqrt(this.particle.v.x * this.particle.v.x + this.particle.v.y * this.particle.v.y); var angleOfRamp = Math.atan2(dy, dx); var angleOfvelocity = Math.atan2(this.particle.v.y, this.particle.v.x); var angleBetween = angleOfRamp * 2 - angleOfvelocity; // not sure why :) if (angleBetween < 0) { angleBetween += 2*Math.PI; } // not sure why :) const restitution = this.elasticity / 100; this.particle.v.x = restitution * spd * Math.cos(angleBetween); this.particle.v.y = restitution * spd * Math.sin(angleBetween); Thanks to all who looked :)
TypeError: can't convert undefined to object in Three.js using THREE.Points()
I have a problem with my Three.js code. I want to implement the asteroid belt for my solar sistem simulator. So I want to use the THREE.Point() object. To do it I write a function but it returns the "type error" as in the title. I'm using THREE.js library on my Ubuntu 19.10 and I'm using Firefox 67.0.4. The problem is also in Chrome. The code below is in createAsteroidBelt() function that I call in the init() function. Here my code: I was inspired to here. var dist = (data[jupiterId].distance + data[marsId].distance)/2; var particleCount = 1000; var asteroidsGeometry = new THREE.Geometry(); // now create the individual particles for (let i = 0; i < particleCount; i++) { // create a particle with random position var asteroidOrbit = dist + THREE.Math.randFloat(-10, 10); var coord = new THREE.Vector3(); coord.x = Math.cos(THREE.Math.randFloat(0, 2*Math.PI)) * asteroidOrbit; coord.y = THREE.Math.randFloat(-2, 2); coord.z = Math.sin(THREE.Math.randFloat(0, 2*Math.PI)) * asteroidOrbit; asteroidsGeometry.vertices.push(coord); } var asteroids = new THREE.Points(asteroidsGeometry, new THREE.PointsMaterial({ size: 20 })); asteroids.name = "Asteroid Belt"; planets[solarSystemId].add(asteroids); The error is exactly in this line: var asteroids = new THREE.Points(asteroidsGeometry, new THREE.PointsMaterial({ size: 20 })); Here the stack of the error: updateMorphTargets file:///.../libraries/three.min.js:672 ac file:///.../libraries/three.min.js:216 createAsteroidBelt file:///.../main.js:363 init file:///.../main.js:478 <anonima> file:///.../main.js:612 I don't understand the cause of this error. ERROR SOLVED Thanks to #jeffld to solve the error. Now I have a problem with the shape of this asteroid belt. It has a square shape instead of a ring. Here a photo: Asteroid belt shape FINAL SOLUTION I just also solve the last error. The problem is that I generate a different angle for coordX and coordY, instead of using the same angle. Here the correct part of the code: var asteroidOrbit = dist + THREE.Math.randFloat(-10, 10); var angle = THREE.Math.randFloat(0, 2*Math.PI); var coord = new THREE.Vector3(); coord.x = Math.cos(angle) * asteroidOrbit; coord.y = THREE.Math.randFloat(-2, 2); coord.z = Math.sin(angle) * asteroidOrbit; asteroidsGeometry.vertices.push(coord); Thank you very much to all for the help :) .
This is a open issue in three.js. The object is missing the morphAttributes object (even though it is empty). You can work around it by adding using this line after the push: asteroidsGeometry.vertices.push(coord); asteroidsGeometry.morphAttributes = {};
Why does my Perlin Noise Generator make this squiggly pattern?
I've started learning about perlin noise generation, and I wanted to try to make my own generator in JavaScript. To get me started, I've been following along with this youtube tutorial. to try and copy their basic implementation. I've also been reading this article. I've provided a jsFiddle of my implementation, which shows what I'm doing and what the output is. Instead of the smoothly-flowing, bubbling noise I see in the youtube tutorial, I get tightly-constricted black squiggles. Here's a picture: Here's my generator code: var p = [151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, 151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180] function generatePerlinNoise(x, y){ // Determine gradient cell corners var xi = Math.floor(x) & 255; var yi = Math.floor(y) & 255; var g1 = p[p[xi] + yi]; var g2 = p[p[xi + 1] + yi]; var g3 = p[p[xi] + yi + 1]; var g4 = p[p[xi + 1] + yi + 1]; // Get point within gradient cell var xf = x - Math.floor(x); var yf = y - Math.floor(y); // Get dot products at each corner of the gradient cell var d1 = generateGradient(g1, xf, yf); var d2 = generateGradient(g2, xf - 1, yf); var d3 = generateGradient(g3, xf, yf - 1); var d4 = generateGradient(g4, xf - 1, yf - 1); var xFade = fade(xf); var yFade = fade(yf); var x1Interpolated = linearInterpolate(xFade, d1, d2); var x2Interpolated = linearInterpolate(xFade, d3, d4); var noiseValue = linearInterpolate(yFade, x1Interpolated, x2Interpolated); return noiseValue; } function generateGradient(hash, x, y){ switch(hash & 3){ case 0: return x + y; case 1: return -x + y; case 2: return x - y; case 3: return -x - y; default: return 0; } } // This is the fade function described by Ken Perlin function fade(t){ return t * t * t * (t * (t * 6 - 15) + 10); } function linearInterpolate(amount, left, right){ return ((1-amount) * left + (amount * right)) } I'm utilizing the generator function by dividing the pixel x and y values by my canvas height, and scaling by a frequency variable: var freq = 12; var noise = generatePerlinNoise((x/canvas.offsetHeight)*freq, (y/canvas.offsetHeight)*freq); noise = Math.abs(noise); I'm currently just using the noise value to generate a greyscale color value: var blue = Math.floor(noise * 0xFF); // Scale 255 by our noise value, // and take it's integer portion var green = Math.floor(noise * 0xFF); var red = Math.floor(noise * 0xFF); data[i++] = red; data[i++] = green; data[i++] = blue; data[i++] = 255; The point of this for me is to learn more about noise generation and javascript. I've tried to think through the problem and made some observations: There are no visible artifacts, so it seems like my fade function is working fine. There don't seem to be any repeating patterns, so that's a good sign. I go generate a complete range of values in the greyscale - not just black and white. The general issue seems to be how the gradient at each pixel affects its neighbors: Mine seem to clump together in snake-like ropes of fixed widths. It seems like the gradient vector options supplied and the permutation table used to randomly-ish select them would govern this, but mine are an exact copy from the tutorial: The same 4 vectors each pointing into a quadrant at 45 degrees, and the standard permutation table. This leaves me stumped as to figuring out what the cause is. My general suspicions boil down to: I've messed up the algorithm somewhere in a subtle way that I keep overlooking. (Most likely) There's a subtle difference in the way JavaScript does something that i'm overlooking. (Maybe) I'm generating noise correctly, but incorrectly applying the result to the RGB values used in my canvas image data. (Least likely) I'd really like to get to the bottom of this. Thanks in advance for your help! :) Also: I DO think this pattern is cool, and this is a learning exercise, so if anyone can share insight into why I'm getting this pattern specifically, that'd be great!
Draw automatically the first vertice of a path with Open Layers
I'd like to help the user to input an orientation for a segment with OpenLayers. I have that form where user can input the bearing for a point, but I would like to help him by : start drawing the first vertice of a segment on the map when the user clicks on a button, (that first vertice being a known point) then the user just has to click for the second vertice, and bearing is computed automatically. See the fiddle here or SO snippet below. I'm almost done : I can compute the bearing when a segment is drawn. But there's an exception at the very end of the script : I can't get OL to draw automatically the first point of my segment. Thank you to anyone who can help. <script src="http://openlayers.org/api/OpenLayers.js"></script> <body> <div id="map" style="height: 500px"></div> </body> <script> var CONSTANTS = { MAP_FROM_PROJECTION: new OpenLayers.Projection("EPSG:4326"), // Transform from WGS 1984 MAP_TO_PROJECTION: new OpenLayers.Projection("EPSG:900913") // to Spherical Mercator Projection }; function radians(n) { return n * (Math.PI / 180); } function degrees(n) { return n * (180 / Math.PI); } function computeBearing(startLat, startLong, endLat, endLong) { startLat = radians(startLat); startLong = radians(startLong); endLat = radians(endLat); endLong = radians(endLong); var dLong = endLong - startLong; var dPhi = Math.log(Math.tan(endLat / 2.0 + Math.PI / 4.0) / Math.tan(startLat / 2.0 + Math.PI / 4.0)); if (Math.abs(dLong) > Math.PI) { if (dLong > 0.0) dLong = -(2.0 * Math.PI - dLong); else dLong = (2.0 * Math.PI + dLong); } return (degrees(Math.atan2(dLong, dPhi)) + 360.0) % 360.0; } map = new OpenLayers.Map("map"); map.addLayer(new OpenLayers.Layer.OSM()); map.setCenter(new OpenLayers.LonLat(3, 47).transform(CONSTANTS.MAP_FROM_PROJECTION, CONSTANTS.MAP_TO_PROJECTION), 6); var lineLayer = new OpenLayers.Layer.Vector("Line Layer"); map.addLayers([lineLayer]); var lineControl = new OpenLayers.Control.DrawFeature(lineLayer, OpenLayers.Handler.Path, { handlerOptions: { maxVertices: 2, freehandMode: function(evt) { return false; } }, featureAdded: function(feature) { var drawnLinePoints = feature.geometry.getVertices(); var lonlat1 = drawnLinePoints[0].transform(CONSTANTS.MAP_TO_PROJECTION, CONSTANTS.MAP_FROM_PROJECTION); var lonlat2 = drawnLinePoints[1].transform(CONSTANTS.MAP_TO_PROJECTION, CONSTANTS.MAP_FROM_PROJECTION); var bearingValue = computeBearing(lonlat1.y, lonlat1.x, lonlat2.y, lonlat2.x); console.log(bearingValue); } }); map.addControl(lineControl); lineControl.activate(); var handler; for (var i = 0; i < map.controls.length; i++) { var control = map.controls[i]; if (control.displayClass === "olControlDrawFeature") { handler = control.handler; break; } } // Here I have an exception in the console : I would like // OL to draw hat point automatically. handler.addPoint(new OpenLayers.Pixel(50, 50)); </script>
OpenLayers.Handler.Path.addPoint works on OpenLayers.Pixel, not OpenLayers.LonLat: /** * Method: addPoint * Add point to geometry. Send the point index to override * the behavior of LinearRing that disregards adding duplicate points. * * Parameters: * pixel - {<OpenLayers.Pixel>} The pixel location for the new point. */ addPoint: function(pixel) { this.layer.removeFeatures([this.point]); var lonlat = this.layer.getLonLatFromViewPortPx(pixel); this.point = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat) ); this.line.geometry.addComponent( this.point.geometry, this.line.geometry.components.length ); this.layer.addFeatures([this.point]); this.callback("point", [this.point.geometry, this.getGeometry()]); this.callback("modify", [this.point.geometry, this.getSketch()]); this.drawFeature(); delete this.redoStack; } I actually see no good way of achieving this other than adding an addPointByLonLat method: OpenLayers.Handler.Path.prototype.addPointByLonLat = function(lonLat) { this.layer.removeFeatures([this.point]); this.point = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat) ); this.line.geometry.addComponent( this.point.geometry, this.line.geometry.components.length ); this.layer.addFeatures([this.point]); this.callback("point", [this.point.geometry, this.getGeometry()]); this.callback("modify", [this.point.geometry, this.getSketch()]); this.drawFeature(); delete this.redoStack; }; Or subclass as your own handler class (propbably cleaner). Notes: addPoint is not an API method (so addPointByLonLat is also not). This may result in problem on version changes. Don't use the compressed/minified JS in development and check docs on methods you use. Next time consider asking on https://gis.stackexchange.com/. Consider asking for a code review on your JS.
You can also use insertXY(x,y) function in order to insert a point with geographic coordinates http://dev.openlayers.org/docs/files/OpenLayers/Handler/Path-js.html#OpenLayers.Handler.Path.insertXY lonlat = new OpenLayers.LonLat(1,45); lonlat.transform(CONSTANTS.MAP_FROM_PROJECTION,CONSTANTS.MAP_TO_PROJECTION); handler.createFeature(new OpenLayers.Pixel(100, 100)); handler.insertXY(lonlat.lon,lonlat.lat); handler.drawFeature(); You can check it here with a fork of your original jsfiddle : http://jsfiddle.net/mefnpbn2/
See this fiddle for the solution. tldr : // draw the first point as I needed, as if a user has clicked on the map handler.modifyFeature(new OpenLayers.Pixel(50, 50), true); // draw a first point on the map (not clicked). // This will be the initial point where the "cursor" is on the map, as long as // the user hasn't hovered onto the map with its mouse. This make the blue // line showing current segment to appear, without this segment is drawn but // no feedback is given to the user as long as he hasn't clicked. handler.addPoint(new OpenLayers.Pixel(50, 50)); //