Skip to content

Instantly share code, notes, and snippets.

@comster
Created June 27, 2017 21:27
Show Gist options
  • Save comster/4a5c208dc39d3c3be262abb05fa791a3 to your computer and use it in GitHub Desktop.
Save comster/4a5c208dc39d3c3be262abb05fa791a3 to your computer and use it in GitHub Desktop.
requirebin sketch
var SAT = require('sat');
var testCircleCollision = function (a, b) {
var circleA = new SAT.Circle(new SAT.Vector(a.x, a.y), a.r);
var circleB = new SAT.Circle(new SAT.Vector(b.x, b.y), b.r);
var response = new SAT.Response();
var collided = SAT.testCircleCircle(circleA, circleB, response);
console.log("did collide?", collided, "details:", response)
return {
collided: collided,
response: response
};
};
var reflectBullet = function (bullet, wall, sat_response) {
var distVector = new SAT.Vector(bullet.v, 0);
var origBulletX = bullet.x;
var origBulletY = bullet.y;
var currentVector = new SAT.Vector(bullet.x, bullet.y);
var bullet_trajectory_radians = Math.atan2(bullet.targetY - bullet.origY, bullet.targetX - bullet.origX);
console.log("bullet tragectory radians", bullet_trajectory_radians)
distVector.rotate(bullet_trajectory_radians);
currentVector.sub(distVector);
// move the bullet back from the collision state
bullet.x = currentVector.x;
bullet.y = currentVector.y;
// also set that as our new starting position vector
bullet.origY = bullet.y;
bullet.origX = bullet.x;
var reflectVector = sat_response.overlapN;
var targetVector = new SAT.Vector(bullet.origX, bullet.origY);
// TODO find the new target direction, via reflection off of the collision point, [origBulletX, origBulletY]
// use the tangent line of the cirlce at this point? to determine an axis to pass to .reflect(axis) ?
// targetVector.reflect(reflectVector);
// set our new target
// bullet.targetX = targetVector.x;
// bullet.targetY = targetVector.y;
console.log("reflected towards target", targetVector);
}
var a_beam_of_light = {
x: 25,
y: 40,
r: 1,
v: 1,
origX: 0,
origY: 40,
targetX: 50,
targetY: 40,
}
var a_curved_mirror = {
x: 50,
y: 50,
r: 25
}
var step_world = function() {
// step the world forward, moving the bullet towards it's target
a_beam_of_light.x = a_beam_of_light.x + a_beam_of_light.v;
// console.log(a_beam_of_light, a_curved_mirror)
var test_results = testCircleCollision(a_beam_of_light, a_curved_mirror);
if(test_results.collided) {
reflectBullet(a_beam_of_light, a_curved_mirror, test_results);
}
}
step_world();
setTimeout(function(){require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({sat:[function(require,module,exports){(function(root,factory){"use strict";if(typeof define==="function"&&define["amd"]){define(factory)}else if(typeof exports==="object"){module["exports"]=factory()}else{root["SAT"]=factory()}})(this,function(){"use strict";var SAT={};function Vector(x,y){this["x"]=x||0;this["y"]=y||0}SAT["Vector"]=Vector;SAT["V"]=Vector;Vector.prototype["copy"]=Vector.prototype.copy=function(other){this["x"]=other["x"];this["y"]=other["y"];return this};Vector.prototype["clone"]=Vector.prototype.clone=function(){return new Vector(this["x"],this["y"])};Vector.prototype["perp"]=Vector.prototype.perp=function(){var x=this["x"];this["x"]=this["y"];this["y"]=-x;return this};Vector.prototype["rotate"]=Vector.prototype.rotate=function(angle){var x=this["x"];var y=this["y"];this["x"]=x*Math.cos(angle)-y*Math.sin(angle);this["y"]=x*Math.sin(angle)+y*Math.cos(angle);return this};Vector.prototype["reverse"]=Vector.prototype.reverse=function(){this["x"]=-this["x"];this["y"]=-this["y"];return this};Vector.prototype["normalize"]=Vector.prototype.normalize=function(){var d=this.len();if(d>0){this["x"]=this["x"]/d;this["y"]=this["y"]/d}return this};Vector.prototype["add"]=Vector.prototype.add=function(other){this["x"]+=other["x"];this["y"]+=other["y"];return this};Vector.prototype["sub"]=Vector.prototype.sub=function(other){this["x"]-=other["x"];this["y"]-=other["y"];return this};Vector.prototype["scale"]=Vector.prototype.scale=function(x,y){this["x"]*=x;this["y"]*=y||x;return this};Vector.prototype["project"]=Vector.prototype.project=function(other){var amt=this.dot(other)/other.len2();this["x"]=amt*other["x"];this["y"]=amt*other["y"];return this};Vector.prototype["projectN"]=Vector.prototype.projectN=function(other){var amt=this.dot(other);this["x"]=amt*other["x"];this["y"]=amt*other["y"];return this};Vector.prototype["reflect"]=Vector.prototype.reflect=function(axis){var x=this["x"];var y=this["y"];this.project(axis).scale(2);this["x"]-=x;this["y"]-=y;return this};Vector.prototype["reflectN"]=Vector.prototype.reflectN=function(axis){var x=this["x"];var y=this["y"];this.projectN(axis).scale(2);this["x"]-=x;this["y"]-=y;return this};Vector.prototype["dot"]=Vector.prototype.dot=function(other){return this["x"]*other["x"]+this["y"]*other["y"]};Vector.prototype["len2"]=Vector.prototype.len2=function(){return this.dot(this)};Vector.prototype["len"]=Vector.prototype.len=function(){return Math.sqrt(this.len2())};function Circle(pos,r){this["pos"]=pos||new Vector;this["r"]=r||0}SAT["Circle"]=Circle;Circle.prototype["getAABB"]=Circle.prototype.getAABB=function(){var r=this["r"];var corner=this["pos"].clone().sub(new Vector(r,r));return new Box(corner,r*2,r*2).toPolygon()};function Polygon(pos,points){this["pos"]=pos||new Vector;this["angle"]=0;this["offset"]=new Vector;this.setPoints(points||[])}SAT["Polygon"]=Polygon;Polygon.prototype["setPoints"]=Polygon.prototype.setPoints=function(points){var lengthChanged=!this["points"]||this["points"].length!==points.length;if(lengthChanged){var i;var calcPoints=this["calcPoints"]=[];var edges=this["edges"]=[];var normals=this["normals"]=[];for(i=0;i<points.length;i++){calcPoints.push(new Vector);edges.push(new Vector);normals.push(new Vector)}}this["points"]=points;this._recalc();return this};Polygon.prototype["setAngle"]=Polygon.prototype.setAngle=function(angle){this["angle"]=angle;this._recalc();return this};Polygon.prototype["setOffset"]=Polygon.prototype.setOffset=function(offset){this["offset"]=offset;this._recalc();return this};Polygon.prototype["rotate"]=Polygon.prototype.rotate=function(angle){var points=this["points"];var len=points.length;for(var i=0;i<len;i++){points[i].rotate(angle)}this._recalc();return this};Polygon.prototype["translate"]=Polygon.prototype.translate=function(x,y){var points=this["points"];var len=points.length;for(var i=0;i<len;i++){points[i].x+=x;points[i].y+=y}this._recalc();return this};Polygon.prototype._recalc=function(){var calcPoints=this["calcPoints"];var edges=this["edges"];var normals=this["normals"];var points=this["points"];var offset=this["offset"];var angle=this["angle"];var len=points.length;var i;for(i=0;i<len;i++){var calcPoint=calcPoints[i].copy(points[i]);calcPoint.x+=offset.x;calcPoint.y+=offset.y;if(angle!==0){calcPoint.rotate(angle)}}for(i=0;i<len;i++){var p1=calcPoints[i];var p2=i<len-1?calcPoints[i+1]:calcPoints[0];var e=edges[i].copy(p2).sub(p1);normals[i].copy(e).perp().normalize()}return this};Polygon.prototype["getAABB"]=Polygon.prototype.getAABB=function(){var points=this["calcPoints"];var len=points.length;var xMin=points[0]["x"];var yMin=points[0]["y"];var xMax=points[0]["x"];var yMax=points[0]["y"];for(var i=1;i<len;i++){var point=points[i];if(point["x"]<xMin){xMin=point["x"]}else if(point["x"]>xMax){xMax=point["x"]}if(point["y"]<yMin){yMin=point["y"]}else if(point["y"]>yMax){yMax=point["y"]}}return new Box(this["pos"].clone().add(new Vector(xMin,yMin)),xMax-xMin,yMax-yMin).toPolygon()};function Box(pos,w,h){this["pos"]=pos||new Vector;this["w"]=w||0;this["h"]=h||0}SAT["Box"]=Box;Box.prototype["toPolygon"]=Box.prototype.toPolygon=function(){var pos=this["pos"];var w=this["w"];var h=this["h"];return new Polygon(new Vector(pos["x"],pos["y"]),[new Vector,new Vector(w,0),new Vector(w,h),new Vector(0,h)])};function Response(){this["a"]=null;this["b"]=null;this["overlapN"]=new Vector;this["overlapV"]=new Vector;this.clear()}SAT["Response"]=Response;Response.prototype["clear"]=Response.prototype.clear=function(){this["aInB"]=true;this["bInA"]=true;this["overlap"]=Number.MAX_VALUE;return this};var T_VECTORS=[];for(var i=0;i<10;i++){T_VECTORS.push(new Vector)}var T_ARRAYS=[];for(var i=0;i<5;i++){T_ARRAYS.push([])}var T_RESPONSE=new Response;var TEST_POINT=new Box(new Vector,1e-6,1e-6).toPolygon();function flattenPointsOn(points,normal,result){var min=Number.MAX_VALUE;var max=-Number.MAX_VALUE;var len=points.length;for(var i=0;i<len;i++){var dot=points[i].dot(normal);if(dot<min){min=dot}if(dot>max){max=dot}}result[0]=min;result[1]=max}function isSeparatingAxis(aPos,bPos,aPoints,bPoints,axis,response){var rangeA=T_ARRAYS.pop();var rangeB=T_ARRAYS.pop();var offsetV=T_VECTORS.pop().copy(bPos).sub(aPos);var projectedOffset=offsetV.dot(axis);flattenPointsOn(aPoints,axis,rangeA);flattenPointsOn(bPoints,axis,rangeB);rangeB[0]+=projectedOffset;rangeB[1]+=projectedOffset;if(rangeA[0]>rangeB[1]||rangeB[0]>rangeA[1]){T_VECTORS.push(offsetV);T_ARRAYS.push(rangeA);T_ARRAYS.push(rangeB);return true}if(response){var overlap=0;if(rangeA[0]<rangeB[0]){response["aInB"]=false;if(rangeA[1]<rangeB[1]){overlap=rangeA[1]-rangeB[0];response["bInA"]=false}else{var option1=rangeA[1]-rangeB[0];var option2=rangeB[1]-rangeA[0];overlap=option1<option2?option1:-option2}}else{response["bInA"]=false;if(rangeA[1]>rangeB[1]){overlap=rangeA[0]-rangeB[1];response["aInB"]=false}else{var option1=rangeA[1]-rangeB[0];var option2=rangeB[1]-rangeA[0];overlap=option1<option2?option1:-option2}}var absOverlap=Math.abs(overlap);if(absOverlap<response["overlap"]){response["overlap"]=absOverlap;response["overlapN"].copy(axis);if(overlap<0){response["overlapN"].reverse()}}}T_VECTORS.push(offsetV);T_ARRAYS.push(rangeA);T_ARRAYS.push(rangeB);return false}SAT["isSeparatingAxis"]=isSeparatingAxis;function voronoiRegion(line,point){var len2=line.len2();var dp=point.dot(line);if(dp<0){return LEFT_VORONOI_REGION}else if(dp>len2){return RIGHT_VORONOI_REGION}else{return MIDDLE_VORONOI_REGION}}var LEFT_VORONOI_REGION=-1;var MIDDLE_VORONOI_REGION=0;var RIGHT_VORONOI_REGION=1;function pointInCircle(p,c){var differenceV=T_VECTORS.pop().copy(p).sub(c["pos"]);var radiusSq=c["r"]*c["r"];var distanceSq=differenceV.len2();T_VECTORS.push(differenceV);return distanceSq<=radiusSq}SAT["pointInCircle"]=pointInCircle;function pointInPolygon(p,poly){TEST_POINT["pos"].copy(p);T_RESPONSE.clear();var result=testPolygonPolygon(TEST_POINT,poly,T_RESPONSE);if(result){result=T_RESPONSE["aInB"]}return result}SAT["pointInPolygon"]=pointInPolygon;function testCircleCircle(a,b,response){var differenceV=T_VECTORS.pop().copy(b["pos"]).sub(a["pos"]);var totalRadius=a["r"]+b["r"];var totalRadiusSq=totalRadius*totalRadius;var distanceSq=differenceV.len2();if(distanceSq>totalRadiusSq){T_VECTORS.push(differenceV);return false}if(response){var dist=Math.sqrt(distanceSq);response["a"]=a;response["b"]=b;response["overlap"]=totalRadius-dist;response["overlapN"].copy(differenceV.normalize());response["overlapV"].copy(differenceV).scale(response["overlap"]);response["aInB"]=a["r"]<=b["r"]&&dist<=b["r"]-a["r"];response["bInA"]=b["r"]<=a["r"]&&dist<=a["r"]-b["r"]}T_VECTORS.push(differenceV);return true}SAT["testCircleCircle"]=testCircleCircle;function testPolygonCircle(polygon,circle,response){var circlePos=T_VECTORS.pop().copy(circle["pos"]).sub(polygon["pos"]);var radius=circle["r"];var radius2=radius*radius;var points=polygon["calcPoints"];var len=points.length;var edge=T_VECTORS.pop();var point=T_VECTORS.pop();for(var i=0;i<len;i++){var next=i===len-1?0:i+1;var prev=i===0?len-1:i-1;var overlap=0;var overlapN=null;edge.copy(polygon["edges"][i]);point.copy(circlePos).sub(points[i]);if(response&&point.len2()>radius2){response["aInB"]=false}var region=voronoiRegion(edge,point);if(region===LEFT_VORONOI_REGION){edge.copy(polygon["edges"][prev]);var point2=T_VECTORS.pop().copy(circlePos).sub(points[prev]);region=voronoiRegion(edge,point2);if(region===RIGHT_VORONOI_REGION){var dist=point.len();if(dist>radius){T_VECTORS.push(circlePos);T_VECTORS.push(edge);T_VECTORS.push(point);T_VECTORS.push(point2);return false}else if(response){response["bInA"]=false;overlapN=point.normalize();overlap=radius-dist}}T_VECTORS.push(point2)}else if(region===RIGHT_VORONOI_REGION){edge.copy(polygon["edges"][next]);point.copy(circlePos).sub(points[next]);region=voronoiRegion(edge,point);if(region===LEFT_VORONOI_REGION){var dist=point.len();if(dist>radius){T_VECTORS.push(circlePos);T_VECTORS.push(edge);T_VECTORS.push(point);return false}else if(response){response["bInA"]=false;overlapN=point.normalize();overlap=radius-dist}}}else{var normal=edge.perp().normalize();var dist=point.dot(normal);var distAbs=Math.abs(dist);if(dist>0&&distAbs>radius){T_VECTORS.push(circlePos);T_VECTORS.push(normal);T_VECTORS.push(point);return false}else if(response){overlapN=normal;overlap=radius-dist;if(dist>=0||overlap<2*radius){response["bInA"]=false}}}if(overlapN&&response&&Math.abs(overlap)<Math.abs(response["overlap"])){response["overlap"]=overlap;response["overlapN"].copy(overlapN)}}if(response){response["a"]=polygon;response["b"]=circle;response["overlapV"].copy(response["overlapN"]).scale(response["overlap"])}T_VECTORS.push(circlePos);T_VECTORS.push(edge);T_VECTORS.push(point);return true}SAT["testPolygonCircle"]=testPolygonCircle;function testCirclePolygon(circle,polygon,response){var result=testPolygonCircle(polygon,circle,response);if(result&&response){var a=response["a"];var aInB=response["aInB"];response["overlapN"].reverse();response["overlapV"].reverse();response["a"]=response["b"];response["b"]=a;response["aInB"]=response["bInA"];response["bInA"]=aInB}return result}SAT["testCirclePolygon"]=testCirclePolygon;function testPolygonPolygon(a,b,response){var aPoints=a["calcPoints"];var aLen=aPoints.length;var bPoints=b["calcPoints"];var bLen=bPoints.length;for(var i=0;i<aLen;i++){if(isSeparatingAxis(a["pos"],b["pos"],aPoints,bPoints,a["normals"][i],response)){return false}}for(var i=0;i<bLen;i++){if(isSeparatingAxis(a["pos"],b["pos"],aPoints,bPoints,b["normals"][i],response)){return false}}if(response){response["a"]=a;response["b"]=b;response["overlapV"].copy(response["overlapN"]).scale(response["overlap"])}return true}SAT["testPolygonPolygon"]=testPolygonPolygon;return SAT})},{}]},{},[]);var SAT=require("sat");var testCircleCollision=function(a,b){var circleA=new SAT.Circle(new SAT.Vector(a.x,a.y),a.r);var circleB=new SAT.Circle(new SAT.Vector(b.x,b.y),b.r);var response=new SAT.Response;var collided=SAT.testCircleCircle(circleA,circleB,response);console.log("did collide?",collided,"details:",response);return{collided:collided,response:response}};var reflectBullet=function(bullet,wall,sat_response){var distVector=new SAT.Vector(bullet.v,0);var origBulletX=bullet.x;var origBulletY=bullet.y;var currentVector=new SAT.Vector(bullet.x,bullet.y);var bullet_trajectory_radians=Math.atan2(bullet.targetY-bullet.origY,bullet.targetX-bullet.origX);console.log("bullet tragectory radians",bullet_trajectory_radians);distVector.rotate(bullet_trajectory_radians);currentVector.sub(distVector);bullet.x=currentVector.x;bullet.y=currentVector.y;bullet.origY=bullet.y;bullet.origX=bullet.x;var reflectVector=sat_response.overlapN;var targetVector=new SAT.Vector(bullet.origX,bullet.origY);console.log("reflected towards target",targetVector)};var a_beam_of_light={x:25,y:40,r:1,v:1,origX:0,origY:40,targetX:50,targetY:40};var a_curved_mirror={x:50,y:50,r:25};var step_world=function(){a_beam_of_light.x=a_beam_of_light.x+a_beam_of_light.v;var test_results=testCircleCollision(a_beam_of_light,a_curved_mirror);if(test_results.collided){reflectBullet(a_beam_of_light,a_curved_mirror,test_results)}};step_world()},0);
{
"name": "requirebin-sketch",
"version": "1.0.0",
"dependencies": {
"sat": "0.6.0"
}
}
<!-- contents of this file will be placed inside the <body> -->
<!-- contents of this file will be placed inside the <head> -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment