Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74d4ceb109 | ||
|
|
95ef070bd4 | ||
|
|
3a81b53624 |
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.idea/
|
||||
.DS_Store
|
||||
@@ -1,16 +0,0 @@
|
||||
# 🚀 Welcome to your new awesome project!
|
||||
|
||||
This project has been created using **webpack scaffold**, you can now run
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
to bundle your application
|
||||
# Asteroids
|
||||
@@ -0,0 +1,90 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _express = _interopRequireDefault(require("express"));
|
||||
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
|
||||
var _cookieParser = _interopRequireDefault(require("cookie-parser"));
|
||||
|
||||
var _morgan = _interopRequireDefault(require("morgan"));
|
||||
|
||||
var _index = _interopRequireDefault(require("./routes/index"));
|
||||
|
||||
var _users = _interopRequireDefault(require("./routes/users"));
|
||||
|
||||
var _events = _interopRequireDefault(require("events"));
|
||||
|
||||
var _vm = _interopRequireDefault(require("vm"));
|
||||
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
|
||||
var _socket = _interopRequireDefault(require("socket.io"));
|
||||
|
||||
var _asteroidserver = _interopRequireDefault(require("../models/asteroidserver.js"));
|
||||
|
||||
var _game = _interopRequireDefault(require("./models/game.js"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var app = (0, _express["default"])();
|
||||
app.use((0, _morgan["default"])('dev'));
|
||||
app.use(_express["default"].json());
|
||||
app.use(_express["default"].urlencoded({
|
||||
extended: false
|
||||
}));
|
||||
app.use((0, _cookieParser["default"])());
|
||||
app.use(_express["default"]["static"](_path["default"].join(_dirname, '../public')));
|
||||
app.use('/', _index["default"]);
|
||||
app.use('/users', _users["default"]);
|
||||
var _default = app;
|
||||
exports["default"] = _default;
|
||||
var PORT = process.env.PORT | 8000;
|
||||
console.log("Port number: ".concat(PORT));
|
||||
var server = http.createServer(app);
|
||||
|
||||
var io = _socket["default"].listen(server);
|
||||
|
||||
var files = [// Tools and tests
|
||||
// "libs/tools.js",
|
||||
// "libs/test.js",
|
||||
//Base types
|
||||
"libs/iterator.js", "libs/dict.js", "libs/tree.js", // Math types
|
||||
"libs/group.js", // "libs/tensor.js",
|
||||
// "libs/vector.js",
|
||||
"libs/matrix.js", // Visual types
|
||||
"libs/color.js", "libs/point.js", "libs/figure.js", // "libs/line.js",
|
||||
"libs/form.js", "libs/basepolygon.js", "libs/segment.js", "libs/polygon.js", "libs/rectangle.js", "libs/square.js", "libs/circle.js", // Physics types
|
||||
"libs/motion.js", "libs/body.js", "libs/plane.js", "libs/context.js", "libs/entity.js", "libs/manager.js", // Game types
|
||||
"models/gameentity.js", "models/missile.js", "models/missilegroup.js", "models/life.js", "models/follower.js", "models/shooter.js", "models/asteroid.js", "models/asteroidgroup.js", "models/spaceship.js", "models/spaceshipgroup.js", "models/meteor.js", "models/meteorgroup.js", "models/collider.js", "models/gamemap.js", "models/supergroup.js", "models/game.js", // Entry point
|
||||
"models/gameserver.js"]; // for (const file of files) {
|
||||
// console.log(file);
|
||||
// let data = fs.readFileSync(file);
|
||||
// let script = new vm.Script(data);
|
||||
// script.runInThisContext();
|
||||
// }
|
||||
|
||||
server.listen(process.env.PORT || 8000);
|
||||
app.use('/libs', _express["default"]["static"]('libs'));
|
||||
app.use('/models', _express["default"]["static"]('models'));
|
||||
app.use('/dist', _express["default"]["static"]('dist'));
|
||||
|
||||
var _dirname = _path["default"].resolve();
|
||||
|
||||
console.log(_dirname); // var path = __dirname.split("/");
|
||||
// path = path.slice(0,-1).join("/");
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile(_dirname + "/views/index.html");
|
||||
});
|
||||
|
||||
var game = _game["default"].random(); //console.log(`Le group ${game.group.get('asteroidGroup')}`);
|
||||
|
||||
|
||||
var gameServer = new GameServer(game, io);
|
||||
gameServer.setUp();
|
||||
gameServer.main();
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var _app = _interopRequireDefault(require("../app"));
|
||||
|
||||
var _debug = _interopRequireDefault(require("debug"));
|
||||
|
||||
var _http = _interopRequireDefault(require("http"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var debug = (0, _debug["default"])('your-project-name:server');
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
|
||||
_app["default"].set('port', port);
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
|
||||
var server = _http["default"].createServer(_app["default"]);
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (port >= 0) {
|
||||
// port number
|
||||
return port;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
||||
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; // handle specific listen errors with friendly messages
|
||||
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
|
||||
|
||||
function onListening() {
|
||||
var addr = server.address();
|
||||
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _express = _interopRequireDefault(require("express"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var router = _express["default"].Router();
|
||||
/* GET home page. */
|
||||
|
||||
|
||||
router.get('/', function (req, res, next) {
|
||||
res.render('index', {
|
||||
title: 'Express'
|
||||
});
|
||||
});
|
||||
var _default = router;
|
||||
exports["default"] = _default;
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _express = _interopRequireDefault(require("express"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var router = _express["default"].Router();
|
||||
/* GET users listing. */
|
||||
|
||||
|
||||
router.get('/', function (req, res, next) {
|
||||
res.send('respond with a resource');
|
||||
});
|
||||
var _default = router;
|
||||
exports["default"] = _default;
|
||||
@@ -0,0 +1,15 @@
|
||||
class Activity {
|
||||
constructor(components) {
|
||||
this.components = components;
|
||||
}
|
||||
show(context) {
|
||||
for(const component of this.components) {
|
||||
component.show(context);
|
||||
}
|
||||
}
|
||||
update(dt) {
|
||||
for(const component of this.components) {
|
||||
component.update(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
interface Anatomy {
|
||||
// constructor: func;
|
||||
// contains: func;
|
||||
// rotate: func;
|
||||
// move: func;
|
||||
lineWidth: number;
|
||||
str: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
var Angle = {
|
||||
toRadian: function(angle) {
|
||||
return Math.pi*angle/180;
|
||||
},
|
||||
toDegree: function(angle) {
|
||||
return 180*angle/Math.pi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export default class Animation {
|
||||
static on = false;
|
||||
static state = 0;
|
||||
constructor(object, on=Animation.on, state=Animation.state) {
|
||||
this.object = object;
|
||||
this.on = on;
|
||||
this.state = state;
|
||||
}
|
||||
trigger() {
|
||||
this.on = true;
|
||||
this.start();
|
||||
}
|
||||
start() {
|
||||
|
||||
}
|
||||
next() {
|
||||
|
||||
}
|
||||
end() {
|
||||
|
||||
}
|
||||
update(dt) {
|
||||
throw 'The update method of an animation must be implemented.'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ColorChanger extends Animation {
|
||||
constructor(entity, state, colors) {
|
||||
super(entity, state);
|
||||
this.colors = colors;
|
||||
}
|
||||
get color() {
|
||||
const [c1, c2] = this.colors;
|
||||
const v1 = Color.toVector(c1);
|
||||
const v2 = Color.toVector(c2);
|
||||
const v = v1.rmul(1-this.state).add(v2.rmul(this.state));
|
||||
return Color.fromVector(v);
|
||||
}
|
||||
update(dt) {
|
||||
if (this.on) {
|
||||
if (this.state<1) {
|
||||
this.next()
|
||||
} else {
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
next() {
|
||||
this.state += dt;
|
||||
this.object.form.color = this.color;
|
||||
}
|
||||
end() {
|
||||
this.state = 0;
|
||||
this.on = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
import Form from './form.js'
|
||||
|
||||
export default class BasePolygon extends Form {
|
||||
/**
|
||||
* Radius of the smallest circle containing the base polygon
|
||||
* which center is the center of the base polygon.
|
||||
* Usefull for collision detection.
|
||||
*/
|
||||
static getRadiusBubble(points, center) {
|
||||
let radius = 0;
|
||||
let distance = 0;
|
||||
for (const point of points) {
|
||||
distance = Point.distance(point, center);
|
||||
if (distance>radius) { // slightly faster than using max
|
||||
radius = distance;
|
||||
}
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
constructor(color, lineWidth, fill) {
|
||||
super();
|
||||
this.color = color;
|
||||
this.lineWidth = lineWidth;
|
||||
this.fill = fill;
|
||||
}
|
||||
// get vectors() {
|
||||
// throw "A base polygon must have a points getter."
|
||||
// }
|
||||
// set vectors() {
|
||||
// throw "A base polygon must have a points getter."
|
||||
// }
|
||||
// get points() {
|
||||
// throw "A base polygon must have a points getter."
|
||||
// }
|
||||
// set points(pts) {
|
||||
// throw "A base polygon must have a points setter."
|
||||
// }
|
||||
get center() {
|
||||
return Vector.average(...this.points);
|
||||
}
|
||||
set center(point) {
|
||||
const v = point.sub(this.center)
|
||||
this.translate(v);
|
||||
}
|
||||
/**
|
||||
* Radius of the smallest circle containing the base polygon
|
||||
* which center is the center of the base polygon.
|
||||
* Usefull for collision detection.
|
||||
*/
|
||||
get hitBubbleRadius() {
|
||||
const points = this.points;
|
||||
const center = Vector.average(...points);
|
||||
return this.constructor.getRadiusBubble(points, center);
|
||||
}
|
||||
/**
|
||||
* Smallest circle containing the base polygon which center
|
||||
* is the center of the polygon.
|
||||
*/
|
||||
get hitBubble() {
|
||||
const points = this.points;
|
||||
const center = Vector.average(...points);
|
||||
const radius = this.constructor.getRadiusBubble(points, center);
|
||||
return new Circle(center, radius, 0.1);
|
||||
}
|
||||
/**
|
||||
* Smallest rectangle containing the base polygon.
|
||||
*/
|
||||
get hitBox() {
|
||||
const points = this.points;
|
||||
let xmin = Infinity;
|
||||
let ymin = Infinity;
|
||||
let xmax = -Infinity;
|
||||
let ymax = -Infinity;
|
||||
for (const p of points) {
|
||||
if (p.x<xmin) { xmin = p.x; }
|
||||
if (p.x>xmax) { xmax = p.x; }
|
||||
if (p.y<ymin) { ymin = p.y; }
|
||||
if (p.y>ymax) { ymax = p.y; }
|
||||
}
|
||||
return new Rectangle([xmin, ymin], [xmax-xmin, ymax-ymin], 0.1);
|
||||
}
|
||||
translate(vector) {
|
||||
throw "A base polygon must have a translate method."
|
||||
}
|
||||
scale(n) {
|
||||
throw "A base polygon must have a scale method."
|
||||
}
|
||||
move(...vector) {
|
||||
this.translate(Vector.from(vector));
|
||||
}
|
||||
get length() {
|
||||
return this.points.length;
|
||||
}
|
||||
show(ctx) {
|
||||
if (this.fill) {
|
||||
ctx.fillStyle = this.color;
|
||||
} else {
|
||||
ctx.strokeStyle = this.color;
|
||||
}
|
||||
if (this.lineWidth) {
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
}
|
||||
const points = this.points;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(...points[points.length-1]);
|
||||
for (const pt of points) {
|
||||
ctx.lineTo(...pt);
|
||||
}
|
||||
ctx.closePath();
|
||||
if (this.fill) {
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
get segments() {
|
||||
const segments = [];
|
||||
const points = this.points;
|
||||
let p = points[this.points.length-1];
|
||||
for (const pi of points) {
|
||||
segments.push(new Segment(p, pi));
|
||||
p = pi;
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
set segments(segs) {
|
||||
// this is naive mapping,
|
||||
//because the information is redundant
|
||||
this.points = segs.map(s => s.p1);
|
||||
}
|
||||
showPoints(ctx) {
|
||||
for (const point of this.points) {
|
||||
point.show(ctx);
|
||||
}
|
||||
}
|
||||
showSegments(ctx) {
|
||||
for (const segment of this.segments) {
|
||||
segment.show(ctx);
|
||||
}
|
||||
}
|
||||
get area() {
|
||||
|
||||
}
|
||||
get perimeter() {
|
||||
return Math.sum(this.segments.map(s => s.length));
|
||||
}
|
||||
contains2(point) {
|
||||
// make a segment which must cross the polygon.
|
||||
// then check if it crosses.
|
||||
const s = new Segment(point, point.radd(0, this.perimeter))
|
||||
|
||||
|
||||
}
|
||||
contains(p) {
|
||||
const points = this.points;
|
||||
var isInside = false;
|
||||
var minX = points[0].x, maxX = points[0].x;
|
||||
var minY = points[0].y, maxY = points[0].y;
|
||||
for (let n = 1; n < points.length; n++) {
|
||||
let q = points[n];
|
||||
minX = Math.min(q.x, minX);
|
||||
maxX = Math.max(q.x, maxX);
|
||||
minY = Math.min(q.y, minY);
|
||||
maxY = Math.max(q.y, maxY);
|
||||
}
|
||||
if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
|
||||
return false;
|
||||
}
|
||||
let i = 0, j = points.length - 1;
|
||||
for (i, j; i < points.length; j = i++) {
|
||||
if ( (points[i].y > p.y) != (points[j].y > p.y) &&
|
||||
p.x < (points[j].x - points[i].x) * (p.y - points[i].y) / (points[j].y - points[i].y) + points[i].x ) {
|
||||
isInside = !isInside;
|
||||
}
|
||||
}
|
||||
return isInside;
|
||||
}
|
||||
collide(polygon) {
|
||||
for (const p of polygon.points) {
|
||||
if (this.contains(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
import Tensor from "./tensor.js";
|
||||
import Motion from "./motion.js";
|
||||
import Vector from "./vector.js";
|
||||
|
||||
export default class Body extends Tensor {
|
||||
static n = 1;
|
||||
static w = 2;
|
||||
static h = 2;
|
||||
static random(n=Body.n, w=Body.w, h=Body.h) {
|
||||
let motions = new Tensor(n);
|
||||
for (let i=0; i<n; i++) {
|
||||
motions[i] = Motion.random(w, h);
|
||||
}
|
||||
return this.from(motions);
|
||||
}
|
||||
get motion() {
|
||||
return this[0];
|
||||
}
|
||||
set motion(value) {
|
||||
this[0] = value;
|
||||
}
|
||||
get moment() {
|
||||
return this[1];
|
||||
}
|
||||
set moment(value) {
|
||||
this[1] = value;
|
||||
}
|
||||
get x() {
|
||||
return this.motion.x;
|
||||
}
|
||||
set x(v) {
|
||||
this.motion.x = v;
|
||||
}
|
||||
get y() {
|
||||
return this.motion.y;
|
||||
}
|
||||
set y(v) {
|
||||
this.motion.y = v;
|
||||
}
|
||||
get z() {
|
||||
return this.motion.z;
|
||||
}
|
||||
set z(v) {
|
||||
this.motion.z = v;
|
||||
}
|
||||
get vx() {
|
||||
return this.motion.vx;
|
||||
}
|
||||
set vx(v) {
|
||||
this.motion.vx = v;
|
||||
}
|
||||
get vy() {
|
||||
return this.motion.vy;
|
||||
}
|
||||
set vy(v) {
|
||||
this.motion.vy = v;
|
||||
}
|
||||
get vz() {
|
||||
return this.motion.vz;
|
||||
}
|
||||
set vz(v) {
|
||||
this.motion.vz = v;
|
||||
}
|
||||
get ax() {
|
||||
return this.motion.ax;
|
||||
}
|
||||
set ax(v) {
|
||||
this.motion.ax = v;
|
||||
}
|
||||
get ay() {
|
||||
return this.motion.ay;
|
||||
}
|
||||
set ay(v) {
|
||||
this.motion.ay = v;
|
||||
}
|
||||
get az() {
|
||||
return this.motion.az;
|
||||
}
|
||||
set az(v) {
|
||||
this.motion.az = v;
|
||||
}
|
||||
get position() {
|
||||
return this.motion.position;
|
||||
}
|
||||
set position(v) {
|
||||
this.motion.position = v;
|
||||
}
|
||||
get velocity() {
|
||||
return this.motion.velocity;
|
||||
}
|
||||
set velocity(v) {
|
||||
this.motion.velocity = v;
|
||||
}
|
||||
get acceleration() {
|
||||
return this.motion.acceleration;
|
||||
}
|
||||
set acceleration(v) {
|
||||
this.motion.acceleration;
|
||||
}
|
||||
update(dt) {
|
||||
for (let i=0; i<this.length; i++) {
|
||||
this[i].update(dt);
|
||||
}
|
||||
// this.updateFriction(0.1)
|
||||
// this.map(motion => motion.update(dt));
|
||||
}
|
||||
updateFriction(friction=0.1) {
|
||||
for (const motion of this) {
|
||||
motion.updateFriction(friction);
|
||||
}
|
||||
}
|
||||
follow(vector) {
|
||||
let norm = vector.sub(this.position).norm/10;
|
||||
// norm = sigmoid(norm);
|
||||
// norm = Math.min(Math.max(0, norm),1);
|
||||
let angle = vector.sub(this.position).angle;
|
||||
// this[0][2].angle = angle;
|
||||
this[0][2] = Vector.polar(norm, angle);
|
||||
// this.motion.acceleration = Vector.polar(norm, angle);
|
||||
}
|
||||
limit(vmin, vmax) {
|
||||
this.motion[0].limit(vmin, vmax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import Rectangle from './rectangle.js';
|
||||
|
||||
export default class Box extends Rectangle {
|
||||
constructor(...ars, position, size) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import Form from './form.js';
|
||||
|
||||
export default class Circle extends Form {
|
||||
constructor(
|
||||
center,
|
||||
radius,
|
||||
lineWidth=Form.lineWidth,
|
||||
color=Form.color,
|
||||
fill=Form.fill
|
||||
) {
|
||||
super();
|
||||
this.center = center
|
||||
this.radius = radius;
|
||||
this.lineWidth = lineWidth;
|
||||
this.color = color;
|
||||
this.fill = fill;
|
||||
}
|
||||
get x() {
|
||||
return this.center[0];
|
||||
}
|
||||
set x(value) {
|
||||
this.center[0] = value;
|
||||
}
|
||||
get y() {
|
||||
return this.center[1];
|
||||
}
|
||||
set y(value) {
|
||||
this.center[1] = value;
|
||||
}
|
||||
get r() {
|
||||
return this.radius;
|
||||
}
|
||||
set r(value) {
|
||||
this.radius = value;
|
||||
}
|
||||
show(context) {
|
||||
context.lineWidth = this.lineWidth;
|
||||
if (this.fill) {
|
||||
context.fillStyle = this.color;
|
||||
} else {
|
||||
context.strokeStyle = this.color;
|
||||
}
|
||||
context.beginPath();
|
||||
context.arc(...this.center, this.radius, 0, 2*Math.PI);
|
||||
if (this.fill) {
|
||||
context.fill();
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
context.closePath();
|
||||
}
|
||||
contains(position) {
|
||||
return this.center.sub(position).norm < this.radius;
|
||||
}
|
||||
crossCircle(circle) {
|
||||
return this.center.sub(circle.center).norm < this.radius+other.radius;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import Connexion from './connexion.js';
|
||||
|
||||
export default class Client extends Connexion {
|
||||
constructor() {
|
||||
self.url = url;
|
||||
}
|
||||
/* Return the url. */
|
||||
getUrl() {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import Vector from './vector.js';
|
||||
|
||||
/*
|
||||
* Color namespace.
|
||||
*/
|
||||
var Color = {
|
||||
/*
|
||||
* Return a random color.
|
||||
*/
|
||||
random: function() {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i=0; i<6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
},
|
||||
/*
|
||||
* Create a vector using a color.
|
||||
*/
|
||||
toVector: function(color) {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let v = new Vector();
|
||||
for (let i=0; i<3; i++) {
|
||||
let a = letters.indexOf(color[2*i+1].toUpperCase());
|
||||
let b = letters.indexOf(color[2*i+2].toUpperCase());
|
||||
v.push(a*16+b);
|
||||
}
|
||||
return v;
|
||||
},
|
||||
/*
|
||||
* Create a color using a vector.
|
||||
*/
|
||||
fromVector: function(vector) {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let v = vector.floor();
|
||||
let color = "#";
|
||||
for (let i=0; i<3; i++) {
|
||||
let b = v[i]%16;
|
||||
let a = (v[i]-b)/16;
|
||||
color += letters[a];
|
||||
color += letters[b];
|
||||
}
|
||||
return color;
|
||||
},
|
||||
|
||||
/*
|
||||
* Mix the given colors together.
|
||||
*/
|
||||
mix: function(...colors) {
|
||||
let vectors = colors.map(Color.toVector);
|
||||
let vector = Vector.average(...vectors);
|
||||
return Color.fromVector(vector);
|
||||
},
|
||||
/*
|
||||
* Ligthen a given color using some k factor which must be between 0 and 1.
|
||||
*/
|
||||
lighten: function(color, k=0.5) {
|
||||
let v = Color.toVector(color);
|
||||
let m = new Vector(255, 255, 255);
|
||||
let d = m.sub(v);
|
||||
d.irmul(k);
|
||||
v.iadd(d);
|
||||
return Color.fromVector(v);
|
||||
},
|
||||
/*
|
||||
* Ligthen a given color using some k factor which must be between 0 and 1.
|
||||
*/
|
||||
darken: function(color, k=0.5) {
|
||||
let v = Color.toVector(color);
|
||||
let d = Vector.copy(v);
|
||||
d.irmul(k);
|
||||
v.isub(d);
|
||||
return Color.fromVector(v);
|
||||
},
|
||||
/*
|
||||
* Interpolate a color between a list of couples (color,value) using a t parameter between 0 and 1.
|
||||
*/
|
||||
interpolate: function(couples, t) {
|
||||
let i = 0;
|
||||
while (t>couples[i][1]) {
|
||||
i+=1;
|
||||
}
|
||||
let v1 = Color.toVector(couples[i-1][0]);
|
||||
let v2 = Color.toVector(couples[i][0]);
|
||||
t = Color.linear(t, couples[i-1][1], couples[i][1], 0, 1);
|
||||
return Color.fromVector(v1.rmul(1-t).add(v2.rmul(t)));
|
||||
},
|
||||
linear: function(x, e1, e2, s1, s2) {
|
||||
return (x-e1)/(e2-e1)*(s2-s1)+s1;
|
||||
},
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
import Plane from './plane.js';
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class ContextAdapter {
|
||||
constructor(context, plane=new Plane()) {
|
||||
this.context = context;
|
||||
this.plane = plane;
|
||||
this.textSeparator = "px ";
|
||||
}
|
||||
toScreen(v) {
|
||||
return this.plane.toScreen(v).add(this.center);
|
||||
}
|
||||
fromScreen(v) {
|
||||
return this.plane.fromScreen(v.sub(this.center));;
|
||||
}
|
||||
get width() {
|
||||
return this.context.width;
|
||||
}
|
||||
set width(value) {
|
||||
this.context.width = value;
|
||||
}
|
||||
get height() {
|
||||
return this.context.height;
|
||||
}
|
||||
set height(value) {
|
||||
this.context.height = value;
|
||||
}
|
||||
get center() {
|
||||
return new Vector(this.context.width/2, this.context.height/2);
|
||||
}
|
||||
set strokeStyle(value) {
|
||||
this.context.strokeStyle = value;
|
||||
}
|
||||
get strokeStyle() {
|
||||
return this.context.strokeStyle;
|
||||
}
|
||||
set fillStyle(value) {
|
||||
this.context.fillStyle = value;
|
||||
}
|
||||
get fillStyle() {
|
||||
return this.context.fillStyle;
|
||||
}
|
||||
set strokeWidth(value) {
|
||||
this.context.strokeWidth = value;
|
||||
}
|
||||
get strokeWidth() {
|
||||
return this.context.strokeWidth;
|
||||
}
|
||||
set lineWidth(value) {
|
||||
this.context.lineWidth = value * Math.max(...this.plane.units.position);
|
||||
}
|
||||
get lineWidth() {
|
||||
return this.context.lineWidth / Math.max(...this.plane.units.position);
|
||||
}
|
||||
beginPath() {
|
||||
this.context.beginPath();
|
||||
}
|
||||
closePath() {
|
||||
this.context.closePath();
|
||||
}
|
||||
stroke() {
|
||||
this.context.stroke();
|
||||
}
|
||||
fill() {
|
||||
this.context.fill();
|
||||
}
|
||||
translate(x, y) {
|
||||
let v = this.plane.units.position.tdot(new Vector(x,y));
|
||||
this.plane.position = v;
|
||||
}
|
||||
clear() {
|
||||
this.context.clearRect(0, 0, this.context.width, this.context.height);
|
||||
}
|
||||
strokeRect(x, y, w, h) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
[w, h] = this.plane.units.position.tdot(new Vector(w,h));
|
||||
this.context.strokeRect(x, y, w, h);
|
||||
}
|
||||
fillRect(x, y, w, h, mg=0) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
[w, h] = this.plane.units.position.tdot(new Vector(w,h));
|
||||
this.context.fillRect(x, y, w+mg, h+mg);
|
||||
}
|
||||
clearRect(x, y, w, h) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
[w, h] = this.plane.units.position.tdot(new Vector(w,h));
|
||||
this.context.clearRect(x, y, w, h);
|
||||
}
|
||||
fillText(text, x, y) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
this.context.fillText(text, x, y, 1000);
|
||||
}
|
||||
drawImage(img, x, y, w=undefined, h=undefined) {
|
||||
if (w && h) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
[w, h] = this.plane.units.position.tdot(new Vector(w,h));
|
||||
this.context.drawImage(img, x, y, w, h);
|
||||
} else {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
this.context.drawImage(img, x, y);
|
||||
}
|
||||
}
|
||||
arc(x, y, r, a, b, conversion=true) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
if (conversion) {
|
||||
r *= Math.max(...this.plane.units.position);
|
||||
}
|
||||
this.context.arc(x, y, r, a, b);
|
||||
}
|
||||
moveTo(x, y) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
this.context.moveTo(x, y);
|
||||
}
|
||||
lineTo(x, y) {
|
||||
[x, y] = this.toScreen(new Vector(x,y));
|
||||
this.context.lineTo(x, y);
|
||||
}
|
||||
get font() {
|
||||
return this.context.font;
|
||||
}
|
||||
set font(value) {
|
||||
this.context.font = value;
|
||||
}
|
||||
get textSize() {
|
||||
return Number(this.context.font.split(this.textSeparator)[0])/Math.max(...this.plane.units.position);
|
||||
}
|
||||
get textFont() {
|
||||
return this.context.font.split(this.textSeparator)[1];
|
||||
}
|
||||
set textSize(value) {
|
||||
value *= Math.max(...this.plane.units.position);
|
||||
this.context.font = String(value) + this.textSeparator + this.textFont;
|
||||
}
|
||||
set textFont(value) {
|
||||
this.context.font = String(this.textSize) + this.textSeparator + value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Polygon...
|
||||
*/
|
||||
class Polygon extends Form {
|
||||
static random(...args) {
|
||||
var p = new Polygon(Form.random(...args));
|
||||
|
||||
}
|
||||
get area() {
|
||||
|
||||
}
|
||||
get center() {
|
||||
return Point.average(...this);
|
||||
}
|
||||
set center(value) {
|
||||
const t = value.sub(this.center);
|
||||
this.imap(p => p.add(t));
|
||||
}
|
||||
get segments() {
|
||||
const sgs = new Tensor();
|
||||
var p = undefined;
|
||||
for (const pi of this) {
|
||||
if (p) {
|
||||
sgs.push(new Segment(pi, p));
|
||||
}
|
||||
}
|
||||
return sgs;
|
||||
}
|
||||
set segments(value) {
|
||||
|
||||
}
|
||||
show(context) {
|
||||
if (this.fill) {
|
||||
context.strokeStyle = this.color;
|
||||
} else {
|
||||
context.fillStyle = this.color;
|
||||
}
|
||||
context.lineWidth = this.lineWidth;
|
||||
context.beginPath();
|
||||
context.moveTo(...this[0]);
|
||||
for (const point of this) {
|
||||
context.lineTo(...point);
|
||||
}
|
||||
if (this.fill) {
|
||||
context.stroke();
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
context.closePath();
|
||||
}
|
||||
rotate(angle, point=Point.zero) {
|
||||
this.map(v => v.rotate(angle, point));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Python like dictionary in javascript
|
||||
* - btw:
|
||||
* The keys in Map are ordered. Thus, when iterating over it,
|
||||
* a Map object returns keys in order of insertion. And so
|
||||
* it is for this dictionary class.
|
||||
* - also:
|
||||
* Performs better in scenarios involving frequent additions
|
||||
* and removals of key-value pairs.
|
||||
*/
|
||||
export default class Dict extends Map {
|
||||
static fromIterables(iterable) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
get items() {
|
||||
return this.entries();
|
||||
}
|
||||
/**
|
||||
* Same as dict.setdefault in python.
|
||||
* @param {Any} key
|
||||
* @param {Any} value
|
||||
*/
|
||||
setDefault(key, value) {
|
||||
if (!this.has(key)) {
|
||||
this.set(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Same as dict.update in python
|
||||
* @param {IterableCouples, ArrayOfCouples, Map, Dict} entries
|
||||
*/
|
||||
update(entries) {
|
||||
for (const [k,v] in entries) {
|
||||
if (!this.has(k)) {
|
||||
this.set(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Same as update but it is syntactic sugar.
|
||||
* @param {...Couple} entries
|
||||
*/
|
||||
setDefaults(...entries) {
|
||||
this.update(entries);
|
||||
}
|
||||
imap(f) {
|
||||
for (const [k,v] in this) {
|
||||
this.set(k, f(v));
|
||||
}
|
||||
}
|
||||
map(f) {
|
||||
const dict = new this();
|
||||
for (const [k,v] in this) {
|
||||
dict.set(k, f(v));
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import Polygon from './polygon.js';
|
||||
import Body from './body.js';
|
||||
|
||||
export default class Entity {
|
||||
static random(form_args=[], body_args=[]) {
|
||||
const form = Polygon.random(...form_args);
|
||||
const body = Body.random(...body_args);
|
||||
return new this(form, body);
|
||||
}
|
||||
constructor(form, body) {
|
||||
this.form = form;
|
||||
this.body = body;
|
||||
}
|
||||
update(dt) {
|
||||
this.body.update(dt);
|
||||
// if (this.body.length==1) {
|
||||
// console.log(this.form, this.body)
|
||||
// this.form.angle = this.body[0][1].angle;
|
||||
// }
|
||||
// else if (this.body.length==2) {
|
||||
// this.form.angle = this.body.angle;
|
||||
// }
|
||||
// console.log(this.form, this.body)
|
||||
this.form.center = this.body.position;
|
||||
}
|
||||
show(ctx) {
|
||||
this.form.show(ctx);
|
||||
// this.hitBox.show(ctx);
|
||||
// this.hitBubble.show(ctx);
|
||||
}
|
||||
get motion() {
|
||||
return this.body[0];
|
||||
}
|
||||
set motion(v) {
|
||||
this.body[0].set(v);
|
||||
}
|
||||
get angularMotion() {
|
||||
return this.body[1];
|
||||
}
|
||||
set angularMotion(v) {
|
||||
this.body[1].set(v);
|
||||
}
|
||||
get x() {
|
||||
return this.body[0][0][0];
|
||||
}
|
||||
set x(v) {
|
||||
this.body[0][0][0] = v;
|
||||
}
|
||||
get y() {
|
||||
return this.body[0][0][1];
|
||||
}
|
||||
set y(v) {
|
||||
this.body[0][0][1] = v;
|
||||
}
|
||||
get z() {
|
||||
return this.body[0][0][2];
|
||||
}
|
||||
set z(v) {
|
||||
this.body[0][0][2] = v;
|
||||
}
|
||||
get vx() {
|
||||
return this.body[0][1][0];
|
||||
}
|
||||
set vx(v) {
|
||||
this.body[0][1][0].set(v);
|
||||
}
|
||||
get ax() {
|
||||
return this.body[0][1][2];
|
||||
}
|
||||
set ax(v) {
|
||||
this.body[0][1][2].set(v);
|
||||
}
|
||||
get position() {
|
||||
return this.body[0][0];
|
||||
}
|
||||
set position(v) {
|
||||
this.body[0][0].set(v);
|
||||
}
|
||||
get p() {
|
||||
return this.body[0][0];
|
||||
}
|
||||
set p(v) {
|
||||
this.body[0][0].set(v);
|
||||
}
|
||||
get velocity() {
|
||||
return this.body[0][1];
|
||||
}
|
||||
set velocity(v) {
|
||||
this.body[0][1].set(v);
|
||||
}
|
||||
get velocity() {
|
||||
return this.body[0][1];
|
||||
}
|
||||
set velocity(v) {
|
||||
this.body[0][1].set(v);
|
||||
}
|
||||
get angle() {
|
||||
return this.body.angle;
|
||||
}
|
||||
set angle(angle) {
|
||||
this.body.angle = angle;
|
||||
}
|
||||
get hitBox() {
|
||||
return this.form.hitBox;
|
||||
}
|
||||
get hitBubble() {
|
||||
return this.form.hitBubble;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* A figure is basically a base class
|
||||
* for any visual object.
|
||||
*/
|
||||
export default class Figure {
|
||||
static lineWidth = 1;
|
||||
static color = "#ffffff";
|
||||
static conversion = true;
|
||||
static random(...args) {
|
||||
throw "Static function random is not implemented."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import VectorField from './vectorfield.js';
|
||||
|
||||
export default class Moment extends VectorField {
|
||||
constructor(force) {
|
||||
this.force = force;
|
||||
this.f = (v => v.cross(force));
|
||||
}
|
||||
call(vector) {
|
||||
return this.f(vector);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import Figure from './figure.js';
|
||||
|
||||
/*
|
||||
Unlike a figure, a form does have an area.
|
||||
This class is more of an interface than
|
||||
a true class or we can also see it as an
|
||||
abstract class. (reference to PHP, C#, C++, ...)
|
||||
*/
|
||||
export default class Form extends Figure {
|
||||
static fill = false;
|
||||
static random(...args) {
|
||||
throw "Static function random is not implemented."
|
||||
}
|
||||
// get center() {
|
||||
// throw "The get center method is not implemented for "+this+".";
|
||||
// }
|
||||
// set center(vector) {
|
||||
// throw "The set center method is not implemented for "+this+".";
|
||||
// }
|
||||
get area() {
|
||||
throw "The get area method is not implemented for "+this+".";
|
||||
}
|
||||
set area(area) {
|
||||
throw "The set area method is not implemented for "+this+".";
|
||||
}
|
||||
translate(v) {
|
||||
throw "The translate method is not implemented for "+this+".";
|
||||
}
|
||||
rotate(r, vector=undefined) {
|
||||
throw "The rotate method is not implemented for "+this+".";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Game {
|
||||
constructor(ticks) {
|
||||
this.ticks = ticks
|
||||
this.interval = undefined
|
||||
}
|
||||
update() {
|
||||
|
||||
}
|
||||
start() {
|
||||
this.interval = setInterval(this.update.bind(this), this.ticks)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import Game from '../libs/game.js'
|
||||
import Vector from 'vector.js';
|
||||
import ContextAdapter from 'context.js';
|
||||
|
||||
export class GameClient {
|
||||
constructor(ticks, canvas, eventsListeners=[]) {
|
||||
super(ticks)
|
||||
this.canvas = canvas
|
||||
this.eventsListeners = eventsListeners
|
||||
this.backgroundColor = "#000000"
|
||||
this.mouse = new Vector(0, 0);
|
||||
this.context = undefined
|
||||
this.interval = undefined
|
||||
}
|
||||
start() {
|
||||
this.deactivate()
|
||||
this.context = new ContextAdapter(this.canvas.getContext('2d'))
|
||||
this.eventsListeners = [
|
||||
this.canvas.addEventListener("keydown", this.onKeyDown.bind(this))
|
||||
this.canvas.addEventListener("keyup", this.onKeyUp.bind(this))
|
||||
this.canvas.addEventListener("mousemove", this.onMouseMotion.bind(this))
|
||||
this.canvas.addEventListener("scroll", this.onScroll.bind(this))
|
||||
this.canvas.addEventListener("mousewheel", this.onScroll.bind(this))
|
||||
this.canvas.addEventListener("resize", this.resize.bind(this))
|
||||
]
|
||||
this.resize()
|
||||
this.interval = setInterval(this.update.bind(this), this.ticks)
|
||||
}
|
||||
clear() {
|
||||
this.context.fillStyle = this.backgroundColor
|
||||
this.context.clear()
|
||||
}
|
||||
deactivate() {
|
||||
// prevent default behaviours of space and arrow keys
|
||||
this.canvas.addEventListener("keydown", function(e) {
|
||||
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
resize() {
|
||||
this.canvas.width = this.context.width = window.innerWidth;
|
||||
this.canvas.height = this.context.height = window.innerHeight;
|
||||
}
|
||||
onKeyDown(evt) {
|
||||
switch(evt.keyCode){
|
||||
case 39: // Arrow Right for moving right
|
||||
this.movement.right = true;
|
||||
break;
|
||||
case 37: // Arrow Left for moving left
|
||||
this.movement.left = true;
|
||||
break;
|
||||
case 40: // Arrow Up for moving up
|
||||
this.movement.up = true;
|
||||
break;
|
||||
case 38: // Arrow Down for moving down
|
||||
this.movement.down = true;
|
||||
break;
|
||||
case 16: // Left and Right shift for zooming in and out
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = true;
|
||||
} else {
|
||||
this.movement.zoomin = true;
|
||||
}
|
||||
break;
|
||||
case 18: // Alt for pause
|
||||
if (this.game.dt) {
|
||||
this.game.dt = 0;
|
||||
} else {
|
||||
this.game.dt = Game.dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
onKeyUp(evt) {
|
||||
switch(evt.keyCode) {
|
||||
case 39: // Arrow Right
|
||||
this.movement.right = false;
|
||||
break;
|
||||
case 37: // Arrow Left
|
||||
this.movement.left = false;
|
||||
break;
|
||||
case 40: // Arrow Up
|
||||
this.movement.up = false;
|
||||
break;
|
||||
case 38: // Arrow Down
|
||||
this.movement.down = false;
|
||||
break;
|
||||
case 16:
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = false;
|
||||
} else {
|
||||
this.movement.zoomin = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
onScroll(evt) {
|
||||
if (evt.wheelDeltaY>0) {
|
||||
this.context.plane.units[0].irmul(1+Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
} else {
|
||||
this.context.plane.units[0].irmul(1-Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
onMouseMotion(evt) {
|
||||
this.mouse = new Vector(evt.x, evt.y);
|
||||
}
|
||||
|
||||
move() {
|
||||
if (this.movement.up) {
|
||||
this.context.plane.y += this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.down) {
|
||||
this.context.plane.y -= this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.left) {
|
||||
this.context.plane.x -= this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.right) {
|
||||
this.context.plane.x += this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.zoomin) {
|
||||
this.context.plane.units[0].irmul(1+Manager.zoomDelta);
|
||||
}
|
||||
if (this.movement.zoomout) {
|
||||
this.context.plane.units[0].irmul(1-Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Manager {
|
||||
static zoomDelta = 0.1;
|
||||
static scrollZoomFactor = 0.5;
|
||||
static entities = [];
|
||||
static movement = {up: false, down: false, right: false, left: false, zoomin: false, zoomout: false};
|
||||
static backgroundColor = "#000000";
|
||||
|
||||
addEventListeners() {
|
||||
// using window as a global
|
||||
window.addEventListener("keydown", this.onKeyDown.bind(this));
|
||||
window.addEventListener("keyup", this.onKeyUp.bind(this));
|
||||
window.addEventListener("mousemove", this.onMouseMotion.bind(this));
|
||||
window.addEventListener("scroll", this.onScroll.bind(this));
|
||||
window.addEventListener("mousewheel", this.onScroll.bind(this));
|
||||
window.addEventListener("resize", this.resize.bind(this));
|
||||
}
|
||||
deactivate() {
|
||||
// prevent default actions from space and arrow keys
|
||||
window.addEventListener("keydown", function(e) {
|
||||
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
constructor(
|
||||
canvas,
|
||||
game,
|
||||
movement=Manager.movement,
|
||||
backgroundColor=Manager.backgroundColor
|
||||
) {
|
||||
this.canvas = canvas;
|
||||
this.context = new ContextAdapter(this.canvas.getContext("2d"));
|
||||
this.game = game;
|
||||
this.movement = movement;
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.mouse = new Vector(0, 0);
|
||||
}
|
||||
clear() {
|
||||
this.context.fillStyle = this.backgroundColor;
|
||||
this.context.clear();
|
||||
}
|
||||
show() {
|
||||
this.clear();
|
||||
this.game.show(this.context);
|
||||
}
|
||||
update() {
|
||||
this.context.plane.location.update();
|
||||
this.context.plane.units.update();
|
||||
this.move();
|
||||
this.game.update();
|
||||
}
|
||||
resize() {
|
||||
this.canvas.width = this.context.width = window.innerWidth;
|
||||
this.canvas.height = this.context.height = window.innerHeight;
|
||||
}
|
||||
onKeyDown(evt) {
|
||||
switch(evt.keyCode){
|
||||
case 39: // Arrow Right for moving right
|
||||
this.movement.right = true;
|
||||
break;
|
||||
case 37: // Arrow Left for moving left
|
||||
this.movement.left = true;
|
||||
break;
|
||||
case 40: // Arrow Up for moving up
|
||||
this.movement.up = true;
|
||||
break;
|
||||
case 38: // Arrow Down for moving down
|
||||
this.movement.down = true;
|
||||
break;
|
||||
case 16: // Left and Right shift for zooming in and out
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = true;
|
||||
} else {
|
||||
this.movement.zoomin = true;
|
||||
}
|
||||
break;
|
||||
case 18: // Alt for pause
|
||||
if (this.game.dt) {
|
||||
this.game.dt = 0;
|
||||
} else {
|
||||
this.game.dt = Game.dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
onKeyUp(evt) {
|
||||
switch(evt.keyCode) {
|
||||
case 39: // Arrow Right
|
||||
this.movement.right = false;
|
||||
break;
|
||||
case 37: // Arrow Left
|
||||
this.movement.left = false;
|
||||
break;
|
||||
case 40: // Arrow Up
|
||||
this.movement.up = false;
|
||||
break;
|
||||
case 38: // Arrow Down
|
||||
this.movement.down = false;
|
||||
break;
|
||||
case 16:
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = false;
|
||||
} else {
|
||||
this.movement.zoomin = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
onScroll(evt) {
|
||||
if (evt.wheelDeltaY>0) {
|
||||
this.context.plane.units[0].irmul(1+Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
} else {
|
||||
this.context.plane.units[0].irmul(1-Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
onMouseMotion(evt) {
|
||||
this.mouse = new Vector(evt.x, evt.y);
|
||||
}
|
||||
|
||||
move() {
|
||||
if (this.movement.up) {
|
||||
this.context.plane.y += this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.down) {
|
||||
this.context.plane.y -= this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.left) {
|
||||
this.context.plane.x -= this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.right) {
|
||||
this.context.plane.x += this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.zoomin) {
|
||||
this.context.plane.units[0].irmul(1+Manager.zoomDelta);
|
||||
}
|
||||
if (this.movement.zoomout) {
|
||||
this.context.plane.units[0].irmul(1-Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
loop() {
|
||||
this.update();
|
||||
this.show();
|
||||
// requestAnimationFrame(this.loop.bind(this));
|
||||
}
|
||||
start() {
|
||||
this.addEventListeners();
|
||||
}
|
||||
main() {
|
||||
this.start();
|
||||
setInterval(this.loop.bind(this), 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import Game from '../libs/game.js'
|
||||
|
||||
class GameServer extends Game {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Point from './points.js';
|
||||
import Segment from './segment.js';
|
||||
import Polygon from './polygon.js';
|
||||
|
||||
var p1 = new Point(1,2);
|
||||
var p2 = Point.random();
|
||||
var p3 = new Point(-5, 2);
|
||||
var p4 = Point.random();
|
||||
var p5 = p1.add(p2);
|
||||
|
||||
|
||||
var s1 = new Segment(p1, p2);
|
||||
var s2 = Segment.random();
|
||||
var s3 = s1.add(s2);
|
||||
|
||||
var pl1 = new Polygon(p1, p2, p3);
|
||||
var pl2 = pl1.neg();
|
||||
@@ -0,0 +1,75 @@
|
||||
import Tree from './tree.js';
|
||||
|
||||
export default class Group extends Tree {
|
||||
// type of the entities of the group
|
||||
static type = undefined;
|
||||
// number of entites created (usefull for making ids)
|
||||
static n=0;
|
||||
// separator for naming entities
|
||||
static separator = ":";
|
||||
|
||||
static of(items) {
|
||||
const group = new this(items);
|
||||
group.type = this.type;
|
||||
return group;
|
||||
}
|
||||
|
||||
static random(n, args=[]) {
|
||||
const g = new this();
|
||||
for (let i=0; i<n; i++) {
|
||||
g.set(this.type.name.toLowerCase()+this.separator+this.n, this.type.random(...args));
|
||||
this.n += 1;
|
||||
}
|
||||
return g;
|
||||
}
|
||||
show(ctx) {
|
||||
for (const v of this.values()) {
|
||||
v.show(ctx);
|
||||
}
|
||||
}
|
||||
update(dt) {
|
||||
for (const v of this.values()) {
|
||||
v.update(dt);
|
||||
}
|
||||
}
|
||||
take(key) {
|
||||
return this.get(this.constructor.type.name.toLowerCase()+this.constructor.separator+key);
|
||||
}
|
||||
add(key, value) {
|
||||
this.set(this.constructor.type.name.toLowerCase()+this.constructor.separator+key, value);
|
||||
}
|
||||
remove(key) {
|
||||
this.delete(this.constructor.type.name.toLowerCase()+this.constructor.separator+key);
|
||||
}
|
||||
getBodies() {
|
||||
const bodies = [];
|
||||
for (const [k,v] of this) {
|
||||
if (v instanceof Group) {
|
||||
bodies.push([k, v.getBodies()]);
|
||||
} else {
|
||||
bodies.push([k, v.body]);
|
||||
}
|
||||
}
|
||||
return bodies;
|
||||
}
|
||||
setBodies(bodies) {
|
||||
for (const [k,v] of bodies) {
|
||||
if (v[0] instanceof String) {
|
||||
this.get(k).setBodies(v);
|
||||
} else {
|
||||
this.get(k).body.rset(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// A function that returns a class with a
|
||||
// predefined type
|
||||
export function Grouper(type) {
|
||||
class SubGroup extends Group {} // Just here to bypass a bug
|
||||
const g = SubGroup.bind({});
|
||||
g.type = type;
|
||||
return g;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
export function permutation(array) {
|
||||
const l = array.length,
|
||||
used = Array(l),
|
||||
data = Array(l);
|
||||
return function* backtracking(pos) {
|
||||
if(pos == l) yield data.slice();
|
||||
else for(let i=0; i<l; i+=1) if(!used[i]) {
|
||||
used[i] = true;
|
||||
data[pos] = array[i];
|
||||
yield* backtracking(pos+1);
|
||||
used[i] = false;
|
||||
}
|
||||
}(0);
|
||||
}
|
||||
|
||||
export function permuted(array) {
|
||||
return [...permutation(array)];
|
||||
}
|
||||
|
||||
|
||||
export function* range(...args) {
|
||||
let end;
|
||||
let start = 0;
|
||||
let step = 1;
|
||||
if (args.length == 1) {
|
||||
[end] = args;
|
||||
} else if (args.length == 2) {
|
||||
[start, end] = args;
|
||||
} else if (args.length == 3) {
|
||||
[start, end, step] = args;
|
||||
} else {
|
||||
throw new RangeError("Invalid number of arguments.");
|
||||
}
|
||||
for (let i = start; i < end; i+=1) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
export function ranged(...args) {
|
||||
return [...range(...args)];
|
||||
}
|
||||
|
||||
export function *enumerate(array) {
|
||||
for (let i = 0; i < array.length; i+=1) {
|
||||
yield [i, array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
export function enumerated(array) {
|
||||
return [...enumerate(array)];
|
||||
}
|
||||
|
||||
|
||||
export function *zip (...iterables){
|
||||
let iterators = iterables.map(i => i[Symbol.iterator]() )
|
||||
while (true) {
|
||||
let results = iterators.map(iter => iter.next() )
|
||||
if (results.some(res => res.done) ) return
|
||||
else yield results.map(res => res.value )
|
||||
}
|
||||
}
|
||||
|
||||
export function zipped(...iterables) {
|
||||
return [...zip(...iterables)]
|
||||
}
|
||||
|
||||
export function last(array) { return array[array.length - 1]; }
|
||||
|
||||
export function* numericCombinations(n, r, loc = []) {
|
||||
const idx = loc.length;
|
||||
if (idx === r) {
|
||||
yield loc;
|
||||
return;
|
||||
}
|
||||
for (let next of range(idx ? last(loc) + 1 : 0, n - r + 1 + idx)) { yield* numericCombinations(n, r, loc.concat(next)); }
|
||||
}
|
||||
|
||||
export function numericCombinated(n, r) {
|
||||
return [...numericCombinations(n, r)];
|
||||
}
|
||||
|
||||
function* combinations(array, r) {
|
||||
for (let idxs of numericCombinations(array.length, r)) { yield idxs.map(i => array[i]); }
|
||||
}
|
||||
|
||||
|
||||
export function combinated(array, r) {
|
||||
return [...combinations(array, r)];
|
||||
}
|
||||
|
||||
export function count() {
|
||||
let index = 0;
|
||||
return {
|
||||
next: function(){
|
||||
return {value: index++, done: false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function repeat(value, n=Infinity) {
|
||||
let i = 0;
|
||||
return {
|
||||
next: function(){
|
||||
i+=1;
|
||||
return {value: value, done: i>=n};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function cycle(array) {
|
||||
let index = 0;
|
||||
return {
|
||||
next: function(){
|
||||
return {value: array[(index++)%array.length], done: false};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default class Level {
|
||||
constructor(group) {
|
||||
this.group = group;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import Figure from './figure.js';
|
||||
import Point from './point.js';
|
||||
|
||||
export default class Line extends Figure {
|
||||
static color = "#ffffff";
|
||||
static lineWidth = 1;
|
||||
constructor(point, vector, color=Line.color, lineWidth=Line.lineWidth) {
|
||||
this.point = point;
|
||||
this.vector = vector;
|
||||
this.color = color;
|
||||
this.lineWidth = lineWidth;
|
||||
}
|
||||
/*
|
||||
Reset the point to the closest point of the line to the origin.
|
||||
*/
|
||||
correct() {
|
||||
this.vector.iunit();
|
||||
|
||||
}
|
||||
show(context) {
|
||||
context.lineWidth = this.lineWidth;
|
||||
context.strokeStyle = this.color;
|
||||
context.beginPath();
|
||||
context.closePath();
|
||||
context.close();
|
||||
}
|
||||
get other() {
|
||||
return this.vector.call(this.point);
|
||||
}
|
||||
set other(point) {
|
||||
this.point = this.vector.sub(point)
|
||||
}
|
||||
get slope() {
|
||||
const [x1, y1] = this.point;
|
||||
const [x2, y2] = this.other;
|
||||
return (y2-y1)/(x2-x1);
|
||||
}
|
||||
get a() {
|
||||
return this.slope;
|
||||
}
|
||||
get intercept() {
|
||||
const [x, y] = this.point;
|
||||
return y-this.a*x;
|
||||
}
|
||||
get b() {
|
||||
return this.intercept;
|
||||
}
|
||||
get cartesianComplete() {
|
||||
throw "Not implemented yet.";
|
||||
}
|
||||
get cartesianReduced() {
|
||||
return [this.a, this.b];
|
||||
}
|
||||
get normal() {
|
||||
return this.vector.rot(Math.PI/2);
|
||||
}
|
||||
contains(point) {
|
||||
return point.sub(this.point);
|
||||
}
|
||||
distance(point) {
|
||||
return Math.abs(point.sub(this.point).dot(this.normal));
|
||||
}
|
||||
height(point) {
|
||||
return point.sub(this.point).dot(this.normal);
|
||||
}
|
||||
project(point) {
|
||||
return this.vector.rmul(point.sub(this.point).dot(this.vector)/this.vector.norm**2).add(this.point);
|
||||
}
|
||||
crossLine(line, precision=1e-10) { // Done the Wikipedia way
|
||||
const v1 = this.vector;
|
||||
const v2 = line.vector;
|
||||
const [x1, y1] = this.point;
|
||||
const [x3, y3] = line.point;
|
||||
const [x2, y2] = this.vector.call(this);
|
||||
const [x4, y4] = line.vector.call(line.point);
|
||||
const d = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4);
|
||||
if (abs(d)<precision) { // 2 lines parallels
|
||||
return undefined;
|
||||
}
|
||||
const t = (x1-x3)*(y3-y4)-(y1-y3)*(x3-x4)/d;
|
||||
const u = (x1-x2)*(y1-y3)-(y1-y2)*(x1-x3)/d;
|
||||
return new Point(x1+t*(x2-x1),y1+t*(y2-y1))
|
||||
}
|
||||
crossSegment(segment) {
|
||||
return segment.crossLine(Line);
|
||||
}
|
||||
cross(figure) {
|
||||
if (figure instanceof Line) {
|
||||
return this.crossLine(figure);
|
||||
} else if (figure instanceof Segment) {
|
||||
return this.crossSegment(figure);
|
||||
} else if (figure instanceof Polygon) {
|
||||
figure.crossSegment(this);
|
||||
} else {
|
||||
throw `Collisions between ${this.constructor.name} and ${figure.constructor.name} are not dealt with`;
|
||||
}
|
||||
}
|
||||
crossEquations(line, precision=1e-10) { // Done the Wikipedia way
|
||||
const a = this.slope;
|
||||
const b = line.slope;
|
||||
const c = this.intercept;
|
||||
const d = line.intercept;
|
||||
if (abs(a-b)<precision) { // 2 lines parallels
|
||||
return undefined;
|
||||
}
|
||||
return new Point((d-c)*(a-b), a*(d-c)/(a-b)+c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* More responsible array.
|
||||
* Inspired from python.
|
||||
*/
|
||||
export default class List extends Array {
|
||||
removeOne(item, n=undefined) {
|
||||
let i = 0;
|
||||
while (i < this.length) {
|
||||
if (this[i] === item) {
|
||||
this.splice(i, n);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
removeMany(items, n=undefined) {
|
||||
for (const item in items) {
|
||||
this.removeOne(item, n);
|
||||
}
|
||||
}
|
||||
remove(...items) {
|
||||
this.removeMany(items);
|
||||
}
|
||||
deleteOne(index, n=undefined) {
|
||||
this.splice(index, n);
|
||||
}
|
||||
deleteMany(indices, n=undefined) {
|
||||
for (const index of indices) {
|
||||
this.splice(index, n);
|
||||
}
|
||||
}
|
||||
delete(...indices) {
|
||||
this.deleteMany(indices);
|
||||
}
|
||||
contains(item) {
|
||||
for (const item_ of this) {
|
||||
if (item==item_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
str() {
|
||||
return String(this);
|
||||
}
|
||||
append = this.push;
|
||||
extend = this.concat;
|
||||
len = this.length;
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
import ContextAdapter from 'context.js';
|
||||
import Vector from 'vector.js';
|
||||
|
||||
|
||||
export default class Manager {
|
||||
static zoomDelta = 0.1;
|
||||
static scrollZoomFactor = 0.5;
|
||||
static entities = [];
|
||||
static movement = {up: false, down: false, right: false, left: false, zoomin: false, zoomout: false};
|
||||
static backgroundColor = "#000000";
|
||||
|
||||
addEventListeners() {
|
||||
// using window as a global
|
||||
window.addEventListener("keydown", this.onKeyDown.bind(this));
|
||||
window.addEventListener("keyup", this.onKeyUp.bind(this));
|
||||
window.addEventListener("mousemove", this.onMouseMotion.bind(this));
|
||||
window.addEventListener("scroll", this.onScroll.bind(this));
|
||||
window.addEventListener("mousewheel", this.onScroll.bind(this));
|
||||
window.addEventListener("resize", this.resize.bind(this));
|
||||
}
|
||||
deactivate() {
|
||||
// prevent default actions from space and arrow keys
|
||||
window.addEventListener("keydown", function(e) {
|
||||
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
constructor(
|
||||
canvas,
|
||||
game,
|
||||
movement=Manager.movement,
|
||||
backgroundColor=Manager.backgroundColor
|
||||
) {
|
||||
this.canvas = canvas;
|
||||
this.context = new ContextAdapter(this.canvas.getContext("2d"));
|
||||
this.game = game;
|
||||
this.movement = movement;
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.mouse = new Vector(0, 0);
|
||||
}
|
||||
clear() {
|
||||
this.context.fillStyle = this.backgroundColor;
|
||||
this.context.clear();
|
||||
}
|
||||
show() {
|
||||
this.clear();
|
||||
this.game.show(this.context);
|
||||
}
|
||||
update() {
|
||||
this.context.plane.location.update();
|
||||
this.context.plane.units.update();
|
||||
this.move();
|
||||
this.game.update();
|
||||
}
|
||||
resize() {
|
||||
this.canvas.width = this.context.width = window.innerWidth;
|
||||
this.canvas.height = this.context.height = window.innerHeight;
|
||||
}
|
||||
onKeyDown(evt) {
|
||||
switch(evt.keyCode){
|
||||
case 39: // Arrow Right for moving right
|
||||
this.movement.right = true;
|
||||
break;
|
||||
case 37: // Arrow Left for moving left
|
||||
this.movement.left = true;
|
||||
break;
|
||||
case 40: // Arrow Up for moving up
|
||||
this.movement.up = true;
|
||||
break;
|
||||
case 38: // Arrow Down for moving down
|
||||
this.movement.down = true;
|
||||
break;
|
||||
case 16: // Left and Right shift for zooming in and out
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = true;
|
||||
} else {
|
||||
this.movement.zoomin = true;
|
||||
}
|
||||
break;
|
||||
case 18: // Alt for pause
|
||||
if (this.game.dt) {
|
||||
this.game.dt = 0;
|
||||
} else {
|
||||
this.game.dt = Game.dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
onKeyUp(evt) {
|
||||
switch(evt.keyCode) {
|
||||
case 39: // Arrow Right
|
||||
this.movement.right = false;
|
||||
break;
|
||||
case 37: // Arrow Left
|
||||
this.movement.left = false;
|
||||
break;
|
||||
case 40: // Arrow Up
|
||||
this.movement.up = false;
|
||||
break;
|
||||
case 38: // Arrow Down
|
||||
this.movement.down = false;
|
||||
break;
|
||||
case 16:
|
||||
if (evt.location == 1) {
|
||||
this.movement.zoomout = false;
|
||||
} else {
|
||||
this.movement.zoomin = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
onScroll(evt) {
|
||||
if (evt.wheelDeltaY>0) {
|
||||
this.context.plane.units[0].irmul(1+Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
} else {
|
||||
this.context.plane.units[0].irmul(1-Manager.scrollZoomFactor*Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
onMouseMotion(evt) {
|
||||
this.mouse = new Vector(evt.x, evt.y);
|
||||
}
|
||||
|
||||
move() {
|
||||
if (this.movement.up) {
|
||||
this.context.plane.y += this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.down) {
|
||||
this.context.plane.y -= this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.left) {
|
||||
this.context.plane.x -= this.context.plane.speed/this.context.plane.ux;
|
||||
}
|
||||
if (this.movement.right) {
|
||||
this.context.plane.x += this.context.plane.speed/this.context.plane.uy;
|
||||
}
|
||||
if (this.movement.zoomin) {
|
||||
this.context.plane.units[0].irmul(1+Manager.zoomDelta);
|
||||
}
|
||||
if (this.movement.zoomout) {
|
||||
this.context.plane.units[0].irmul(1-Manager.zoomDelta);
|
||||
}
|
||||
}
|
||||
loop() {
|
||||
this.update();
|
||||
this.show();
|
||||
// requestAnimationFrame(this.loop.bind(this));
|
||||
}
|
||||
start() {
|
||||
this.addEventListeners();
|
||||
}
|
||||
main() {
|
||||
this.start();
|
||||
setInterval(this.loop.bind(this), 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function kronecker(v) {
|
||||
return x => Number(x==v);
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
import Tensor from './tensor.js';
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class Matrix extends Tensor {
|
||||
/*
|
||||
* A matrix can be built from a 2d array, a list of vectors
|
||||
*/
|
||||
static get order() {return 2}; /* Fancy way of declaring a static constant. */
|
||||
static w = 1;
|
||||
static h = 1;
|
||||
static format = [this.w, this.h];
|
||||
static zero(...format) {
|
||||
let v = Tensor.zero(...format)
|
||||
v = v.map(x => Matrix.from(x))
|
||||
return v;
|
||||
}
|
||||
|
||||
static random(w=this.w, h=this.h) {
|
||||
return super.random(w,h).map(t => Vector.from(t));
|
||||
}
|
||||
static toVectors(...vectors) {
|
||||
return this.from(vectors.map(v => Vector.from(v)))
|
||||
}
|
||||
static from(vectors) {
|
||||
return super.from(vectors.map(v => Vector.from(v)));
|
||||
}
|
||||
constructor(...vectors) {
|
||||
super(...vectors)
|
||||
for (let i=0; i<this.length; i++) {
|
||||
if (this[i]!=undefined) {
|
||||
this[i] = Vector.from(this[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
get width() {
|
||||
return this.length;
|
||||
}
|
||||
get height() {
|
||||
return this[0].length;
|
||||
}
|
||||
get format() {
|
||||
return [this.width, this.height];
|
||||
}
|
||||
str() {
|
||||
let m = this.slice();
|
||||
for (let x=0; x<this.width; x++) {
|
||||
m[x] = m[x].join(" ");
|
||||
}
|
||||
return m.join("\n");
|
||||
}
|
||||
slice(...args) {
|
||||
return new Matrix(...super.slice(...args));
|
||||
}
|
||||
dot(matrix) {
|
||||
let d = this.width;
|
||||
let h = this.height;
|
||||
let w = matrix.width;
|
||||
let m = Matrix.fromFormat(w, h);
|
||||
for (let x=0; x<w; x++) {
|
||||
for (let y=0; y<h; y++) {
|
||||
m[x][y] = this[x][y]*matrix[x][y];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
idot(matrix) {
|
||||
this.set(this.dot(matrix));
|
||||
}
|
||||
mul(matrix) {
|
||||
let d = this.width;
|
||||
let h = this.height;
|
||||
let w = matrix.width;
|
||||
let m = Matrix.fromFormat(w, h);
|
||||
for (let x=0; x<w; x++) {
|
||||
for (let y=0; y<h; y++) {
|
||||
m[x][y] = 0;
|
||||
for (let z=0; z<d; z++) {
|
||||
m[x][y] += this[z][y]*matrix[x][z];
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
imul(matrix) {
|
||||
return this.set(this.mul(matrix));
|
||||
}
|
||||
vmul(vector) {
|
||||
let w = this.width;
|
||||
let h = this.height;
|
||||
let a = Array(h);
|
||||
for (let y=0; y<h; y++) {
|
||||
a[y] = 0;
|
||||
for (let x=0; x<w; x++) {
|
||||
a[y] += this[x][y] * vector[x];
|
||||
}
|
||||
}
|
||||
return new Vector(...a);
|
||||
}
|
||||
}
|
||||
|
||||
export class SquareMatrix extends Matrix {
|
||||
static length = 1;
|
||||
static random(length=Matrix.length) {
|
||||
let m = new SquareMatrix(...Matrix.fromFormat(length, length));
|
||||
for (let x=0; x<length; x++) {
|
||||
for (let y=0; y<length; y++) {
|
||||
m[x][y] = Math.random();
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
constructor(...vectors) {
|
||||
if (Math.max(...vectors.map(v => v.length)) != Math.min(...vectors.map(v => v.length))) {
|
||||
throw new Error("The vectors must have the same length.");
|
||||
}
|
||||
super(...vectors);
|
||||
}
|
||||
get det2() {
|
||||
const n = this.length;
|
||||
let v = 0;
|
||||
for (let i=0; i<n; i++) {
|
||||
let p1 = 1;
|
||||
let p2 = 1;
|
||||
for (let j=0; j<n; j++) {
|
||||
console.log()
|
||||
p1 *= this[(i+j)%n][j];
|
||||
p2 *= this[(i-j+n)%n][j];
|
||||
}
|
||||
v += (p1-p2);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
get det() {
|
||||
let v = 0;
|
||||
for (p of permutation([...range(this.length)])) {
|
||||
// v +=
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
/*
|
||||
node
|
||||
.load tensor.js
|
||||
.load matrix.js
|
||||
m = new SquareMatrix([1, 2], [3, 4]);
|
||||
|
||||
*/
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import Matrix from './matrix.js';
|
||||
import Vector from './vector.js';
|
||||
/*
|
||||
A motion is a matrix and also a list of vectors.
|
||||
*/
|
||||
export default class Motion extends Matrix {
|
||||
static width = 3;
|
||||
static height = 2;
|
||||
static get length() {return Motion.width};
|
||||
static set length(n) {Motion.width = n};
|
||||
static get dimension() {return Motion.height};
|
||||
static set dimension(n) {Motion.height = n};
|
||||
|
||||
static random(width=Motion.width, height=Motion.height) {
|
||||
return Motion.from(Matrix.random(width, height));
|
||||
}
|
||||
static zero(width=Motion.width, height=Motion.height) {
|
||||
return Motion.from(Matrix.zero(width, height));
|
||||
}
|
||||
slice(...args) {
|
||||
return new Motion(...super.slice(...args));
|
||||
}
|
||||
// constructor(...vectors) {
|
||||
// console.log("motion_constructor:", vectors);
|
||||
// super(...vectors);
|
||||
// console.log(this);
|
||||
// // this.imap(t => new Vector(...t));
|
||||
// for (let i=0; i<this.length; i++) {
|
||||
// this[i] = new Vector(...this[i]);
|
||||
// }
|
||||
// }
|
||||
get position() {
|
||||
return new Vector(...this[0]);
|
||||
}
|
||||
set position(value) {
|
||||
this[0] = value;
|
||||
}
|
||||
get velocity() {
|
||||
return new Vector(...this[1]);
|
||||
}
|
||||
set velocity(value) {
|
||||
this[1] = value;
|
||||
}
|
||||
get acceleration() {
|
||||
return new Vector(...this[2]);
|
||||
}
|
||||
set acceleration(value) {
|
||||
this[2] = v;
|
||||
}
|
||||
get x() {
|
||||
return this[0][0];
|
||||
}
|
||||
set x(v) {
|
||||
this[0][0] = v;
|
||||
}
|
||||
get y() {
|
||||
return this[0][1];
|
||||
}
|
||||
set y(v) {
|
||||
this[0][1] = v;
|
||||
}
|
||||
get z() {
|
||||
return this[0][2];
|
||||
}
|
||||
set z(v) {
|
||||
this[0][2] = v;
|
||||
}
|
||||
|
||||
get vx() {
|
||||
return this[1][0];
|
||||
}
|
||||
set vx(v) {
|
||||
this[1][0] = v;
|
||||
}
|
||||
get vy() {
|
||||
return this[1][1];
|
||||
}
|
||||
set vy(v) {
|
||||
this[1][1] = v;
|
||||
}
|
||||
get vz() {
|
||||
return this[1][2];
|
||||
}
|
||||
set vz(v) {
|
||||
this[1][2] = v;
|
||||
}
|
||||
|
||||
get ax() {
|
||||
return this[2][0];
|
||||
}
|
||||
set ax(v) {
|
||||
this[2][0] = v;
|
||||
}
|
||||
get ay() {
|
||||
return this[2][1];
|
||||
}
|
||||
set ay(v) {
|
||||
this[2][1] = v;
|
||||
}
|
||||
get az() {
|
||||
return this[2][2];
|
||||
}
|
||||
set az(v) {
|
||||
this[2][2] = v;
|
||||
}
|
||||
update(dt=1) {
|
||||
// console.log(this)
|
||||
for (let x=this.width-2; x>=0; x--) {
|
||||
this[x].iadd(this[x+1].rmul(dt));
|
||||
}
|
||||
}
|
||||
updateFriction(friction=0.1) {
|
||||
this[1].irmul(1-friction);
|
||||
}
|
||||
show(ctx) {
|
||||
this.velocity.show(ctx, this.position, "blue");
|
||||
this.acceleration.show(ctx, this.position, "red");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default class Node {
|
||||
constructor(children, parent) {
|
||||
this.children = children;
|
||||
this.parent = parent;
|
||||
}
|
||||
get()
|
||||
}
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* A speed-improved perlin and simplex noise algorithms for 2D.
|
||||
*
|
||||
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
|
||||
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
|
||||
* Better rank ordering method by Stefan Gustavson in 2012.
|
||||
* Converted to Javascript by Joseph Gentle.
|
||||
*
|
||||
* Version 2012-03-09
|
||||
*
|
||||
* This code was placed in the public domain by its original author,
|
||||
* Stefan Gustavson. You may use it as you see fit, but
|
||||
* attribution is appreciated.
|
||||
*
|
||||
*/
|
||||
|
||||
(function(global){
|
||||
var module = global.noise = {};
|
||||
|
||||
function Grad(x, y, z) {
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
}
|
||||
|
||||
Grad.prototype.dot2 = function(x, y) {
|
||||
return this.x*x + this.y*y;
|
||||
};
|
||||
|
||||
Grad.prototype.dot3 = function(x, y, z) {
|
||||
return this.x*x + this.y*y + this.z*z;
|
||||
};
|
||||
|
||||
var grad3 = [new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0),
|
||||
new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1),
|
||||
new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)];
|
||||
|
||||
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];
|
||||
// To remove the need for index wrapping, double the iterator table length
|
||||
var perm = new Array(512);
|
||||
var gradP = new Array(512);
|
||||
|
||||
// This isn't a very good seeding function, but it works ok. It supports 2^16
|
||||
// different seed values. Write something better if you need more seeds.
|
||||
module.seed = function(seed) {
|
||||
if(seed > 0 && seed < 1) {
|
||||
// Scale the seed out
|
||||
seed *= 65536;
|
||||
}
|
||||
|
||||
seed = Math.floor(seed);
|
||||
if(seed < 256) {
|
||||
seed |= seed << 8;
|
||||
}
|
||||
|
||||
for(var i = 0; i < 256; i++) {
|
||||
var v;
|
||||
if (i & 1) {
|
||||
v = p[i] ^ (seed & 255);
|
||||
} else {
|
||||
v = p[i] ^ ((seed>>8) & 255);
|
||||
}
|
||||
|
||||
perm[i] = perm[i + 256] = v;
|
||||
gradP[i] = gradP[i + 256] = grad3[v % 12];
|
||||
}
|
||||
};
|
||||
|
||||
module.seed(0);
|
||||
|
||||
/*
|
||||
for(var i=0; i<256; i++) {
|
||||
perm[i] = perm[i + 256] = p[i];
|
||||
gradP[i] = gradP[i + 256] = grad3[perm[i] % 12];
|
||||
}*/
|
||||
|
||||
// Skewing and unskewing factors for 2, 3, and 4 dimensions
|
||||
var F2 = 0.5*(Math.sqrt(3)-1);
|
||||
var G2 = (3-Math.sqrt(3))/6;
|
||||
|
||||
var F3 = 1/3;
|
||||
var G3 = 1/6;
|
||||
|
||||
// 2D simplex noise
|
||||
module.simplex2 = function(xin, yin) {
|
||||
var n0, n1, n2; // Noise contributions from the three corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
var s = (xin+yin)*F2; // Hairy factor for 2D
|
||||
var i = Math.floor(xin+s);
|
||||
var j = Math.floor(yin+s);
|
||||
var t = (i+j)*G2;
|
||||
var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed.
|
||||
var y0 = yin-j+t;
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
if(x0>y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
i1=1; j1=0;
|
||||
} else { // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
i1=0; j1=1;
|
||||
}
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
var y1 = y0 - j1 + G2;
|
||||
var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
var y2 = y0 - 1 + 2 * G2;
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
i &= 255;
|
||||
j &= 255;
|
||||
var gi0 = gradP[i+perm[j]];
|
||||
var gi1 = gradP[i+i1+perm[j+j1]];
|
||||
var gi2 = gradP[i+1+perm[j+1]];
|
||||
// Calculate the contribution from the three corners
|
||||
var t0 = 0.5 - x0*x0-y0*y0;
|
||||
if(t0<0) {
|
||||
n0 = 0;
|
||||
} else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
var t1 = 0.5 - x1*x1-y1*y1;
|
||||
if(t1<0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * gi1.dot2(x1, y1);
|
||||
}
|
||||
var t2 = 0.5 - x2*x2-y2*y2;
|
||||
if(t2<0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * gi2.dot2(x2, y2);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70 * (n0 + n1 + n2);
|
||||
};
|
||||
|
||||
// 3D simplex noise
|
||||
module.simplex3 = function(xin, yin, zin) {
|
||||
var n0, n1, n2, n3; // Noise contributions from the four corners
|
||||
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
var s = (xin+yin+zin)*F3; // Hairy factor for 2D
|
||||
var i = Math.floor(xin+s);
|
||||
var j = Math.floor(yin+s);
|
||||
var k = Math.floor(zin+s);
|
||||
|
||||
var t = (i+j+k)*G3;
|
||||
var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed.
|
||||
var y0 = yin-j+t;
|
||||
var z0 = zin-k+t;
|
||||
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
|
||||
var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
|
||||
if(x0 >= y0) {
|
||||
if(y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; }
|
||||
else if(x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; }
|
||||
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }
|
||||
} else {
|
||||
if(y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; }
|
||||
else if(x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; }
|
||||
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; }
|
||||
}
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
var x1 = x0 - i1 + G3; // Offsets for second corner
|
||||
var y1 = y0 - j1 + G3;
|
||||
var z1 = z0 - k1 + G3;
|
||||
|
||||
var x2 = x0 - i2 + 2 * G3; // Offsets for third corner
|
||||
var y2 = y0 - j2 + 2 * G3;
|
||||
var z2 = z0 - k2 + 2 * G3;
|
||||
|
||||
var x3 = x0 - 1 + 3 * G3; // Offsets for fourth corner
|
||||
var y3 = y0 - 1 + 3 * G3;
|
||||
var z3 = z0 - 1 + 3 * G3;
|
||||
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
i &= 255;
|
||||
j &= 255;
|
||||
k &= 255;
|
||||
var gi0 = gradP[i+ perm[j+ perm[k ]]];
|
||||
var gi1 = gradP[i+i1+perm[j+j1+perm[k+k1]]];
|
||||
var gi2 = gradP[i+i2+perm[j+j2+perm[k+k2]]];
|
||||
var gi3 = gradP[i+ 1+perm[j+ 1+perm[k+ 1]]];
|
||||
|
||||
// Calculate the contribution from the four corners
|
||||
var t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
|
||||
if(t0<0) {
|
||||
n0 = 0;
|
||||
} else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * gi0.dot3(x0, y0, z0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
var t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
|
||||
if(t1<0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * gi1.dot3(x1, y1, z1);
|
||||
}
|
||||
var t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
|
||||
if(t2<0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * gi2.dot3(x2, y2, z2);
|
||||
}
|
||||
var t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
|
||||
if(t3<0) {
|
||||
n3 = 0;
|
||||
} else {
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * gi3.dot3(x3, y3, z3);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 32 * (n0 + n1 + n2 + n3);
|
||||
|
||||
};
|
||||
|
||||
// ##### Perlin noise stuff
|
||||
|
||||
function fade(t) {
|
||||
return t*t*t*(t*(t*6-15)+10);
|
||||
}
|
||||
|
||||
function lerp(a, b, t) {
|
||||
return (1-t)*a + t*b;
|
||||
}
|
||||
|
||||
// 2D Perlin Noise
|
||||
module.perlin2 = function(x, y) {
|
||||
// Find unit grid cell containing point
|
||||
var X = Math.floor(x), Y = Math.floor(y);
|
||||
// Get relative xy coordinates of point within that cell
|
||||
x = x - X; y = y - Y;
|
||||
// Wrap the integer cells at 255 (smaller integer period can be introduced here)
|
||||
X = X & 255; Y = Y & 255;
|
||||
|
||||
// Calculate noise contributions from each of the four corners
|
||||
var n00 = gradP[X+perm[Y]].dot2(x, y);
|
||||
var n01 = gradP[X+perm[Y+1]].dot2(x, y-1);
|
||||
var n10 = gradP[X+1+perm[Y]].dot2(x-1, y);
|
||||
var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1);
|
||||
|
||||
// Compute the fade curve value for x
|
||||
var u = fade(x);
|
||||
|
||||
// Interpolate the four results
|
||||
return lerp(
|
||||
lerp(n00, n10, u),
|
||||
lerp(n01, n11, u),
|
||||
fade(y));
|
||||
};
|
||||
|
||||
// 3D Perlin Noise
|
||||
module.perlin3 = function(x, y, z) {
|
||||
// Find unit grid cell containing point
|
||||
var X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z);
|
||||
// Get relative xyz coordinates of point within that cell
|
||||
x = x - X; y = y - Y; z = z - Z;
|
||||
// Wrap the integer cells at 255 (smaller integer period can be introduced here)
|
||||
X = X & 255; Y = Y & 255; Z = Z & 255;
|
||||
|
||||
// Calculate noise contributions from each of the eight corners
|
||||
var n000 = gradP[X+ perm[Y+ perm[Z ]]].dot3(x, y, z);
|
||||
var n001 = gradP[X+ perm[Y+ perm[Z+1]]].dot3(x, y, z-1);
|
||||
var n010 = gradP[X+ perm[Y+1+perm[Z ]]].dot3(x, y-1, z);
|
||||
var n011 = gradP[X+ perm[Y+1+perm[Z+1]]].dot3(x, y-1, z-1);
|
||||
var n100 = gradP[X+1+perm[Y+ perm[Z ]]].dot3(x-1, y, z);
|
||||
var n101 = gradP[X+1+perm[Y+ perm[Z+1]]].dot3(x-1, y, z-1);
|
||||
var n110 = gradP[X+1+perm[Y+1+perm[Z ]]].dot3(x-1, y-1, z);
|
||||
var n111 = gradP[X+1+perm[Y+1+perm[Z+1]]].dot3(x-1, y-1, z-1);
|
||||
|
||||
// Compute the fade curve value for x, y, z
|
||||
var u = fade(x);
|
||||
var v = fade(y);
|
||||
var w = fade(z);
|
||||
|
||||
// Interpolate
|
||||
return lerp(
|
||||
lerp(
|
||||
lerp(n000, n100, u),
|
||||
lerp(n001, n101, u), w),
|
||||
lerp(
|
||||
lerp(n010, n110, u),
|
||||
lerp(n011, n111, u), w),
|
||||
v);
|
||||
};
|
||||
|
||||
})(this);
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import Motion from './motion.js';
|
||||
|
||||
export default class Plane {
|
||||
static location = Motion.zero(3, 2)
|
||||
static units = Motion.zero(3, 2);
|
||||
static speed = 10;
|
||||
constructor(location=Plane.location, units=Plane.units, speed=Plane.speed) {
|
||||
this.location = location;
|
||||
this.units = units;
|
||||
this.speed = speed;
|
||||
}
|
||||
get position() {
|
||||
return this.location.position;
|
||||
}
|
||||
set position(value) {
|
||||
this.location.position = value;
|
||||
}
|
||||
get velocity() {
|
||||
return this.location.velocity;
|
||||
}
|
||||
set velocity(value) {
|
||||
this.location.velocity = value;
|
||||
}
|
||||
get acceleration() {
|
||||
return this.location.acceleration;
|
||||
}
|
||||
set acceleration(value) {
|
||||
this.location.acceleration = value;
|
||||
}
|
||||
get x() {
|
||||
return this.location[0][0];
|
||||
}
|
||||
set x(value) {
|
||||
this.location[0][0] = value;
|
||||
}
|
||||
get y() {
|
||||
return this.location[0][1];
|
||||
}
|
||||
set y(value) {
|
||||
this.location[0][1] = value;
|
||||
}
|
||||
get vx() {
|
||||
return this.location[1][0];
|
||||
}
|
||||
set vx(value) {
|
||||
this.location[1][0] = value;
|
||||
}
|
||||
get vy() {
|
||||
return this.location[1][1];
|
||||
}
|
||||
set vy(value) {
|
||||
this.location[1][1] = value;
|
||||
}
|
||||
get ax() {
|
||||
return this.location[2][0];
|
||||
}
|
||||
set ax(value) {
|
||||
this.location[2][0] = value;
|
||||
}
|
||||
get ay() {
|
||||
return this.location[2][1];
|
||||
}
|
||||
set ay(value) {
|
||||
this.location[2][1] = value;
|
||||
}
|
||||
get ux() {
|
||||
return this.units[0][0];
|
||||
}
|
||||
set ux(value) {
|
||||
this.units[0][0] = value;
|
||||
}
|
||||
get uy() {
|
||||
return this.units[0][1];
|
||||
}
|
||||
set uy(value) {
|
||||
this.units[0][1] = value;
|
||||
}
|
||||
get uvx() {
|
||||
return this.units[1][0];
|
||||
}
|
||||
set uvx(value) {
|
||||
this.units[1][0] = value;
|
||||
}
|
||||
get uvy() {
|
||||
return this.units[1][1];
|
||||
}
|
||||
set uvy(value) {
|
||||
this.units[1][1] = value;
|
||||
}
|
||||
get uax() {
|
||||
return this.units[2][0];
|
||||
}
|
||||
set uax(value) {
|
||||
this.units[2][0] = value;
|
||||
}
|
||||
get uay() {
|
||||
return this.units[2][1];
|
||||
}
|
||||
set uay(value) {
|
||||
this.units[2][1] = value;
|
||||
}
|
||||
toScreen(position) {
|
||||
return position.sub(this.location.position).tdot(this.units.position);
|
||||
}
|
||||
fromScreen(position) {
|
||||
return position.tdot(this.units.position.map(x => 1/x)).add(this.location.position);
|
||||
}
|
||||
zoom(value) {
|
||||
this.units[0].irmul(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class Point extends Vector {
|
||||
static radius = 1;
|
||||
static color = "#ffffff";
|
||||
static conversion = false;
|
||||
static average(...points) {
|
||||
return new Point(...Vector.average(...points));
|
||||
}
|
||||
static random(n=2) {
|
||||
return Point.from(Vector.random(n));
|
||||
}
|
||||
map(f) {
|
||||
return new Point(...super.map(f));
|
||||
}
|
||||
get r() {
|
||||
return this.radius;
|
||||
}
|
||||
set r(value) {
|
||||
this.radius = value;
|
||||
}
|
||||
show(
|
||||
context,
|
||||
color=Point.color,
|
||||
radius=Point.radius,
|
||||
conversion=Point.conversion
|
||||
) {
|
||||
context.beginPath();
|
||||
context.fillStyle = color;
|
||||
context.arc(this.x, this.y, radius, 0, 2*Math.PI, conversion);
|
||||
context.fill();
|
||||
context.closePath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import Point from './point.js';
|
||||
|
||||
let p1 = new Point(1, 2);
|
||||
let p2 = Point.random();
|
||||
|
||||
|
||||
console.log("I'm executed");
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import BasePolygon from './basepolygon.js';
|
||||
import Matrix from './matrix.js';
|
||||
import Segment from './segment.js';
|
||||
import Point from './point.js';
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class Polygon extends BasePolygon {
|
||||
static color = "#ffffff";
|
||||
static width=null;
|
||||
static fill=false;
|
||||
static w=3; // number of points (triangle)
|
||||
static h=2; // number of components (2d)
|
||||
static s=1; // size factor
|
||||
static get matrix() {
|
||||
return Matrix.zero(this.w, this.h);
|
||||
}
|
||||
static random(
|
||||
w=Polygon.w,
|
||||
h=Polygon.h,
|
||||
s=Polygon.s,
|
||||
color=Polygon.color,
|
||||
width=Polygon.width,
|
||||
fill=Polygon.fill
|
||||
) {
|
||||
let m = new Matrix(w);
|
||||
let v = Vector.random(w, 0, s);
|
||||
v.irdiv(v.sum);
|
||||
let a = 0;
|
||||
for (let i=0; i<w; i++) {
|
||||
a += v[i];
|
||||
m[i] = Vector.polar(s * Math.random(), 2*Math.PI*a);
|
||||
}
|
||||
return new this(m, color, width, fill);
|
||||
}
|
||||
constructor(
|
||||
matrix=Polygon.matrix,
|
||||
color=Polygon.color,
|
||||
width=Polygon.width,
|
||||
fill=Polygon.fill,
|
||||
) {
|
||||
super(color, width, fill);
|
||||
this.matrix = Matrix.from(matrix);
|
||||
}
|
||||
get points() {
|
||||
return Array.from(this.matrix);
|
||||
}
|
||||
set points(points) {
|
||||
this.matrix.set(points);
|
||||
}
|
||||
move(x, y) {
|
||||
|
||||
}
|
||||
get segments() {
|
||||
const segments = [];
|
||||
let p = this.matrix[this.matrix.length-1];
|
||||
for (const pi of this.matrix) {
|
||||
segments.push(new Segment(p, pi));
|
||||
p = pi;
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
get center() {
|
||||
return Point.average(...this.matrix);
|
||||
}
|
||||
set center(point) {
|
||||
const v = point.sub(this.center)
|
||||
this.translate(v);
|
||||
}
|
||||
|
||||
get angle(){
|
||||
//Retourne l'angle du polygone, lors de la création du polygone, celui-ci vaut 0
|
||||
const c = this.center;
|
||||
const p = this.points[0];
|
||||
return p.sub(c).angle;
|
||||
}
|
||||
|
||||
set angle(r) {
|
||||
this.rotate(r-this.angle);
|
||||
}
|
||||
translate(vector) {
|
||||
this.matrix.map(v => v.translate(vector));
|
||||
}
|
||||
rotate(angle, point=Point.zero) {
|
||||
this.matrix.map(v => v.rotate(angle, point));
|
||||
}
|
||||
show(ctx) {
|
||||
if (this.fill) {
|
||||
ctx.fillStyle = this.color;
|
||||
} else {
|
||||
ctx.strokeStyle = this.color;
|
||||
}
|
||||
if (this.lineWidth) {
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(...this.matrix[this.matrix.length-1]);
|
||||
for (const pt of this.matrix) {
|
||||
ctx.lineTo(...pt);
|
||||
}
|
||||
ctx.closePath();
|
||||
if (this.fill) {
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determine the distance of a point to the polygon.
|
||||
* @param {Point} point
|
||||
*/
|
||||
distance(point) {
|
||||
let d = 0;
|
||||
for (const p of this.matrix) {
|
||||
d += Point.distance(point, p);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
/**
|
||||
* Determine if the polygon contains a point
|
||||
* @param {*} point
|
||||
*/
|
||||
contains(p) {
|
||||
const points = this.matrix;
|
||||
var isInside = false;
|
||||
var minX = points[0].x, maxX = points[0].x;
|
||||
var minY = points[0].y, maxY = points[0].y;
|
||||
for (let n = 1; n < points.length; n++) {
|
||||
let q = points[n];
|
||||
minX = Math.min(q.x, minX);
|
||||
maxX = Math.max(q.x, maxX);
|
||||
minY = Math.min(q.y, minY);
|
||||
maxY = Math.max(q.y, maxY);
|
||||
}
|
||||
if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
|
||||
return false;
|
||||
}
|
||||
let i = 0, j = points.length - 1;
|
||||
for (i, j; i < points.length; j = i++) {
|
||||
if ( (points[i].y > p.y) != (points[j].y > p.y) &&
|
||||
p.x < (points[j].x - points[i].x) * (p.y - points[i].y) / (points[j].y - points[i].y) + points[i].x ) {
|
||||
isInside = !isInside;
|
||||
}
|
||||
}
|
||||
return isInside;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class Polynomial extends Vector {
|
||||
static fromRoots(roots) {
|
||||
let p = this.one;
|
||||
for (const root of roots) {
|
||||
p.imul(new this(root, 1))
|
||||
}
|
||||
return p;
|
||||
}
|
||||
/*
|
||||
Create a polynomial using the unpacked list of components.
|
||||
*/
|
||||
constructor(...components) {
|
||||
super(...components);
|
||||
}
|
||||
/*
|
||||
Evaluate the polynomial at a given value.
|
||||
*/
|
||||
call(x) {
|
||||
let v = 0;
|
||||
for (let i=0; i<this.length; i++) {
|
||||
v += x*this[i]**i;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
str() {
|
||||
let ws = [];
|
||||
let w = "";
|
||||
let c, j;
|
||||
for (let i=0; i<this.length; i++) {
|
||||
j = this.length-1-i;
|
||||
c = this[j];
|
||||
if (c!=0) {
|
||||
if (j==0) {
|
||||
w = String(c);
|
||||
} else if (j==1) {
|
||||
if (c==1) {
|
||||
w = "X";
|
||||
} else {
|
||||
w = String(c)+"X";
|
||||
}
|
||||
} else {
|
||||
if (c==1) {
|
||||
w = "X^"+String(j);
|
||||
} else {
|
||||
w = String(c)+"X^"+String(j);
|
||||
}
|
||||
}
|
||||
ws.push(w);
|
||||
}
|
||||
}
|
||||
let s = ws.join("+");
|
||||
if (s=="") {
|
||||
s = "0";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
/*
|
||||
Delete the zeros at the end of the polynomial.
|
||||
*/
|
||||
correct() {
|
||||
while (this[this.length-1]==0) {
|
||||
delete this[this.length-1];
|
||||
}
|
||||
}
|
||||
/*
|
||||
Return the degree of the polynomial.
|
||||
*/
|
||||
get degree() {
|
||||
return this.length;
|
||||
}
|
||||
/*
|
||||
Multiply the polynomial with another polynomial.
|
||||
*/
|
||||
mul(other) {
|
||||
let n = this.degree;
|
||||
let p = other.degree;
|
||||
let m = Math.max(n, p);
|
||||
this.iadapt(m);
|
||||
other.iadapt(m);
|
||||
let polynomial = new Polynomial();
|
||||
for (let k=0; k<m; k++) {
|
||||
let c = 0;
|
||||
for (let i=0; i<k; i++) {
|
||||
c += this[i] * other[k-i];
|
||||
}
|
||||
polynomial.push(c);
|
||||
}
|
||||
polynomial.correct();
|
||||
return polynomial;
|
||||
}
|
||||
factorize() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
export default class Quadtree {
|
||||
/*
|
||||
format :
|
||||
recursive case:
|
||||
tree = [tree1, tree2, tree3, tree4]
|
||||
base case:
|
||||
tree = [point1, point2, ...]
|
||||
|
||||
point = [x, y]
|
||||
2|3
|
||||
0|1
|
||||
*/
|
||||
constructor(tree=new Map(), chart=new Map(), nodes=3) {
|
||||
this.tree = tree;
|
||||
this.chart = chart;
|
||||
this.nodes = nodes;
|
||||
}
|
||||
create(points) {
|
||||
this.borders = this.getBorders(points);
|
||||
this.size = this.getSize(this.borders);
|
||||
this.tree = this.divide(points, ...this.size);
|
||||
}
|
||||
divide(points, sx, sy, hx=0, hy=0, n=0) {
|
||||
if (points.length<this.nodes) {return points}
|
||||
if (n>10) {return points}
|
||||
n += 1;
|
||||
sx /= 2;
|
||||
sy /= 2;
|
||||
const xmin = this.borders[0];
|
||||
const ymin = this.borders[1];
|
||||
const tree = new Map([
|
||||
[0, []], [1, []], [2, []], [3, []]
|
||||
]);
|
||||
for (const [x, y] of points) {
|
||||
if (y<hy+sx+ymin) {
|
||||
if (x<hx+sy+xmin) {
|
||||
tree.get(0).push([x, y]);
|
||||
} else {
|
||||
tree.get(1).push([x, y]);
|
||||
}
|
||||
} else {
|
||||
if (x<hx+xmin) {
|
||||
tree.get(2).push([x, y]);
|
||||
} else {
|
||||
tree.get(3).push([x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log(points.length, n, sx, hx);
|
||||
let x, y;
|
||||
for (let i=0; i<4; i++) {
|
||||
if (tree.get(i).length==0) {
|
||||
tree.delete(i);
|
||||
} else {
|
||||
x = i%2;
|
||||
y = Math.floor(i/2);
|
||||
console.log(i, x, y);
|
||||
tree.set(i,
|
||||
this.divide(
|
||||
tree.get(i), sx, sy, hx+sx*x, hy+sx*y, n
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
access(path) {
|
||||
let tree = this.tree;
|
||||
while (path.lenth!=0) {
|
||||
tree = tree.get(path.pop());
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
getBorders(points) {
|
||||
let xmin = Infinity;
|
||||
let ymin = Infinity;
|
||||
let xmax = -Infinity;
|
||||
let ymax = -Infinity;
|
||||
for (const [x, y] of points) {
|
||||
xmin = Math.min(xmin, x);
|
||||
ymin = Math.min(ymin, y);
|
||||
xmax = Math.max(xmax, x);
|
||||
ymax = Math.max(ymax, y);
|
||||
}
|
||||
return [xmin, ymin, xmax, ymax];
|
||||
}
|
||||
getSize(borders) {
|
||||
return [borders[2]-borders[0], borders[3]-borders[1]];
|
||||
}
|
||||
getPoints(tree) {
|
||||
const points = [];
|
||||
return points;
|
||||
}
|
||||
contains(x, y, r) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function test(n=1000) {
|
||||
let pts = [];
|
||||
for (let i=0; i<n; i++) {
|
||||
pts.push([Math.random(), Math.random()]);
|
||||
}
|
||||
|
||||
let q = new Quadtree();
|
||||
q.create(pts);
|
||||
return q;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import BasePolygon from './basepolygon.js';
|
||||
import Vector from './vector.js';
|
||||
import Point from './point.js';
|
||||
|
||||
export default class Rectangle extends BasePolygon {
|
||||
static color = "#ffffff";
|
||||
static lineWidth = 1;
|
||||
static of(x, y, w, h, ...args) {
|
||||
return this(x,y, w,h, ...args);
|
||||
}
|
||||
static from(a, ...args) {
|
||||
return new this(a.slice(0,2), a.slice(2), ...args)
|
||||
}
|
||||
static random() {
|
||||
return new this(Vector.random(), Vector.random());
|
||||
}
|
||||
|
||||
constructor(
|
||||
position,
|
||||
size,
|
||||
lineWidth=Rectangle.lineWidth,
|
||||
color=Rectangle.color,
|
||||
fill=Rectangle.fill,
|
||||
) {
|
||||
super(color, lineWidth, fill);
|
||||
this.position = new Vector(...position);
|
||||
this.size = new Vector(...size);
|
||||
}
|
||||
translate(vector) {
|
||||
this.position.iadd(vector);
|
||||
}
|
||||
get x() {
|
||||
return this.position[0]
|
||||
}
|
||||
set x(value) {
|
||||
this.position[0] = value;
|
||||
}
|
||||
get y() {
|
||||
return this.position[1];
|
||||
}
|
||||
set y(value) {
|
||||
this.position[1] = value;
|
||||
}
|
||||
get w() {
|
||||
return this.size[0];
|
||||
}
|
||||
set w(value) {
|
||||
this.size[0] = value;
|
||||
}
|
||||
get h() {
|
||||
return this.size[1];
|
||||
}
|
||||
set h(value) {
|
||||
this.size[1] = value;
|
||||
}
|
||||
get components() {
|
||||
return [...this.position, ...this.size];
|
||||
}
|
||||
set components(value) {
|
||||
this.position = value.slice(0, 2);
|
||||
this.size = value.slice(2, 4);
|
||||
}
|
||||
get points() {
|
||||
return [
|
||||
new Point(this.x, this.y),
|
||||
new Point(this.x+this.w, this.y),
|
||||
new Point(this.x+this.w, this.y+this.h),
|
||||
new Point(this.x, this.y+this.h)
|
||||
];
|
||||
}
|
||||
set points(value) {
|
||||
this.x = Math.min(value.map((p) => p.x));
|
||||
this.y = Math.min(value.map((p) => p.y));
|
||||
this.w = Math.max(value.map((p) => p.x))-this.x;
|
||||
this.h = Math.max(value.map((p) => p.y))-this.y;
|
||||
}
|
||||
get area() {
|
||||
return this.w*this.h;
|
||||
}
|
||||
show(ctx) {
|
||||
if (this.lineWidth) {
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
}
|
||||
if (this.fill) {
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fillRect(...this.components);
|
||||
} else {
|
||||
ctx.strokeStyle = this.color;
|
||||
ctx.strokeRect(...this.components);
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
import BasePolygon from './basepolygon.js';
|
||||
import Matrix from './matrix.js';
|
||||
import Point from './point.js';
|
||||
|
||||
export default class Segment extends BasePolygon {
|
||||
static lineWidth = 3; // pixels
|
||||
static color = "#ffffff";
|
||||
static random(lineWidth=super.width, color=super.color) {
|
||||
return new Segment(
|
||||
Point.random(),
|
||||
Point.random(),
|
||||
lineWidth,
|
||||
color)
|
||||
}
|
||||
constructor(p1=Point.zero2, p2=Point.zero2,
|
||||
lineWidth=Segment.lineWidth,
|
||||
color=Segment.color
|
||||
) {
|
||||
super();
|
||||
this.points = new Matrix(Point.from(p1), Point.from(p2));
|
||||
this.lineWidth = lineWidth;
|
||||
this.color = color;
|
||||
}
|
||||
get p1() {
|
||||
return this.points[0];
|
||||
}
|
||||
set p1(value) {
|
||||
this.points[0] = value;
|
||||
}
|
||||
get p2() {
|
||||
return this.points[1];
|
||||
}
|
||||
set p2(value) {
|
||||
this.points[1] = value;
|
||||
}
|
||||
translate(vector) {
|
||||
for (const p of this.points) {
|
||||
p.translate(vector);
|
||||
}
|
||||
}
|
||||
rotate(angle, vector=undefined) {
|
||||
vector = vector || this.center;
|
||||
for (const p of this.points) {
|
||||
p.rotate(angle, vector);
|
||||
}
|
||||
}
|
||||
get center() {
|
||||
return Vector.average(...this.points);
|
||||
}
|
||||
set center(v) {
|
||||
this.translate(v.sub(this.center));
|
||||
}
|
||||
get vector() {
|
||||
return this.p2.sub(this.p1);
|
||||
}
|
||||
set vector(v) {
|
||||
this.p2 = v.sub(this.p1);
|
||||
}
|
||||
get normal() {
|
||||
return this.vector.rot(Math.PI/2);
|
||||
}
|
||||
set normal(vector) {
|
||||
this.vector = vector.rot(-Math.PI/2);
|
||||
}
|
||||
get angle() {
|
||||
return this.vector.angle;
|
||||
}
|
||||
set angle(a) {
|
||||
this.rotate(a-this.angle);
|
||||
}
|
||||
get length() {
|
||||
return this.p1.sub(this.p2).norm
|
||||
}
|
||||
get line() {
|
||||
return new Line(this.p1, this.vector);
|
||||
}
|
||||
show(context) {
|
||||
context.context.lineWidth = this.lineWidth;
|
||||
context.strokeStyle = this.color;
|
||||
context.beginPath();
|
||||
context.moveTo(...this.p1);
|
||||
context.lineTo(...this.p2);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
}
|
||||
get base() {
|
||||
const [a, b] = this.vector.unit();
|
||||
const [c, d] = this.normal.unit();
|
||||
return new Matrix([a,b],[c,d]);
|
||||
}
|
||||
toBase(vector) {
|
||||
return new Vector(
|
||||
vector.dot(this.vector)/this.vector.norm,
|
||||
vector.dot(this.normal)/this.normal.norm
|
||||
);
|
||||
}
|
||||
contains(point, precision=1e-10) {
|
||||
const d = Point.distance(this.p1, point)+Point.distance(this.p2, point);
|
||||
return d-this.length <= precision;
|
||||
}
|
||||
crossLine(line) {
|
||||
const point = this.line.cross(line);
|
||||
if (this.contains(point)) {
|
||||
return point;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
crossSegment(segment) {
|
||||
const point = this.line.cross(segment.line);
|
||||
if (this.contains(point)) {
|
||||
if (segment.contains(point)) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
cross(figure) {
|
||||
if (this.figure instanceof Segment) {
|
||||
return this.crossSegment(segment);
|
||||
} else if (this.figure instanceof Line) {
|
||||
return this.crossLine(figure);
|
||||
} else if (this.figure instanceof Polygon) {
|
||||
return figure.crossSegment(segment);
|
||||
} else {
|
||||
throw `Collisions between ${this.constructor.name} and ${figure.constructor.name} are not dealt with`;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
export default class Server extends Connexion {
|
||||
constructor() {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const express = require('express'),
|
||||
app = express(),
|
||||
server = require('http').createServer(app),
|
||||
io = require('socket.io').listen(server),
|
||||
ent = require('ent'), // Permet de bloquer les caractères HTML (sécurité équivalente à htmlentities en PHP)
|
||||
os = require('os');
|
||||
|
||||
server.maxConnections = 2;
|
||||
|
||||
app.use(express.static('public'));
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile(__dirname + "/index.html");
|
||||
});
|
||||
|
||||
|
||||
io.sockets.on('connection', function (socket, pseudo) {
|
||||
// Dès qu'on nous donne un pseudo, on le stocke en variable de session et on informe les autres personnes
|
||||
socket.on('nouveau_client', function(pseudo) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import BasePolygon from './basepolygon.js';
|
||||
import Point from './point.js';
|
||||
import Vector from './vector.js';
|
||||
|
||||
export default class Square extends BasePolygon {
|
||||
static random(...args) {
|
||||
return new this(
|
||||
Math.random(-1, 1),
|
||||
Math.random(-1, 1),
|
||||
Math.random(0, 1),
|
||||
...args
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
x, y, s,
|
||||
color=BasePolygon.color,
|
||||
lineWidth=BasePolygon.lineWidth,
|
||||
fill=BasePolygon.fill
|
||||
) {
|
||||
super(color, lineWidth, fill);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.s = s;
|
||||
}
|
||||
get size() {return this.s;}
|
||||
set size(v) {this.s = v;}
|
||||
get side() {return this.s;}
|
||||
set side(v) {this.s = v;}
|
||||
get w() {return s;}
|
||||
set w(v) {this.s = v;}
|
||||
get h() {return this.s;}
|
||||
set h(v) {this.s = v;}
|
||||
get area() {return this.s**2;}
|
||||
get center() {
|
||||
return new Vector(this.x+this.s/2, this.y+this.s/2);
|
||||
}
|
||||
set center(vector) {
|
||||
this.x = vector.x - this.s/2;
|
||||
this.y = vector.y - this.s/2;
|
||||
}
|
||||
get points() {
|
||||
return [
|
||||
new Point(this.x, this.y),
|
||||
new Point(this.x+this.s, this.y),
|
||||
new Point(this.x+this.s, this.y+this.s),
|
||||
new Point(this.x, this.y+this.s)
|
||||
];
|
||||
}
|
||||
set points(pts) {
|
||||
this.x = Math.min(value.map(pts => pts.x));
|
||||
this.y = Math.min(value.map(pts => pts.y));
|
||||
// naive mapping because the information is redundant
|
||||
this.s = Math.max(value.map(pts => pts.x))-this.x;
|
||||
}
|
||||
get components() {
|
||||
return [this.x, this.y, this.z];
|
||||
}
|
||||
set components(cps) {
|
||||
this.x = cps[0];
|
||||
this.y = cps[1];
|
||||
this.s = cps[2];
|
||||
}
|
||||
show(ctx) {
|
||||
if (this.lineWidth) {
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
}
|
||||
if (this.fill) {
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fillRect(this.x, this.y, this.s, this.s);
|
||||
} else {
|
||||
ctx.strokeStyle = this.color;
|
||||
ctx.strokeRect(this.x, this.y, this.s, this.s);
|
||||
}
|
||||
}
|
||||
contains(p) {
|
||||
return this.x <= p[0] <= this.x+this.s &&
|
||||
this.y <= p[1] <= this.y+this.s;
|
||||
|
||||
}
|
||||
}
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
A tensor is either an array of tensors or an array of numbers.
|
||||
Every operation done on tensors is executed recursively.
|
||||
It makes it easier to use but maybe slower to compute with.
|
||||
*/
|
||||
export default class Tensor extends Array {
|
||||
/*
|
||||
Every tensor has a norm, order, format and size. Here are stored default values.
|
||||
*/
|
||||
static norm = 2;
|
||||
// static norm = 2;
|
||||
static order = 1;
|
||||
static format = [1];
|
||||
|
||||
static empty(...format) {
|
||||
format = format || this.format;
|
||||
let t = this.from(Array(format[0]));
|
||||
if (format.length>1) {
|
||||
for (let i = 0; i < format[0]; i++) {
|
||||
t[i] = this.empty(...format.slice(1, format.length));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
/**
|
||||
* Create a tensor filled with zero with given format.
|
||||
* @param {...Number} format
|
||||
*/
|
||||
static zero(...format) {
|
||||
format = format || this.format;
|
||||
let t = new Tensor(format[0]);
|
||||
if (format.length > 1) {
|
||||
for (let i = 0; i < format[0]; i++) {
|
||||
t[i] = Tensor.zero(...format.slice(1));
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < format[0]; i++) {
|
||||
t[i] = 0;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
/*
|
||||
Create a new tensor with the same format of the given one but filled with zeros.
|
||||
*/
|
||||
static zeroLike(tensor) {
|
||||
return this.zero(...tensor.format);
|
||||
}
|
||||
static fill(value, ...format) {
|
||||
let t = this.zero(...format);
|
||||
return t.fill(value);
|
||||
}
|
||||
static fillLike(value, tensor) {
|
||||
return this.fill(value, ...tensor.format);
|
||||
}
|
||||
static random(...format) {
|
||||
format = format || this.format;
|
||||
let t = new Tensor(format[0]);
|
||||
if (format.length==0) {return t};
|
||||
for (let i = 0; i < format[0]; i++) {
|
||||
if (format.length==1) {
|
||||
t[i] = Math.random();
|
||||
} else {
|
||||
t[i] = Tensor.random(...format.slice(1));
|
||||
}
|
||||
}
|
||||
return this.from(t);
|
||||
}
|
||||
/*
|
||||
Does the sum of all tensors.
|
||||
*/
|
||||
static sum(...tensors) {
|
||||
return tensors.reduce((x, y) => x.add(y));
|
||||
}
|
||||
/**
|
||||
* Does the average of all tensors.
|
||||
* @param {...Tensor} Tensor
|
||||
*/
|
||||
static average(...tensors) {
|
||||
return Tensor.sum(...tensors).rdiv(tensors.length);
|
||||
}
|
||||
/**
|
||||
* Multiply all the tensors together.
|
||||
* @param {...Tensor} tensors
|
||||
*/
|
||||
static prod(...tensors) {
|
||||
return tensors.reduce((x, y) => x.mul(y));
|
||||
}
|
||||
/**
|
||||
* Copy a tensor.
|
||||
* @param {Tensor} tensor
|
||||
*/
|
||||
static copy(tensor) {
|
||||
return tensor.slice(0);
|
||||
}
|
||||
/**
|
||||
* Determine the distance between multiple tensors.
|
||||
* @param {...Tensor} tensors
|
||||
*/
|
||||
static distance(...tensors) {
|
||||
if (tensors.length==2) {
|
||||
return tensors[0].sub(tensors[1]).norm;
|
||||
}
|
||||
let d = 0;
|
||||
let t = undefined;
|
||||
for (const ti of tensors) {
|
||||
if (t) {
|
||||
d += Tensor.distance(t, ti);
|
||||
}
|
||||
t = ti;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
/*
|
||||
Convert an array of arrays into a tensor of tensors using
|
||||
a recursive approach.
|
||||
*/
|
||||
static convert(v, type=this) {
|
||||
if (v instanceof Array) {
|
||||
if (v[0] instanceof Array) {
|
||||
return v.map(t => this.convert(t, type=type));
|
||||
} else {
|
||||
return type.from(v);
|
||||
}
|
||||
} else {
|
||||
return type.from(v);
|
||||
}
|
||||
}
|
||||
/*
|
||||
The constructor takes an array of arrays as input and makes
|
||||
it a tensor of tensors.
|
||||
*/
|
||||
// constructor(...array) {
|
||||
// if (array.length==1) {
|
||||
// super(1);
|
||||
// this[0] = array[0];
|
||||
// } else {
|
||||
// // super(...Tensor.convert(array));
|
||||
// super(...array);
|
||||
// }
|
||||
// }
|
||||
/*
|
||||
Return the order of the tensor.
|
||||
*/
|
||||
get order() {
|
||||
if (this[0] instanceof Tensor) {
|
||||
return this[0].order + 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Return the format of the tensor.
|
||||
*/
|
||||
get format() {
|
||||
if (this[0] instanceof Tensor) {
|
||||
return [this.length].concat(this[0].format);
|
||||
} else {
|
||||
return [this.length];
|
||||
}
|
||||
}
|
||||
/*
|
||||
Return the size of the tensor which is the product of the format.
|
||||
*/
|
||||
get size() {
|
||||
return this.format.reduce(Math.imul);
|
||||
}
|
||||
/*
|
||||
Return the norm of the tensor.
|
||||
*/
|
||||
get norm() {
|
||||
if (this[0] instanceof Tensor) {
|
||||
return this.map(t => t.norm ** Tensor.norm).reduce((a, b) => a + b)**(1/Tensor.norm);
|
||||
} else {
|
||||
return this.map(t => t ** Tensor.norm).reduce((a, b) => a + b)**(1/Tensor.norm);;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Set the value of the tensor to the given tensor.
|
||||
*/
|
||||
set(t) {
|
||||
for (let i=0; i<Math.max(t.length, this.length); i++) {
|
||||
this[i] = t[i];
|
||||
}
|
||||
}
|
||||
/*
|
||||
Set the value of the tensor to the given tensor recursively.
|
||||
*/
|
||||
rset(t) {
|
||||
if (t[0] instanceof Array) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
this[i].rset(t[i]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
this[i] = t[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// isort(f) {
|
||||
// this.set(this.sort(f));
|
||||
// }
|
||||
/*
|
||||
Adapt the tensor length to the given one, by adding zero-tensors or deleting terms.
|
||||
*/
|
||||
adapt(n) {
|
||||
let t = Tensor.copy(this);
|
||||
if (n > this.length) {
|
||||
for (let i=0; i<n-this.length; i++) {
|
||||
t.push(Tensor.zero(...t[0].format));
|
||||
}
|
||||
} else {
|
||||
for (let i=0; i<this.length-n; i++) {
|
||||
delete t[t.length-1];
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
iadapt(n) {
|
||||
this.set(this.adapt(n));
|
||||
}
|
||||
imap(f) {
|
||||
for (let i=0; i<this.length; i++) {
|
||||
this[i] = f(this[i]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Map that is applied to the lowest elements only.
|
||||
*/
|
||||
rmap(f) {
|
||||
if (this[0] instanceof Tensor) {
|
||||
return this.map(t => t.rmap(f));
|
||||
} else {
|
||||
return this.map(f);
|
||||
}
|
||||
}
|
||||
irmap(f) {
|
||||
if (this[0] instanceof Tensor) {
|
||||
for (let i=0; i<this.length; i++) {
|
||||
this[i].irmap(f);
|
||||
}
|
||||
} else {
|
||||
this.imap(f);
|
||||
}
|
||||
}
|
||||
neg() {
|
||||
return this.rmap(t => -t);
|
||||
}
|
||||
ineg() {
|
||||
this.irmap(t => -t);
|
||||
}
|
||||
inv() {
|
||||
return this.rmap(t => 1 / t);
|
||||
}
|
||||
iinv() {
|
||||
this.irmap(t => 1 / t);
|
||||
}
|
||||
floor() {
|
||||
return this.rmap(Math.floor);
|
||||
}
|
||||
ifloor() {
|
||||
this.irmap(Math.floor);
|
||||
}
|
||||
rmul(k) {
|
||||
return this.rmap(t => k * t);
|
||||
}
|
||||
irmul(k) {
|
||||
this.irmap(t => k * t);
|
||||
}
|
||||
rdiv(k) {
|
||||
return this.rmap(t => t/k);
|
||||
}
|
||||
irdiv(k) {
|
||||
this.irmap(t => t/k);
|
||||
}
|
||||
pow(k) {
|
||||
return this.rmap(x => x ** k)
|
||||
}
|
||||
ipow(k) {
|
||||
this.irmap(x => x ** k);
|
||||
}
|
||||
round(k) {
|
||||
return this.rmap(x => Math.round(x*10**k)/10**k);
|
||||
}
|
||||
iround(k) {
|
||||
this.irmap(x => Math.round(x*10**k)/10**k);
|
||||
}
|
||||
fill(k) {
|
||||
return this.rmap(t => k);
|
||||
}
|
||||
ifill(k) {
|
||||
this.set(this.fill(k));
|
||||
}
|
||||
// component wise operations
|
||||
map2(tensor, f) {
|
||||
let m = Math.max(this.length, tensor.length);
|
||||
let v = this.copy();
|
||||
if (this[0] instanceof Tensor) {
|
||||
for (let i=0; i<m; i++) {
|
||||
v[i] = v.map2(tensor[i], f);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < m; i++) {
|
||||
v[i] = f(this[i], tensor[i]);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
imap2(tensor, f) {
|
||||
let m = Math.max(this.length, tensor.length);
|
||||
if (this[0] instanceof Tensor) {
|
||||
for (let i=0; i<m; i++) {
|
||||
this[i].imap2(tensor[i], f);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < m; i++) {
|
||||
this[i] = f(this[i], tensor[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
mul(tensor) {
|
||||
return this.map2(tensor, (x,y) => x*y);
|
||||
}
|
||||
imul(tensor) {
|
||||
this.imap2(tensor, (x,y) => x*y);
|
||||
}
|
||||
add(tensor) {
|
||||
return this.map2(tensor, (x,y) => x+y);
|
||||
}
|
||||
iadd(tensor) {
|
||||
this.imap2(tensor, (x,y) => x+y);
|
||||
}
|
||||
radd(k) {
|
||||
return this.rmap(t => t+k);
|
||||
}
|
||||
iradd(k) {
|
||||
this.irmap(t => t+k);
|
||||
}
|
||||
rsub(k) {
|
||||
return this.rmap(t => t-k);
|
||||
}
|
||||
irsub(k) {
|
||||
this.irmap(t => t-k);
|
||||
}
|
||||
sub(tensor) {
|
||||
return this.map2(tensor, (x,y) => x-y);
|
||||
}
|
||||
isub(tensor) {
|
||||
this.imap2(tensor, (x,y) => x-y);
|
||||
}
|
||||
dot(tensor) {
|
||||
return Math.sum(this.mul(tensor))
|
||||
}
|
||||
copy() {
|
||||
return this.slice(0);
|
||||
}
|
||||
/*
|
||||
Reshape the tensor to another given format.
|
||||
*/
|
||||
reshape(...format) {
|
||||
throw "Most difficult function to implement.";
|
||||
}
|
||||
flatten() {
|
||||
if (this.order == 1) {
|
||||
return Tensor.from(this);
|
||||
} else {
|
||||
let t = new Tensor();
|
||||
for (const ti of this) {
|
||||
t = t.concat(ti.flatten());
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import Tensor from './tensor.js';
|
||||
|
||||
let t1 = new Tensor1([1,2], [3,4]);
|
||||
let t2 = new Tensor1([3,4], [5,6]);
|
||||
|
||||
let ta = t1.add(t2);
|
||||
console.log("addition:", ta);
|
||||
|
||||
let tn = t1.neg();
|
||||
console.log("negation:", tn);
|
||||
|
||||
|
||||
let ts = t1.sub(t2);
|
||||
console.log("substraction:", ts);
|
||||
|
||||
let format = [1,2,3];
|
||||
console.log(Tensor1.zero(format));
|
||||
|
||||
// iadd
|
||||
// isub
|
||||
// imul
|
||||
@@ -0,0 +1,27 @@
|
||||
export function test(f, args=[], iterations=10**6) {
|
||||
const start = performance.now();
|
||||
for(let i=0; i<iterations; i++ ){
|
||||
f(...args);
|
||||
};
|
||||
return performance.now() - start;
|
||||
}
|
||||
|
||||
|
||||
export function compare(f1, f2, args=[], n=10**6, comparison=(x,y)=>x/y) {
|
||||
const t1 = test(f1, args, n);
|
||||
const t2 = test(f2, args, n);
|
||||
return comparison(t1,t2);
|
||||
}
|
||||
|
||||
export function analyse(fs=[], args=[], iterations=10**6) {
|
||||
const results = [];
|
||||
for(let i=0; i<fs.length; i++ ){
|
||||
results.push(test(fs[i],args,iterations));
|
||||
};
|
||||
const m = Math.max(...results);
|
||||
for(let i=0; i<fs.length; i++ ){
|
||||
results[i]/=m;
|
||||
};
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
export function map2str(map) {
|
||||
let str = "{";
|
||||
for (const [key, value] of map) {
|
||||
str += key;
|
||||
str += ":"
|
||||
str += String(value);
|
||||
str += ","
|
||||
}
|
||||
return str.slice(0, str.length-1)+"}";
|
||||
}
|
||||
|
||||
export function str2map(str) {
|
||||
str = str.replace("{","").replace("}","");
|
||||
let m = new Map();
|
||||
let key, value;
|
||||
for (const parameters of str.split(",")) {
|
||||
[key, value] = parameters.split(":")
|
||||
m.set(key, eval(value));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
export function tree2str(tree) {
|
||||
str = "{";
|
||||
for (const [key, value] of tree) {
|
||||
str += key;
|
||||
str += ":"
|
||||
if (value instanceof Map) {
|
||||
str += tree2str(String(value))
|
||||
} else {
|
||||
str += value;
|
||||
}
|
||||
str += ","
|
||||
}
|
||||
return str.slice(0, str.length-1)+"}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function prod(...values) {
|
||||
if (values.length>1) {
|
||||
return values[0] * prod(...values.slice(1, values.length));
|
||||
} else {
|
||||
return values[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function round(r, n) {
|
||||
return Math.round(r*10**n)/10**n;
|
||||
}
|
||||
|
||||
let sigmoid = (x) => 1/(1+Math.exp(-x));
|
||||
|
||||
export function hash(s){
|
||||
return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default class Torsor extends VectorField {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import Dict from './dict.js';
|
||||
export default class Tree extends Dict {
|
||||
paths(path=[]) {
|
||||
const ps = [];
|
||||
for (const [k, v] of this) {
|
||||
if (v instanceof Tree) {
|
||||
ps.push(...v.paths(path+[k]));
|
||||
} else {
|
||||
ps.push(path+[k]);
|
||||
}
|
||||
}
|
||||
return ps;
|
||||
}
|
||||
iforEach(f) {
|
||||
for (const [k,v] of this) {
|
||||
f(this[k]);
|
||||
}
|
||||
}
|
||||
imap(f) {
|
||||
for (const [k, v] of this) {
|
||||
this[k] = f(v);
|
||||
}
|
||||
}
|
||||
irmap(f) {
|
||||
for (const [k, v] of this) {
|
||||
if (v instanceof Tree) {
|
||||
v.irmap(f);
|
||||
} else {
|
||||
this[k] = f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traverse() {
|
||||
|
||||
}
|
||||
filter(condition) {
|
||||
const l = [];
|
||||
for (const [k,v] of this) {
|
||||
if (condition(k, v)) {
|
||||
l.push(v);
|
||||
}
|
||||
if (v instanceof Tree) {
|
||||
l.push(...v.filter(key));
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
findOne(key) {
|
||||
for (const [k,v] of this) {
|
||||
if (k==key) {
|
||||
return v;
|
||||
}
|
||||
if (v instanceof Tree) {
|
||||
return v.findOne(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
findAll(key) {
|
||||
const l = [];
|
||||
for (const [k,v] of this) {
|
||||
if (k==key) {
|
||||
l.push(v);
|
||||
}
|
||||
if (v instanceof Tree) {
|
||||
l.push(...v.findAll(key));
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
rget(path, v) {
|
||||
const k = path[0];
|
||||
path = path.slice(1);
|
||||
if (path.length==0) {
|
||||
return this.get(k);
|
||||
}
|
||||
return this.get(k).rget(path);
|
||||
}
|
||||
rset(path, v) {
|
||||
const k = path[0];
|
||||
path = path.slice(1);
|
||||
if (path.length==0) {
|
||||
return this.set(k, v);
|
||||
}
|
||||
return this.get(k).rset(path);
|
||||
}
|
||||
getAll() {
|
||||
const all = [];
|
||||
for (const [k,v] of this) {
|
||||
if (v instanceof Tree) {
|
||||
all.push(...v.getAll());
|
||||
} else {
|
||||
all.push(v);
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
}
|
||||
|
||||
//t = new Tree([[1,2],[3,new Tree([[1,2]])]]);
|
||||
@@ -0,0 +1,36 @@
|
||||
export default class Triangle extends Polygon {
|
||||
// number of points is always 3
|
||||
static get length() {return 3;}
|
||||
static get w() {return 3;}
|
||||
static get width() {return 3;}
|
||||
static random(
|
||||
s=1,
|
||||
color=this.color,
|
||||
lineWidth=this.lineWidth,
|
||||
fill=this.fill
|
||||
) {
|
||||
return new this(
|
||||
Matrix.random(this.w, this.h).rmul(s),
|
||||
color, lineWidth, fill);
|
||||
}
|
||||
constructor(matrix, ...args) {
|
||||
super(matrix, ...args)
|
||||
}
|
||||
get area() {
|
||||
const [a,b,c] = this.segments.map(s => s.length);
|
||||
console.log(a, b, c);
|
||||
return 1/4*Math.sqrt((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c));
|
||||
}
|
||||
contains(p) {
|
||||
const [p0, p1, p2] = this.matrix;
|
||||
const dX = p.x-p2.x;
|
||||
const dY = p.y-p2.y;
|
||||
const dX21 = p2.x-p1.x;
|
||||
const dY12 = p1.y-p2.y;
|
||||
const D = dY12*(p0.x-p2.x) + dX21*(p0.y-p2.y);
|
||||
const s = dY12*dX + dX21*dY;
|
||||
const t = (p2.y-p0.y)*dX + (p0.x-p2.x)*dY;
|
||||
if (D<0) return s<=0 && t<=0 && s+t>=D;
|
||||
return s>=0 && t>=0 && s+t<=D;
|
||||
}
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
import Tensor from './tensor.js';
|
||||
|
||||
export default class Vector extends Tensor {
|
||||
static length = 2;
|
||||
// Mathematically a vector doesn't have a dimension
|
||||
// only a length we abuse of this term to qualify
|
||||
// the length, same goes for size.
|
||||
static get dim() {return this.length;}
|
||||
static set dim(v) {this.length = v;}
|
||||
static get size() {return this.length;}
|
||||
static set size(v) {this.length = v;}
|
||||
static color = "#ffffff";
|
||||
static norm = 2; // euclidan norm
|
||||
static lineWidth = 2; // in pixels
|
||||
static zero(...format) {
|
||||
return this.from(Tensor.zero(...format));
|
||||
}
|
||||
static one(n) {
|
||||
return this.fill(1, length);
|
||||
}
|
||||
static copy = this.from; // Abusive
|
||||
static empty(n=0) {
|
||||
return this.from(Array(n));
|
||||
}
|
||||
static get zero2() {
|
||||
return new this(0, 0);
|
||||
}
|
||||
static get zero3() {
|
||||
return new this(0, 0, 0);
|
||||
}
|
||||
static random(dim = this.dim, min=-1, max=1) {
|
||||
let v = new this(dim);
|
||||
for (let i = 0; i < dim; i++) {
|
||||
v[i] = (Math.random()*(max-min))+min;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
static randoms(n, dim = this.dim) {
|
||||
let vs = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
vs.push(this.random(dim));
|
||||
}
|
||||
return vs;
|
||||
}
|
||||
static polar(norm, angle) {
|
||||
return new this(
|
||||
norm * Math.cos(angle),
|
||||
norm * Math.sin(angle)
|
||||
);
|
||||
}
|
||||
static fill(value, dim = this.dim) {
|
||||
let v = new this(dim);
|
||||
for (let i = 0; i < dim; i++) {
|
||||
v[i] = value;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
static adapt(dim, ...vectors) {
|
||||
// let dim = Math.max(...vectors.map(v => v.dim));
|
||||
for (let i = 0; i < vectors.length; i++) {
|
||||
vectors[i].push(...Array(dim - vectors[i].dim).fill(0));
|
||||
}
|
||||
}
|
||||
get x() {
|
||||
return this[0];
|
||||
}
|
||||
set x(value) {
|
||||
this[0] = value;
|
||||
}
|
||||
get y() {
|
||||
return this[1];
|
||||
}
|
||||
set y(value) {
|
||||
this[1] = value;
|
||||
}
|
||||
get z() {
|
||||
return this[2];
|
||||
}
|
||||
set z(value) {
|
||||
this[2] = value;
|
||||
}
|
||||
get sum() {
|
||||
return this.reduce((x, y) => x+y);
|
||||
}
|
||||
get norm() {
|
||||
return (this.map(x => x ** Vector.norm).reduce((x, y) => x + y))**(1/Vector.norm);
|
||||
}
|
||||
set norm(value) {
|
||||
this.irmul(value/this.norm);
|
||||
}
|
||||
// The angle of a vector can only make sense
|
||||
// when applied on 2d vectors.
|
||||
// This is an abusive way to generalize for
|
||||
// simple usage.
|
||||
get angle() {
|
||||
return Math.atan2(this.y,this.x);
|
||||
}
|
||||
set angle(value) {
|
||||
const n = this.norm;
|
||||
this.x = n*Math.cos(value);
|
||||
this.y = n*Math.sin(value);
|
||||
}
|
||||
get components() {
|
||||
return Array.from(this);
|
||||
}
|
||||
set components(values) {
|
||||
this.set(values);
|
||||
}
|
||||
get dim() {
|
||||
return this.length;
|
||||
}
|
||||
set dim(value) {
|
||||
this.length = value;
|
||||
}
|
||||
get inv() {
|
||||
return this.map(x => 1 / x);
|
||||
}
|
||||
get unit() {
|
||||
let m = Math.max(...this);
|
||||
return this.map(x => x / m);
|
||||
}
|
||||
tdot(other) {
|
||||
const v = new Vector(this.length);
|
||||
for (let i=0; i<this.length; i++) {
|
||||
v[i] = this[i]*other[i];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
dot(other) {
|
||||
let r = 0;
|
||||
for (const [r1, r2] of zip(this, other)) {
|
||||
r += r1*r2;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
project(other) {
|
||||
return other.rmul(this.dot(other)/other.norm)
|
||||
}
|
||||
iproject(other) {
|
||||
this.set(this.project(other));
|
||||
}
|
||||
towards(vector) {
|
||||
return Vector.polar(this.norm, vector.sub(this).angle);
|
||||
}
|
||||
itowards(vector) {
|
||||
this.angle = vector.sub(this).angle;
|
||||
}
|
||||
translate(vector) {
|
||||
this.iadd(vector);
|
||||
}
|
||||
rotate(angle, vector=undefined) {
|
||||
vector = vector || Vector.zero(Vector.length);
|
||||
this.isub(vector)
|
||||
this.angle += angle;
|
||||
this.iadd(vector);
|
||||
}
|
||||
irot(angle, vector=undefined) {
|
||||
this.rotate(angle, vector);
|
||||
}
|
||||
rot(angle, vector=undefined) {
|
||||
vector = vector || Vector.zero(Vector.length);
|
||||
const v = this.sub(vector)
|
||||
v.angle += angle;
|
||||
v.iadd(vector);
|
||||
return v;
|
||||
}
|
||||
equals(vector) {
|
||||
for (let i = 0; i < Math.max(vector.dim, this.dim); i++) {
|
||||
if (this[i] != vector[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
colinear(vector, error = 10 ** -10) {
|
||||
return this.mul(vector).norm < error;
|
||||
}
|
||||
fill(value) {
|
||||
return Vector.from(super.fill(value));
|
||||
}
|
||||
ifill(value) {
|
||||
for (let i = 0; i < this.dim; i++) {
|
||||
this[i] = value;
|
||||
}
|
||||
}
|
||||
map(f) {
|
||||
return Vector.from(super.map(f));
|
||||
}
|
||||
imap(f) {
|
||||
for (let i = 0; i < this.dim; i++) {
|
||||
this[i] = f(this[i]);
|
||||
}
|
||||
}
|
||||
slice(a, b) {
|
||||
return Vector.from(super.slice(a, b));
|
||||
}
|
||||
map2(vector, f) {
|
||||
let m = Math.max(this.dim, vector.dim);
|
||||
let v = Vector.empty(m);
|
||||
for (let i = 0; i < m; i++) {
|
||||
v[i] = (f(this[i], vector[i]));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
imap2(vector, f) {
|
||||
let m = Math.max(this.dim, vector.dim);
|
||||
for (let i = 0; i < m; i++) {
|
||||
this[i] = (f(this[i], vector[i]));
|
||||
}
|
||||
}
|
||||
adapt(n) {
|
||||
let v = Vector.copy(this);
|
||||
if (n>this.length) {
|
||||
for (let i=0; i<n-this.length; i++) {
|
||||
v.push(0);
|
||||
}
|
||||
} else {
|
||||
for (let i=0; i<this.length-n; i++) {
|
||||
delete v[v.length-1];
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
// prod(vector) {
|
||||
// // Vectorial product
|
||||
// throw "Not implemented error";
|
||||
// }
|
||||
div(k) {
|
||||
return this.rmul(1 / k);
|
||||
}
|
||||
idiv(k) {
|
||||
this.imul(1 / k);
|
||||
}
|
||||
ifloor() {
|
||||
this.imap(Math.floor);
|
||||
}
|
||||
floor() {
|
||||
return this.map(Math.floor);
|
||||
}
|
||||
limit(vmin, vmax) {
|
||||
let m = Math.max(this.length, vmin.length, vmax.length);
|
||||
for (let i=0; i<m; i++) {
|
||||
this[i] = Math.max(this[i], vmin[i]);
|
||||
this[i] = Math.min(this[i], vmax[i]);
|
||||
}
|
||||
}
|
||||
call(point) {
|
||||
return Point.from(this.add(point));
|
||||
}
|
||||
get normalized() {
|
||||
return this.rdiv(this.norm);
|
||||
}
|
||||
/**
|
||||
* Shows an arrow.
|
||||
*/
|
||||
show(
|
||||
ctx,
|
||||
position=Vector.zero(this.dim),
|
||||
color=Vector.color,
|
||||
lineWidth=Vector.lineWidth,
|
||||
factor=10,
|
||||
angle=Math.PI/6
|
||||
) {
|
||||
ctx.beginPath();
|
||||
ctx.context.lineWidth = lineWidth;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.moveTo(...position);
|
||||
const p = this.add(position)
|
||||
ctx.lineTo(...p);
|
||||
const v = this.rdiv(-factor);
|
||||
v.rotate(angle)
|
||||
ctx.lineTo(...v.add(p));
|
||||
v.rotate(-2*angle)
|
||||
ctx.moveTo(...p)
|
||||
ctx.lineTo(...v.add(p));
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
closest(vectors) {
|
||||
let a = Infinity;
|
||||
let b, v;
|
||||
for (const vi of vectors) {
|
||||
b = this.sub(vi).norm;
|
||||
if (a > b) {
|
||||
v = vi;
|
||||
a = b;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
closestIndex(vectors) {
|
||||
let a = Infinity;
|
||||
let b;
|
||||
let i = 0,
|
||||
j = 0;
|
||||
for (const vi of vectors) {
|
||||
b = this.sub(vi).norm;
|
||||
if (a > b) {
|
||||
a = b;
|
||||
j = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
farthest(vectors) {
|
||||
let a = 0;
|
||||
let b, v;
|
||||
for (const vi of vectors) {
|
||||
b = this.sub(vi).norm;
|
||||
if (a < b) {
|
||||
v = vi;
|
||||
a = b;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
farthestIndex(vectors) {
|
||||
let a = Infinity;
|
||||
let b;
|
||||
let i = 0,
|
||||
j = 0;
|
||||
for (const vi of vectors) {
|
||||
b = this.sub(vi).norm;
|
||||
if (a < b) {
|
||||
a = b;
|
||||
j = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
get directionCosines() {
|
||||
for (let i=0; i<this.length; i++) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Vector2 extends Vector {
|
||||
// force the dimension to be 2
|
||||
static get dim() {return 2;}
|
||||
static polar(norm, angle) {
|
||||
return new this(
|
||||
norm * Math.cos(angle),
|
||||
norm * Math.sin(angle)
|
||||
);
|
||||
}
|
||||
constructor(x, y) {
|
||||
super(x, y);
|
||||
}
|
||||
get angle() {
|
||||
return Math.atan2(this[1],this[0]);
|
||||
}
|
||||
set angle(value) {
|
||||
this[0] = this.norm*Math.cos(value);
|
||||
this[1] = this.norm*Math.sin(value);
|
||||
}
|
||||
}
|
||||
|
||||
export class Vector3 extends Vector {
|
||||
// force the dimension to be 3
|
||||
static get dim() {return 3;}
|
||||
constructor(x, y, z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
cross(vector) {
|
||||
return new Vector(
|
||||
this[2]*vector[3]-this[3]*vector[2],
|
||||
this[3]*vector[1]-this[1]*vector[3],
|
||||
this[1]*vector[2]-this[2]*vector[1]
|
||||
);
|
||||
}
|
||||
rotate(angle, vector) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Wikipedia:
|
||||
* In vector calculus and physics, a vector field is an
|
||||
* assignment of a vector to each point in a subset of space.
|
||||
*/
|
||||
export class TensorField {
|
||||
constructor(f) {
|
||||
this.f = f;
|
||||
}
|
||||
call(vector) {
|
||||
return this.f(vector);
|
||||
}
|
||||
add(vf) {
|
||||
return new VectorField(
|
||||
function add(f1, f2, x) {
|
||||
return f1(x).add(f2(x));
|
||||
}.bind(null, this.f, vf.f));
|
||||
}
|
||||
sub(vf) {
|
||||
return new VectorField(function sub(f1, f2, x) {
|
||||
return f1(x).sub(f2(x));
|
||||
}.bind(null, this.f, vf.f));
|
||||
}
|
||||
mul(vf) {
|
||||
return new VectorField(function mul(f1, f2, x) {
|
||||
return f1(x).mul(f2(x));
|
||||
}.bind(null, this.f, vf.f));
|
||||
}
|
||||
neg() {
|
||||
return new VectorField(function neg(f, x) {
|
||||
return f(x).neg();
|
||||
}.bind(null, this.f));
|
||||
}
|
||||
vadd(v) {
|
||||
return new VectorField(function vadd(f1, v, x) {
|
||||
return f1(x).add(v);
|
||||
}.bind(null, this.f, v));
|
||||
}
|
||||
vsub(v) {
|
||||
return new VectorField(function vsub(f1, v, x) {
|
||||
return f1(x).sub(v);
|
||||
}.bind(null, this.f, v));
|
||||
}
|
||||
vmul(v) {
|
||||
return new VectorField(function vmul(f1, v, x) {
|
||||
return f1(x).mul(v);
|
||||
}.bind(null, this.f, v));
|
||||
}
|
||||
}
|
||||
|
||||
export default class VectorField extends {
|
||||
|
||||
}
|
||||
|
||||
function vadd2(v) {return v.radd(2)}
|
||||
function vsub1(v) {return v.rsub(1)}
|
||||
|
||||
var vf1 = new VectorField(vadd2);
|
||||
var vf2 = new VectorField(vsub1);
|
||||
var vf3 = vf2.add(vf1)
|
||||
|
||||
var v = Vector.of(1, 0);
|
||||
|
||||
console.log(vf1.call(v));
|
||||
console.log(vf2.call(v));
|
||||
console.log(vf3.call(v));
|
||||
|
||||
/**
|
||||
.load tensor.js
|
||||
.load vector.js
|
||||
.load VectorField.js
|
||||
*/
|
||||
@@ -0,0 +1,50 @@
|
||||
import Polygon from "../libs/polygon.js";
|
||||
import Entity from "../libs/entity.js";
|
||||
import Body from '../libs/body.js';
|
||||
import Motion from '../libs/motion.js';
|
||||
|
||||
export class AsteroidForm extends Polygon {
|
||||
static color = "#3f87a5";
|
||||
static w = 2;
|
||||
static fill = true;
|
||||
static maxPoints = 8;
|
||||
static minPoints = 3;
|
||||
static size = 1;
|
||||
static random(color=AsteroidForm.color, fill=Asteroid.fill) {
|
||||
const n = Math.floor((AsteroidForm.maxPoints-AsteroidForm.minPoints)*Math.random())+AsteroidForm.minPoints;
|
||||
const p = Polygon.random(n, 2, 10);
|
||||
return new this(p.matrix, color, p.width, fill);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Asteroid extends Entity {
|
||||
static color = "#3f87a5";
|
||||
static w = 5;
|
||||
static riskOfSelfDestruction = 0.00005;
|
||||
static rotation = 0.1;
|
||||
static movement = 0.1;
|
||||
static random() {
|
||||
const form = AsteroidForm.random()
|
||||
form.fill = true;
|
||||
const body = new Body(
|
||||
Motion.random(2, 2).rsub(1/2).rmul(2), // values between 0 and 1
|
||||
Motion.random(2, 1).rsub(1/2).rmul(2)
|
||||
);
|
||||
body[0][1].irmul(Asteroid.rotation); // reducing velocity
|
||||
body[1][1].irmul(Asteroid.movement); // reducing angular velocity
|
||||
return new this(form, body)
|
||||
}
|
||||
update(dt) {
|
||||
super.update(dt);
|
||||
this.form.angle = this.body[1][0][0]; // setting form angle
|
||||
// the form position is already dealt with
|
||||
if (Math.random() <= dt * Asteroid.riskOfSelfDestruction) {
|
||||
console.log("Explosion imminente !!!!");
|
||||
this.selfDestruction();
|
||||
}
|
||||
}
|
||||
selfDestruction() {
|
||||
//silly way of destroying an asteroid
|
||||
return this.removing=true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Group, {Grouper} from "../libs/group.js";
|
||||
import Asteroid from './asteroid.js';
|
||||
|
||||
export default class AsteroidGroup extends Group {
|
||||
static n = 50;
|
||||
static asteroidField(numberAst=AsteroidGroup.n, centerPosition, dimension) {
|
||||
let listDelist=[];
|
||||
let id, ast;
|
||||
for (let pas = 0; pas < numberAst; pas++) {
|
||||
ast = Asteroid.random();
|
||||
ast.x = centerPosition.x + (Math.random() * dimension.x)/2;
|
||||
ast.y = centerPosition.y + (Math.random() * dimension.y)/2;
|
||||
id = "asteroid:"+pas;
|
||||
listDelist.push([id,ast])
|
||||
}
|
||||
return new this(listDelist);
|
||||
}
|
||||
constructor(g) {
|
||||
super(g);
|
||||
this.spread(100);
|
||||
}
|
||||
spread(n) {
|
||||
for (const v of this.values()) {
|
||||
v.position.angle = 2*Math.PI * Math.random();
|
||||
v.position.norm = n*Math.random();
|
||||
}
|
||||
}
|
||||
removeDeads() {
|
||||
const deads = []
|
||||
for (const [k,v] of this) {
|
||||
if (v.removing) {
|
||||
deads.push(k)
|
||||
}
|
||||
}
|
||||
for (const dead of deads) {
|
||||
this.delete(dead)
|
||||
}
|
||||
}
|
||||
update(dt) {
|
||||
super.update(dt);
|
||||
this.removeDeads()
|
||||
}
|
||||
selfDestruction() {
|
||||
this.clear();
|
||||
//Détruit tous les asteroids du groupe
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import GameServer from '../libs/gameserver.js'
|
||||
|
||||
class AsteroidServer extends GameServer {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import Tensor from '../libs/tensor.js';
|
||||
import Vector from '../libs/vector.js';
|
||||
import Matrix from '../libs/matrix.js';
|
||||
import Motion from '../libs/motion.js';
|
||||
import Body from '../libs/body.js';
|
||||
|
||||
import Point from '../libs/point.js';
|
||||
import Figure from '../libs/figure.js';
|
||||
import Form from '../libs/form.js';
|
||||
import BasePolygon from '../libs/basepolygon.js';
|
||||
import Square from '../libs/square.js';
|
||||
import Rectangle from '../libs/rectangle.js';
|
||||
import Segment from '../libs/segment.js';
|
||||
import Polygon from '../libs/polygon.js';
|
||||
|
||||
import Life, {LifeBar} from './life.js';
|
||||
import Follower from './follower.js';
|
||||
import Shooter from './shooter.js';
|
||||
|
||||
import Spaceship from './spaceship.js';
|
||||
import Missile from './missile.js';
|
||||
import Asteroid, {AsteroidForm} from './asteroid.js';
|
||||
import Meteor from './meteor.js';
|
||||
|
||||
import Group from '../libs/group.js';
|
||||
import SuperGroup from './supergroup.js';
|
||||
import SpaceshipGroup from './spaceshipgroup.js';
|
||||
import MissileGroup from './missilegroup.js';
|
||||
import AsteroidGroup from './asteroidgroup.js';
|
||||
import MeteorGroup from './missilegroup.js';
|
||||
|
||||
import GameMap from './gamemap.js';
|
||||
import { SuperCollider,MissileSpaceshipCollider} from './collider.js';
|
||||
|
||||
|
||||
var canvas = document.getElementById("canvas");
|
||||
|
||||
var context = new ContextAdapter(canvas.getContext("2d"));
|
||||
|
||||
|
||||
context.width = canvas.width = window.innerWidth;
|
||||
context.height = canvas.height = window.innerHeight;
|
||||
|
||||
context.plane.units.position = new Vector(0.2, 0.2);
|
||||
|
||||
// var name = prompt("name");
|
||||
var host = String(document.location);
|
||||
var socket = io.connect(host) //, {query: "name="+name});
|
||||
|
||||
|
||||
|
||||
// var group = SuperGroup.random();
|
||||
// var game = new Game(group);
|
||||
var game = Game.random();
|
||||
|
||||
|
||||
|
||||
gameClient = new GameClient(canvas, game, socket)
|
||||
|
||||
gameClient.main();
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import EventEmitter from 'events';
|
||||
import Missile from './missile.js';
|
||||
import Spaceship from './spaceship.js';
|
||||
|
||||
export default class Collider {
|
||||
collide(group) {
|
||||
throw "A Collider must have a collide method which takes a group as an argument."
|
||||
}
|
||||
}
|
||||
|
||||
export class SuperCollider extends Collider {
|
||||
constructor(...colliders) {
|
||||
super();
|
||||
this.colliders = colliders;
|
||||
this.collisionEmitter = new EventEmitter();
|
||||
}
|
||||
initCollisionListener(serverEmitter) {
|
||||
Missile.initCollisionListener(this.collisionEmitter, serverEmitter);
|
||||
Spaceship.initCollisionListener(this.collisionEmitter, serverEmitter);
|
||||
}
|
||||
collide(superGroup) {
|
||||
for (const collider of this.colliders) {
|
||||
collider.collide(superGroup, this.collisionEmitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class MissileSpaceshipCollider extends Collider {
|
||||
collide(group, collisionEmitter) {
|
||||
const deadMissiles = [];
|
||||
const deadSpaceships = [];
|
||||
const missileGroup = group.get('missileGroup');
|
||||
const spaceshipGroup = group.get('spaceshipGroup');
|
||||
for (const [mid,missile] of missileGroup) {
|
||||
for (const [sid, spaceship] of spaceshipGroup) {
|
||||
if (missile.collideWithSpaceship(spaceship)) {
|
||||
collisionEmitter.emit('spaceship/missile');
|
||||
collisionEmitter.emit('missile/spaceship');
|
||||
console.log("collision:", mid, sid);
|
||||
deadMissiles.push(mid);
|
||||
deadSpaceships.push(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const sid of deadSpaceships) {
|
||||
spaceshipGroup.delete(sid);
|
||||
}
|
||||
return [deadMissiles, deadSpaceships];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default class Dasher {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Cool explosion when the missiles hit something.
|
||||
import { ColorChanger } from '../libs/animation.js';
|
||||
export default class Explosion extends ColorChanger {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
end() {
|
||||
super.end();
|
||||
this.object.removing = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Using strategy pattern for spaceships
|
||||
export default class Follower {
|
||||
orientate(body, form) {
|
||||
//tourne le Follower vers sa vitesse
|
||||
form.angle = body.velocity.angle;
|
||||
}
|
||||
|
||||
follow(body, position, velocityNorm) {
|
||||
//change l'acceleration du Follower en fonction de la position donnée
|
||||
const velocityFollower = position.sub(body.p);
|
||||
velocityFollower.norm = velocityNorm;
|
||||
body.velocity = velocityFollower;
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
// GameClient => Game => SuperGroup => ...
|
||||
// => GameMap
|
||||
// => Context
|
||||
|
||||
// GameServer => Game => SuperGroup => ...
|
||||
// => GameMap
|
||||
|
||||
import SuperGroup from './supergroup.js';
|
||||
import SpaceshipGroup from './spaceshipgroup.js';
|
||||
import MissileGroup from './missilegroup.js';
|
||||
import AsteroidGroup from './asteroidgroup.js';
|
||||
import MeteorGroup from './missilegroup.js';
|
||||
import Group from '../libs/group.js';
|
||||
import Motion from '../libs/motion.js';
|
||||
import Matrix from '../libs/matrix.js';
|
||||
import Body from '../libs/body.js';
|
||||
import Point from '../libs/point.js';
|
||||
import Vector from '../libs/vector.js';
|
||||
import Tensor from '../libs/tensor.js';
|
||||
import Spaceship from './spaceship.js';
|
||||
import Missile from './missile.js';
|
||||
import Asteroid, {AsteroidForm} from './asteroid.js';
|
||||
import Meteor from './meteor.js';
|
||||
import GameMap from './gamemap.js';
|
||||
import Life, {LifeBar} from './life.js';
|
||||
import Follower from './follower.js';
|
||||
import Shooter from './shooter.js';
|
||||
import Square from '../libs/square.js';
|
||||
import Rectangle from '../libs/rectangle.js';
|
||||
import Segment from '../libs/segment.js';
|
||||
import Polygon from '../libs/polygon.js';
|
||||
import BasePolygon from '../libs/basepolygon.js';
|
||||
import { SuperCollider,MissileSpaceshipCollider} from './collider.js';
|
||||
export default class Game {
|
||||
static dt = 0.1;
|
||||
static mapTypes = [
|
||||
SuperGroup,
|
||||
SpaceshipGroup,
|
||||
MissileGroup,
|
||||
AsteroidGroup,
|
||||
MeteorGroup,
|
||||
Group,
|
||||
]
|
||||
static arrayTypes = [
|
||||
Motion,
|
||||
Matrix,
|
||||
Body,
|
||||
Point,
|
||||
Vector,
|
||||
Tensor,
|
||||
]
|
||||
// NOTE THIS IS VERY IMPORTANT
|
||||
// EVERY OBJECTS MUST HAVE DEFAULTS ARGUMENTS IN THEIR CONSTRUCTORS
|
||||
// OTHERWISE BUGS WILL APPEAR WHILE CREATING OBJECTS WITH NO ARGUMENTS
|
||||
// THIS OPERATION IS DONE WHEN ITS IS WRITTEN:
|
||||
// return Object.assign(new type(), value.value);
|
||||
static objectTypes = [
|
||||
Spaceship,
|
||||
Missile,
|
||||
Asteroid,
|
||||
Meteor,
|
||||
GameMap,
|
||||
Life,
|
||||
LifeBar,
|
||||
Follower,
|
||||
Shooter,
|
||||
//Hunter,
|
||||
AsteroidForm,
|
||||
Square,
|
||||
Rectangle,
|
||||
// Circle,
|
||||
Segment,
|
||||
// Triangle,
|
||||
Polygon,
|
||||
BasePolygon,
|
||||
]
|
||||
|
||||
static reviver(key, value) {
|
||||
if(typeof value === 'object' && value !== null) {
|
||||
for (const type of Game.mapTypes) {
|
||||
if (value.type === type.name) {
|
||||
return new type(value.value);
|
||||
}
|
||||
}
|
||||
for (const type of Game.arrayTypes) {
|
||||
if (value.type === type.name) {
|
||||
return type.from(value.value);
|
||||
}
|
||||
}
|
||||
for (const type of Game.objectTypes) {
|
||||
if (value.type === type.name) {
|
||||
return Object.assign(new type(), value.value);
|
||||
}
|
||||
}
|
||||
if (value.type === 'Map') {
|
||||
return new Map(value.value);
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
return value;
|
||||
}
|
||||
// return Object.assign(new AsteroidForm(), value);
|
||||
// console.log(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static replacer(key, value) {
|
||||
const originalObject = this[key];
|
||||
if (value === null) return null;
|
||||
if(originalObject instanceof Map) {
|
||||
for (const type of Game.mapTypes) {
|
||||
if (originalObject instanceof type) {
|
||||
return {
|
||||
type : type.name,
|
||||
value: Array.from(originalObject.entries())
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: "Map",
|
||||
value: Array.from(originalObject.entries())
|
||||
};
|
||||
} else if (originalObject instanceof Array) {
|
||||
for (const type of Game.arrayTypes) {
|
||||
if (originalObject instanceof type) {
|
||||
return {
|
||||
type : type.name,
|
||||
value: Array.from(originalObject.round(2))
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (typeof a == 'number') {
|
||||
return Math.round(number*10**2, 2)/10**2;
|
||||
} else {
|
||||
for (const type of Game.objectTypes) {
|
||||
if (originalObject instanceof type) {
|
||||
return {
|
||||
type : type.name,
|
||||
value: {...originalObject}
|
||||
};
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// Making a getter is memory friendly
|
||||
// and it doesn't matter that we can't
|
||||
// change the result.
|
||||
static get gameMap() {
|
||||
return GameMap.fromSize(200,200);
|
||||
}
|
||||
static random() {
|
||||
return new this(
|
||||
SuperGroup.random()
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
group = new SuperGroup(), // this super group is empty
|
||||
collider = new SuperCollider(new MissileSpaceshipCollider()),
|
||||
gameMap = Game.gameMap,
|
||||
dt = Game.dt
|
||||
) {
|
||||
this.group = group;
|
||||
this.collider = collider;
|
||||
this.gameMap = gameMap;
|
||||
this.dt = dt;
|
||||
}
|
||||
update() {
|
||||
// this.collider.collide(this.group);
|
||||
this.group.update(this.dt);
|
||||
this.limit();
|
||||
}
|
||||
collide() {
|
||||
const collisions = this.collider.collide(this.group);
|
||||
}
|
||||
show(ctx) {
|
||||
this.gameMap.show(ctx);
|
||||
this.group.show(ctx);
|
||||
}
|
||||
limit() {
|
||||
const entities = this.group.getAll();
|
||||
this.gameMap.limit(entities);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
export default class GameClient extends Manager {
|
||||
static movement = {up: false, down: false, right: false, left: false, zoomin: false, zoomout: false};
|
||||
static backgroundColor = "#000000";
|
||||
|
||||
constructor(
|
||||
canvas,
|
||||
game,
|
||||
socket,
|
||||
) {
|
||||
super(canvas, game)
|
||||
this.socket = socket;
|
||||
this.id = null;
|
||||
this.mouses = new Map();
|
||||
}
|
||||
on() {
|
||||
this.socket.on('newMissile', function (data) {
|
||||
const missile = JSON.parse(data.missile, Game.reviver);
|
||||
const missileGroup = this.game.group.get('missileGroup');
|
||||
missileGroup.add(missile);
|
||||
}.bind(this));
|
||||
this.socket.on("oneSpaceshipBody", function (data) {
|
||||
const spaceship = this.game.group.get('spaceshipGroup').take(data.uuid);
|
||||
spaceship.body.rset(data.body);
|
||||
}.bind(this));
|
||||
console.log("on écoute les sockets du serv");
|
||||
this.socket.on('spaceshipGroup', function (data) {
|
||||
const spaceshipGroup = JSON.parse(data, Game.reviver);
|
||||
this.game.group.set("spaceshipGroup", spaceshipGroup);
|
||||
// console.log("spaceship");
|
||||
}.bind(this));
|
||||
this.socket.on('spaceshipGroupBodies', function (data) {
|
||||
//console.log('spaceshipGroupBodies');
|
||||
// console.log(data.bodies);
|
||||
this.game.group.get("spaceshipGroup").setBodies(data.bodies);
|
||||
}.bind(this));
|
||||
this.socket.on('superGroup', function (data) {
|
||||
const superGroup = JSON.parse(data, Game.reviver);
|
||||
this.game.group = superGroup;
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on('asteroidGroup', function (data) {
|
||||
const asteroidGroup = JSON.parse(data, Game.reviver);
|
||||
console.log(`On reçois un asteroidGroup`);// ${data}`);
|
||||
this.game.group.set('asteroidGroup', asteroidGroup);
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on('oneMouseMove', function(data) {
|
||||
this.mouses.set(data.uuid, Vector.from(data.mouse));
|
||||
console.log(this.mouses);
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on('mouses', function (data) {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup')
|
||||
for (const [k,v] of data.mouses) {
|
||||
spaceshipGroup.take(k).follow(Vector.from(v));
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
const socketID = this.socket.id;
|
||||
this.socket.on('spaceshipID', function (data) {
|
||||
const spaceshipID = data.spaceshipID;
|
||||
//console.log(`On reçois l'id : socket.id chez nous : ${socketID} socket id chez serv : ${spaceshipID}`);// ${data}`);
|
||||
console.log(`l'id de notre spaceShip ${spaceshipID}`);
|
||||
this.id = spaceshipID;
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("summonSpaceship", function (data) {
|
||||
//ajoute le vaisseau en
|
||||
//data={uuid: this.socket.id, coordinates: [10,10]}
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
const spaceship = Spaceship.random();
|
||||
spaceship.form.color = "green";
|
||||
spaceship.position = Vector.from(data.coordinates);
|
||||
spaceshipGroup.add(data.uuid, spaceship);
|
||||
// spaceshipGroup.addSpaceship(data.uuid, spaceship);
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("unsummonSpaceship", function (data) {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
spaceshipGroup.remove(data.uuid);
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("spaceship-update", function(data) {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
if (data.event=='life-change') {
|
||||
const spaceship = spaceshipGroup.get(data.sid);
|
||||
spaceship.life.value = data.life;
|
||||
} else if (data.event=='removing') {
|
||||
spaceshipGroup.delete(data.sid);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
get player() {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
return spaceshipGroup.take(this.id);
|
||||
}
|
||||
get spaceship() {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
return spaceshipGroup.take(this.id);
|
||||
}
|
||||
show() {
|
||||
const player = this.player;
|
||||
if (player) {
|
||||
this.context.plane.position = player.position;
|
||||
}
|
||||
super.show();
|
||||
}
|
||||
update() {
|
||||
const player = this.player;
|
||||
if (player) {
|
||||
player.follow(this.context.fromScreen(this.mouse));
|
||||
// this.socket.emit("spaceshipBody", {body: player.body});
|
||||
this.socket.emit("mouseMove", {mouse: Array.from(this.context.fromScreen(this.mouse))});
|
||||
|
||||
}
|
||||
// console.log('update');
|
||||
this.followAllMouses();
|
||||
super.update()
|
||||
}
|
||||
followAllMouses() {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
for (const [k,v] of this.mouses) {
|
||||
spaceshipGroup.take(k).follow(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
onKeyDown(e) {
|
||||
super.onKeyDown(e);
|
||||
if (this.id === null) {
|
||||
//on n'a pas encore de spaceship
|
||||
console.log(`Vous n'avez pas encore de spaceship !`);
|
||||
return false;
|
||||
}
|
||||
if (e.code == 'Space') {
|
||||
const missile = this.player.shoot();
|
||||
this.game.group.get('missileGroup').add(missile);
|
||||
const data = JSON.stringify(missile, Game.replacer);
|
||||
this.socket.emit("missile", {missile:data});
|
||||
}
|
||||
}
|
||||
// onMouseMotion(e) {
|
||||
// super.onMouseMotion(e);
|
||||
// this.socket.emit("mouseMove", {mouse: Array.from(this.context.fromScreen(this.mouse))});
|
||||
// }
|
||||
start() {
|
||||
super.start()
|
||||
this.on()
|
||||
}
|
||||
main() {
|
||||
this.start();
|
||||
setInterval(this.loop.bind(this), 20);
|
||||
}
|
||||
|
||||
// let player = this.game.map.group.playerGroup.map.get(this.id);
|
||||
// if (player) {
|
||||
// if (!player.alive) {
|
||||
// console.log("you are dead");
|
||||
// this.socket.emit("player-respawn");
|
||||
// }
|
||||
// }
|
||||
|
||||
// if ('id' in this) {
|
||||
// this.game.map.group.collideClient(this.id);
|
||||
// }
|
||||
// if (this.game.map.group.cache.size>0) { // could overrun the server
|
||||
// this.socket.emit(
|
||||
// "cache",
|
||||
// JSON.stringify(this.game.map.group.cache, Game.replacer)
|
||||
// );
|
||||
// this.game.map.group.cache.clear();
|
||||
// }
|
||||
// this.socket.emit("control-mousemove", this.context.fromScreen(this.mouse));
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Entity from '../libs/entity.js';
|
||||
export default class GameEntity extends Entity {
|
||||
constructor(form, body) {
|
||||
super(form, body)
|
||||
this.removing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// alive() {
|
||||
// this.life.
|
||||
// }
|
||||
// die() {
|
||||
// this.life.value = 0;
|
||||
// }
|
||||
// spawn() {
|
||||
// this.life.value = this.life.max;
|
||||
// }
|
||||
@@ -0,0 +1,28 @@
|
||||
import SuperGroup from './supergroup.js';
|
||||
import AsteroidGroup from './asteroidgroup.js';
|
||||
export default class GameLevel {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
get group() {
|
||||
return new SuperGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class AsteroidLevel extends GameLevel {
|
||||
constructor(n) {
|
||||
super();
|
||||
this.n = n;
|
||||
}
|
||||
get group() {
|
||||
return new SuperGroup([
|
||||
['asteroidGroup', AsteroidGroup.random(this.n)]
|
||||
]);
|
||||
|
||||
}
|
||||
update() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import Rectangle from '../libs/rectangle.js';
|
||||
import Vector from '../libs/vector.js';
|
||||
export default class GameMap {
|
||||
static fromSize(n,m) {
|
||||
const x=-n/2, y=-n/2, w=n, h=n;
|
||||
const r = new Rectangle(new Vector(x, y), new Vector(w, h))
|
||||
return new this(r);
|
||||
}
|
||||
constructor(rectangle) {
|
||||
this.rectangle = rectangle;
|
||||
}
|
||||
|
||||
get r() {
|
||||
return this.rectangle;
|
||||
}
|
||||
set r(value) {
|
||||
this.rectangle = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
compute(position) {
|
||||
let x,y
|
||||
if (Math.abs(position.x) > this.r.w / 2) {
|
||||
let delta = position.x % this.r.w / 2;
|
||||
delta = 0;
|
||||
x = -(position.x/Math.abs(position.x)) * (this.r.w / 2) + delta;
|
||||
} else {
|
||||
x = position.x;
|
||||
}
|
||||
|
||||
if (Math.abs(position.y) > this.r.h / 2) {
|
||||
let delta = position.y % this.r.h / 2;
|
||||
delta = 0;
|
||||
y = -(position.y/Math.abs(position.y)) * (this.r.h / 2) + delta;
|
||||
} else {
|
||||
y = position.y;
|
||||
}
|
||||
return new Vector(x, y);
|
||||
}
|
||||
|
||||
compute2(position) {
|
||||
const t = this.r.size.rdiv(2);
|
||||
// console.log(position)
|
||||
// console.log(t)
|
||||
// position.iradd(t);
|
||||
position.x += this.r.w/2;
|
||||
position.y += this.r.h/2;
|
||||
position.x = position.x % this.r.w;
|
||||
position.y = position.y % this.r.h;
|
||||
position.x -= this.r.w/2;
|
||||
position.y -= this.r.h/2;
|
||||
// position.irsub(t);
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
limit(entities) {
|
||||
for (const entity of entities) {
|
||||
entity.position = this.compute(entity.position);
|
||||
}
|
||||
}
|
||||
|
||||
show(ctx) {
|
||||
this.rectangle.show(ctx)
|
||||
}
|
||||
|
||||
|
||||
update(dt) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import EventEmitter from 'events';
|
||||
var ping = 0;
|
||||
|
||||
export class Player {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.update = new EventEmitter();//Utilise par SockCom/SockClient pour les socket
|
||||
this.mouse = Vector.zero2;
|
||||
//this.update.emit("lifeUpdate", this.life.value);
|
||||
}
|
||||
join() {
|
||||
//ajoute le vaisseau de this dans le jeu
|
||||
}
|
||||
|
||||
left() {
|
||||
//retire le vaisseau du joueur
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class socketClient {
|
||||
//se trouve côté server. Une instance de socketClient par onglet du jeu
|
||||
constructor(socket, gameServer) {
|
||||
this.socket = socket;
|
||||
this.player = new Player(this.socket.id);//fabrication d'une instance Player pour notre client
|
||||
this.gameServer = gameServer;
|
||||
}
|
||||
|
||||
connect() {
|
||||
//lancée quand l'objet se connecte au serveur
|
||||
const spaceshipGroup = this.gameServer.game.group.get('spaceshipGroup');
|
||||
spaceshipGroup.add(this.socket.id, Spaceship.random());
|
||||
this.socket.emit("spaceshipID", { spaceshipID: this.socket.id });
|
||||
this.player.join();
|
||||
this.registerPlayerEvent(this.player);
|
||||
this.on();
|
||||
const superGroup = this.gameServer.game.group;
|
||||
//hipGroup = this.gam erver.game.group.get("spaceshipGroup")
|
||||
//const asteroidGroup = this.gameServer.game.group.get("asteroidGroup");
|
||||
|
||||
//const str = JSON.stringify(group, Game.replacer);
|
||||
//console.log(`On envoie à ${this.socket.id} le groupe de ${str.length} caractère.`); //superGroup : ${str}`);
|
||||
//this.socket.emit("superGroup", str);
|
||||
const data = JSON.stringify(superGroup, Game.replacer)
|
||||
this.socket.emit("superGroup", data);//le nouveau joueur reçois son spaceship ici
|
||||
|
||||
for (let so_id in this.gameServer.socketList) {//todo faire un truc async et utiliser broadcast
|
||||
if (so_id != this.socket.id) {
|
||||
this.gameServer.socketList[so_id].socket.emit("summonSpaceship", {uuid: this.socket.id, coordinates: [0,0]})//todo
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
get spaceship() {
|
||||
return this.gameServer.game.group.get('spaceshipGroup').take(this.socket.id);
|
||||
}
|
||||
disconnect() {
|
||||
//quand l'objet se déconnecte du serveur
|
||||
this.player.left();
|
||||
const spaceshipGroup = this.gameServer.game.group.get("spaceshipGroup")
|
||||
spaceshipGroup.remove(this.socket.id);
|
||||
this.gameServer.sendAll("unsummonSpaceship", {uuid:this.socket.id});
|
||||
|
||||
}
|
||||
|
||||
on() {
|
||||
//permet de recevoir les commandes envoyée par le joueur
|
||||
if (this.player === undefined || this.player === null) {
|
||||
console.log("on essai de iniSocketAPI alors que le joueur est pas co :/");
|
||||
}
|
||||
// const player = this.player;
|
||||
this.socket.on("message", function (message) {
|
||||
console.log(message);
|
||||
this.socket.emit("message", message);
|
||||
});
|
||||
this.socket.on("mouseMove", function (data) {
|
||||
this.player.mouse.set(data.mouse);
|
||||
// this.socket.broadcast.emit("oneMouseMove", {uuid:this.socket.id, mouse:data.mouse});
|
||||
// console.log(data.mouse)
|
||||
// const spaceship = this.gameServer.game.group.get('spaceshipGroup').take(this.socket.id);
|
||||
// if (spaceship) {
|
||||
// spaceship.follow(Vector.from(data.mouse));
|
||||
// this.gameServer.sendAll("oneSpaceshipBody", {body: spaceship.body, uuid: this.socket.id});
|
||||
// }
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("spaceshipBody", function(data) {
|
||||
this.spaceship.body.rset(data.body);
|
||||
}.bind(this));
|
||||
|
||||
this.socket.on("missile", function (data) {
|
||||
//le client a envoyé un missile
|
||||
//todo : vérif que le client respecte les cooldown
|
||||
// console.log(`le client envoi un missile`);
|
||||
const missile = JSON.parse(data.missile, Game.reviver);
|
||||
// console.log(missile);
|
||||
this.gameServer.game.group.get('missileGroup').add(missile);
|
||||
// console.log(this.gameServer.game.group.get('missileGroup'));
|
||||
this.socket.broadcast.emit("newMissile", data);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
registerPlayerEvent(player) {
|
||||
//permet d'annnoncer à tous les joueurs des updates
|
||||
const socket = this.socket;
|
||||
// const player = this.player;
|
||||
const myGameServer = this.gameServer;
|
||||
player.update.on("coordinatesUpdate", function (data) {
|
||||
//console.log("coordinatesUpdate event", data);
|
||||
myGameServer.sendAll("coordinatesUpdate", { uuid: player.id, coordinates: data.coordinates });
|
||||
});
|
||||
|
||||
player.update.on("directionUpdate", function (data) {
|
||||
//console.log("directionUpdate event", data);
|
||||
myGameServer.sendAll("directionUpdate", { uuid: player.id, direction: data.direction });
|
||||
});
|
||||
|
||||
player.update.on("unsummonPlayer", function (data) {
|
||||
myGameServer.sendAll("unsummonPlayer", { uuid: data.uuid });
|
||||
});
|
||||
|
||||
player.update.on("summonPlayer", function (data) {
|
||||
myGameServer.sendAll("summonPlayer", { name: data.name, uuid: data.uuid, coordinates: data.coordinates });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class GameServer {
|
||||
constructor(game, io) {
|
||||
this.game = game;
|
||||
this.socketList = {};
|
||||
this.io = io;
|
||||
this.eventEmitter = new EventEmitter(); // intern event emitter
|
||||
}
|
||||
|
||||
setUp() {
|
||||
//A lancer lors du démarrage du server
|
||||
this.initUpdate();
|
||||
this.acceptConnection();
|
||||
}
|
||||
|
||||
sendAll(event, message) {//todo utiliser les boradcasts https://socket.io/docs/#Broadcasting-messages
|
||||
for (let so_id in this.socketList) {//todo faire un truc async
|
||||
this.socketList[so_id].socket.emit(event, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
acceptConnection() {
|
||||
//demande au seveur d'accepter les connexions des clients
|
||||
console.log(`On écoute les connexions des clients`);
|
||||
this.io.sockets.on("connection", function (socket) {
|
||||
const id = socket.id;
|
||||
|
||||
console.log(`Nouveau client d'id ${JSON.stringify(id)}`);
|
||||
this.socketList[id] = new socketClient(socket, this);
|
||||
this.socketList[id].connect();
|
||||
socket.on("disconnect", function () {
|
||||
console.log(`Deconnexion client d'id ${JSON.stringify(id)}`);
|
||||
this.socketList[id].disconnect();
|
||||
delete this.socketList[id];
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
initUpdate() {
|
||||
this.eventEmitter.on("spaceship-update", function(data) {
|
||||
this.sendAll("spaceship-update", data);
|
||||
}.bind(this));
|
||||
//A lancer au lancement du serveur, permet d'informer les clients des dernières news
|
||||
//this.game.on("Event Update News !!", function (data) {
|
||||
// console.log("event caught by GameServer !!");
|
||||
// this.sendAll("Update de la mort", { param1: "ciao les losers", param2: "toujours pour décrire l'update" });
|
||||
//});
|
||||
}
|
||||
update() {
|
||||
this.follow();
|
||||
// this.sendMouses();
|
||||
this.game.update();
|
||||
this.game.collide();
|
||||
}
|
||||
sendMouses() {
|
||||
const mouses = [];
|
||||
for (const id in this.socketList) {//todo faire un truc async
|
||||
mouses.push([id, this.socketList[id].player.mouse]);
|
||||
}
|
||||
this.sendAll("mouses", {mouses: mouses});
|
||||
}
|
||||
follow() {
|
||||
let socket, spaceship;
|
||||
for (const id in this.socketList) {
|
||||
socket = this.socketList[id]
|
||||
spaceship = socket.spaceship;
|
||||
if (spaceship) { // spaceship might die.
|
||||
spaceship.follow(socket.player.mouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
loop() {
|
||||
this.update();
|
||||
}
|
||||
start() {
|
||||
console.log("starting the game");
|
||||
this.game.collider.initCollisionListener(this.eventEmitter);
|
||||
}
|
||||
main() {
|
||||
this.start();
|
||||
// console.log(this.game.dt*100);
|
||||
// this.game.dt = 0.3;
|
||||
setInterval(this.loop.bind(this), 20);
|
||||
setInterval(this.streamSpaceshipGroupBodies.bind(this), 20);
|
||||
}
|
||||
streamSpaceshipGroupBodies() {
|
||||
const spaceshipGroup = this.game.group.get('spaceshipGroup');
|
||||
this.sendAll('spaceshipGroupBodies', {bodies: spaceshipGroup.getBodies()});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Using strategy pattern for spaceships
|
||||
export default class Hunter {
|
||||
constructor(spaceship, target=undefined) {
|
||||
this.spaceship = spaceship;
|
||||
this.target = target;
|
||||
}
|
||||
hunt() {
|
||||
this.spaceship.follow(this.target.center);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import Rectangle from '../libs/rectangle.js';
|
||||
import Vector from '../libs/vector.js';
|
||||
export default class Life {
|
||||
static value = 1;
|
||||
static max = 1;
|
||||
constructor(value=Life.value, max=Life.max) {
|
||||
this.value = value;
|
||||
this.max = max;
|
||||
// It doesn't matter where the lifebar is
|
||||
// because it only matters when showing it.
|
||||
this.bar = new LifeBar();
|
||||
}
|
||||
isAlive() {
|
||||
return this.value > 0;
|
||||
}
|
||||
regen() {
|
||||
this.value = this.max;
|
||||
}
|
||||
show(ctx, position) {
|
||||
this.bar.show(ctx, position, this.value/this.max);
|
||||
}
|
||||
}
|
||||
|
||||
export class LifeBar extends Rectangle {
|
||||
static areaColor = "lightgreen";
|
||||
static borderColor = "white";
|
||||
static margin = 2;
|
||||
static position = [0,0];
|
||||
static size = [3, 0.5];
|
||||
static lineWidth = 0.1;
|
||||
// defining new default arguments
|
||||
constructor(
|
||||
position=LifeBar.position,
|
||||
size=LifeBar.size,
|
||||
color=undefined,
|
||||
lineWidth=LifeBar.lineWidth,
|
||||
fill=undefined,
|
||||
) {
|
||||
super(position, size, lineWidth, color, fill);
|
||||
}
|
||||
// the order of the operations is very important
|
||||
show(ctx, position, ratio, margin=LifeBar.margin) {
|
||||
this.center = position.sub(new Vector(0, -margin));
|
||||
const w = this.w;
|
||||
this.w *= ratio;
|
||||
this.color = LifeBar.areaColor;
|
||||
this.fill = true;
|
||||
super.show(ctx)
|
||||
this.w = w;
|
||||
this.color = LifeBar.borderColor;
|
||||
this.fill = false;
|
||||
super.show(ctx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import GameEntity from './gameentity.js';
|
||||
import Vector from '../libs/vector.js';
|
||||
import Square from '../libs/square.js';
|
||||
import Body from '../libs/body.js';
|
||||
export default class Meteor extends GameEntity {
|
||||
static color = "#c50000";
|
||||
|
||||
//m = new Motion(new Vector(1, 2))
|
||||
static fromVelocity(velocity) {
|
||||
let form = new Square(-1, -1, 2);
|
||||
form.fill = true;
|
||||
let body = Body.random(2, 2, 2);
|
||||
form.color = Meteor.color;
|
||||
body.velocity = velocity;
|
||||
return new this(form, body)
|
||||
}
|
||||
static random() {
|
||||
return this.fromVelocity(Vector.random());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import {Grouper} from '../libs/group.js';
|
||||
import Meteor from './meteor.js';
|
||||
|
||||
export default class MeteorGroup extends Grouper(Meteor) {
|
||||
static meteorShower(n) {
|
||||
let wtf = [];
|
||||
for (let pas = 0; pas < n; pas++) {
|
||||
wtf.push(["meteor:"+pas, Meteor.random()]);
|
||||
}
|
||||
return new this(wtf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import Entity from '../libs/entity.js';
|
||||
import Segment from '../libs/segment.js';
|
||||
|
||||
export default class Missile extends Entity {
|
||||
static size = 3;
|
||||
static duration = 1000/10; // mili-seconds
|
||||
static velocityBoost = 10;
|
||||
static damage = 0.1;
|
||||
static margin = 1;
|
||||
static color = "yellow";
|
||||
static random(
|
||||
source=undefined,
|
||||
target=undefined
|
||||
) {
|
||||
return new this(
|
||||
new Segment([0, 0], [0, Missile.size]),
|
||||
Body.random(1, 2),
|
||||
source,
|
||||
target
|
||||
);
|
||||
}
|
||||
static make(
|
||||
source,
|
||||
target=undefined,
|
||||
damage=Missile.damage,
|
||||
duration=Missile.duration
|
||||
) {
|
||||
const body = new Body(source.body[0].slice(0, 2).copy());
|
||||
body[0][1].norm += this.velocityBoost;
|
||||
const radius = source.form.hitBubbleRadius;
|
||||
const margin = body[0][1].normalized.rmul(radius+Missile.size/2+Missile.margin);
|
||||
body[0][0].iadd(margin);
|
||||
return new this(
|
||||
new Segment([0, 0], [0, Missile.size], 1, Missile.color),
|
||||
body,
|
||||
source,
|
||||
target,
|
||||
damage,
|
||||
duration
|
||||
);
|
||||
|
||||
}
|
||||
constructor(
|
||||
form,
|
||||
body,
|
||||
source=undefined,
|
||||
target=undefined,
|
||||
damage=Missile.damage,
|
||||
duration=Missile.duration,
|
||||
) {
|
||||
super(form, body);
|
||||
Missile.initCollision(this);
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.damage = damage;
|
||||
this.duration = duration;
|
||||
this.time = undefined;
|
||||
}
|
||||
update(dt) {
|
||||
if (!this.time) {
|
||||
this.time = Date.now()
|
||||
}
|
||||
if (!this.isAlive(dt)) {
|
||||
this.removing = true;
|
||||
}
|
||||
this.form.angle = this.body[0][1].angle;
|
||||
super.update(dt);
|
||||
}
|
||||
get left() {
|
||||
return Date.now() - this.time;
|
||||
}
|
||||
isAlive(dt) {
|
||||
return this.left*dt<this.duration;
|
||||
}
|
||||
/**
|
||||
* Determine whether a missile is colliding with a spaceshiop
|
||||
* @param {Spaceship} spaceship
|
||||
*/
|
||||
collideWithSpaceship(spaceship) {//pk c'est pas static ? parce que c'est la vie
|
||||
return spaceship.form.contains(this.form.p1) || spaceship.form.contains(this.form.p2);
|
||||
}
|
||||
|
||||
static initCollisionListener(collisionEmitter, mainEmitter) {
|
||||
collisionEmitter.on('missile/spaceship', function(missile, spaceship) {
|
||||
missile.removing = true;
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {Grouper} from '../libs/group.js';
|
||||
import Missile from './missile.js';
|
||||
|
||||
export default class MissileGroup extends Grouper(Missile) {
|
||||
static n = 0;
|
||||
removeDeads() {
|
||||
const deads = []
|
||||
for (const [k,v] of this) {
|
||||
if (v.removing) {
|
||||
deads.push(k)
|
||||
}
|
||||
}
|
||||
for (const dead of deads) {
|
||||
this.delete(dead)
|
||||
}
|
||||
}
|
||||
update(dt) {
|
||||
super.update(dt);
|
||||
this.removeDeads()
|
||||
}
|
||||
add(missile) {
|
||||
MissileGroup.n += 1;
|
||||
super.add(String(MissileGroup.n), missile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Missile from './missile.js';
|
||||
|
||||
// Using strategy pattern for spaceships
|
||||
export default class Shooter {
|
||||
static types = [Missile];
|
||||
constructor(types, selection=0) {
|
||||
this.types = types;
|
||||
this.selection = selection;
|
||||
this.shooted = 0; // optional
|
||||
this.last_shooting = 0; // against shoot spam
|
||||
}
|
||||
get type() {
|
||||
return this.types[this.selection];
|
||||
}
|
||||
set type(v) {
|
||||
this.types[this.selection] = v;
|
||||
}
|
||||
shoot(entity) {
|
||||
this.last_shooting = Date.now();
|
||||
this.shooted += 1;
|
||||
return this.constructor.types[this.type].make(entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import Entity from '../libs/entity.js';
|
||||
// Using strategy pattern is used
|
||||
export default class Spaceship extends Entity {
|
||||
static color = "#ef6c00";//du orange
|
||||
static initFuel = 1000;
|
||||
static extraDashVelocityNorm = 2;
|
||||
static afterDashVelocityNorm = 1;
|
||||
static dashing = false;
|
||||
static dashDuration = 2;
|
||||
|
||||
static random() {
|
||||
const body = Body.random(1, 3, 2);
|
||||
const form = new Polygon([[4,0],[-2,2],[-1,0],[-2,-2]]);//triangle regardant vers la droite
|
||||
form.color = Spaceship.color;
|
||||
form.fill = true;
|
||||
form.center = Vector.zero(2);
|
||||
return new this(form, body);
|
||||
}
|
||||
|
||||
constructor(
|
||||
form,
|
||||
body,
|
||||
life = new Life(),
|
||||
shooter = new Shooter([0]),
|
||||
follower = new Follower(),
|
||||
fuel = Spaceship.initFuel,
|
||||
dashing = Spaceship.dashing, // dashing should have its own class using strategy pattern (dasher.js)
|
||||
dashDuration = Spaceship.dashDuration
|
||||
) {
|
||||
super(form, body);
|
||||
this.life = life
|
||||
this.shooter = shooter;
|
||||
this.follower = follower;
|
||||
this.dashing = dashing;
|
||||
this.dashTime = 0;
|
||||
this.dashDuration = dashDuration;
|
||||
this.fuel = fuel; // We'd be better of only using time instead of fuel
|
||||
}
|
||||
canDash() {
|
||||
return Date.now() - this.dashTime > this.dashDuration;
|
||||
}
|
||||
|
||||
startDash(){
|
||||
//Commence un dash s'il reste du fuel
|
||||
this.dashing = this.fuel>0;
|
||||
this.dashTime = Date.now();
|
||||
this.velocity.norm = this.velocity.norm + this.extraDashVelocityNorm;
|
||||
}
|
||||
endDash(){
|
||||
//Termine un dash et remet la vitesse à la normale
|
||||
this.dashing = false;
|
||||
this.velocity.norm = Spaceship.afterDashVelocityNorm;
|
||||
}
|
||||
update(dt) {
|
||||
this.body.updateFriction(0.1);
|
||||
if (this.isDashing){
|
||||
if (this.fuel>=dt){
|
||||
this.fuel+=-dt;
|
||||
this.velocity.norm=Spaceship.dashVelocityNorm;
|
||||
}else{
|
||||
this.isDashing=false;
|
||||
this.velocity.norm=Spaceship.velocityNorm;
|
||||
}
|
||||
}
|
||||
this.follower.orientate(this.body, this.form);
|
||||
super.update(dt);
|
||||
//this.follower.follow(this.body, position);
|
||||
//this.follower.follow(this.body, new Vector(10,0));
|
||||
|
||||
}
|
||||
shoot() {
|
||||
return this.shooter.shoot(this);
|
||||
}
|
||||
follow(position) {
|
||||
this.body.follow(position);
|
||||
// this.follower.follow(this.body, position, 4);
|
||||
}
|
||||
show(ctx) {
|
||||
super.show(ctx);
|
||||
this.life.show(ctx, this.position);
|
||||
this.body.motion.show(ctx);
|
||||
}
|
||||
|
||||
static initCollisionListener(collisionEmitter, mainEmitter) {
|
||||
collisionEmitter.on('spaceship/missile', function([spaceship, sid], [missile, mid]) {
|
||||
spaceship.life.value -= missile.damage;
|
||||
mainEmitter.emit("spaceship-update", {sid: sid, event:"life-change", life:spaceship.life.value});
|
||||
if (spaceship.life.value<=0) {
|
||||
spaceship.removing = true;
|
||||
mainEmitter.emit("spaceship-update", {sid: sid, event:"removing"});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import Spaceship from './spaceship.js';
|
||||
import { Grouper } from '../libs/group.js';
|
||||
export default class SpaceshipGroup extends Grouper(Spaceship) {
|
||||
static type = Spaceship;
|
||||
static readyOnePlayer(){
|
||||
const s = Spaceship.random();
|
||||
return new this([["spaceship:0",s]]);
|
||||
}
|
||||
static readyZeroPlayer() {
|
||||
return new this([]);
|
||||
}
|
||||
|
||||
// sg = new SpaceshipGroup();
|
||||
// sg.set(key, value)
|
||||
// sg.delete(key)
|
||||
|
||||
// addSpaceship(idSpaceship, spaceshipObject) {
|
||||
// //Ajoute le spaceship spaceshipObject dans le groupe this avec l'id idSpaceship
|
||||
// this.set(`spaceship:${idSpaceship}`, spaceshipObject);
|
||||
// }
|
||||
|
||||
// removeSpaceship(idSpaceship) {
|
||||
// // debug(`la classe de notre instance spaceshipGroup ${this.constructor.name}`);
|
||||
// // this=this.filter(array => array[0] !== `spaceship:${idSpaceship}`);
|
||||
// // debug(`la classe de notre instance spaceshipGroup ${this.constructor.name}`);
|
||||
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Group from '../libs/group.js';
|
||||
import AsteroidGroup from './asteroidgroup.js';
|
||||
import MeteorGroup from './meteorgroup.js';
|
||||
import SpaceshipGroup from './spaceshipgroup.js';
|
||||
import MissileGroup from './missilegroup.js';
|
||||
|
||||
export default class SuperGroup extends Group {
|
||||
static random() {
|
||||
const g = new this();
|
||||
g.set('asteroidGroup', AsteroidGroup.asteroidField(50, {x: 0, y: 0}, {x: 100, y: 100}));
|
||||
g.set("meteorGroup", MeteorGroup.meteorShower(100));
|
||||
g.set("spaceshipGroup", SpaceshipGroup.readyZeroPlayer());
|
||||
return g;
|
||||
}
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.setDefault("asteroidGroup", new AsteroidGroup());
|
||||
this.setDefault("meteorGroup", new MeteorGroup());
|
||||
this.setDefault("missileGroup", new MissileGroup());
|
||||
this.setDefault("spaceshipGroup", new SpaceshipGroup());
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Generated
+4511
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "spaceinvader",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npm run prod",
|
||||
"build": "npm-run-all clean transpile",
|
||||
"server": "node ./dist-server/bin/www",
|
||||
"dev": "NODE_ENV=development npm-run-all build server",
|
||||
"prod": "NODE_ENV=production npm-run-all build server",
|
||||
"transpile": "babel ./server --out-dir dist-server",
|
||||
"clean": "rimraf dist-server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.11.6",
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"express": "~4.16.1",
|
||||
"morgan": "~1.9.1",
|
||||
"nodemon": "^2.0.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rimraf": "^3.0.2",
|
||||
"socket.io": "^2.3.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-env"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Express</title>
|
||||
<link rel="stylesheet" href="/stylesheets/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Express</h1>
|
||||
<p>Welcome to Express</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import logger from 'morgan';
|
||||
import indexRouter from './routes/index';
|
||||
import usersRouter from './routes/users';
|
||||
|
||||
var app = express();
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
app.use('/', indexRouter);
|
||||
app.use('/users', usersRouter);
|
||||
export default app;
|
||||
|
||||
|
||||
const PORT = process.env.PORT | 8000;
|
||||
console.log(`Port number: ${PORT}`)
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import vm from 'vm';
|
||||
import fs from 'fs';
|
||||
import socket from 'socket.io';
|
||||
import AsteroidServer from '../models/asteroidserver.js';
|
||||
|
||||
const server = http.createServer(app);
|
||||
const io = socket.listen(server);
|
||||
|
||||
import Game from './models/game.js';
|
||||
|
||||
const files = [
|
||||
// Tools and tests
|
||||
// "libs/tools.js",
|
||||
// "libs/test.js",
|
||||
//Base types
|
||||
"libs/iterator.js",
|
||||
"libs/dict.js",
|
||||
"libs/tree.js",
|
||||
// Math types
|
||||
"libs/group.js",
|
||||
// "libs/tensor.js",
|
||||
// "libs/vector.js",
|
||||
"libs/matrix.js",
|
||||
// Visual types
|
||||
"libs/color.js",
|
||||
"libs/point.js",
|
||||
"libs/figure.js",
|
||||
// "libs/line.js",
|
||||
"libs/form.js",
|
||||
"libs/basepolygon.js",
|
||||
"libs/segment.js",
|
||||
"libs/polygon.js",
|
||||
"libs/rectangle.js",
|
||||
"libs/square.js",
|
||||
"libs/circle.js",
|
||||
// Physics types
|
||||
"libs/motion.js",
|
||||
"libs/body.js",
|
||||
"libs/plane.js",
|
||||
"libs/context.js",
|
||||
"libs/entity.js",
|
||||
"libs/manager.js",
|
||||
// Game types
|
||||
"models/gameentity.js",
|
||||
"models/missile.js",
|
||||
"models/missilegroup.js",
|
||||
"models/life.js",
|
||||
"models/follower.js",
|
||||
"models/shooter.js",
|
||||
"models/asteroid.js",
|
||||
"models/asteroidgroup.js",
|
||||
"models/spaceship.js",
|
||||
"models/spaceshipgroup.js",
|
||||
"models/meteor.js",
|
||||
"models/meteorgroup.js",
|
||||
"models/collider.js",
|
||||
"models/gamemap.js",
|
||||
"models/supergroup.js",
|
||||
"models/game.js",
|
||||
// Entry point
|
||||
"models/gameserver.js",
|
||||
];
|
||||
|
||||
// for (const file of files) {
|
||||
// console.log(file);
|
||||
// let data = fs.readFileSync(file);
|
||||
// let script = new vm.Script(data);
|
||||
// script.runInThisContext();
|
||||
// }
|
||||
|
||||
|
||||
server.listen(process.env.PORT || 8000)
|
||||
|
||||
app.use('/libs', express.static('libs'));
|
||||
app.use('/models', express.static('models'));
|
||||
app.use('/dist', express.static('dist'));
|
||||
|
||||
|
||||
const __dirname = path.resolve();
|
||||
|
||||
console.log(__dirname)
|
||||
|
||||
// var path = __dirname.split("/");
|
||||
// path = path.slice(0,-1).join("/");
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile(__dirname + "/views/index.html");
|
||||
});
|
||||
|
||||
|
||||
|
||||
const game = Game.random();
|
||||
//console.log(`Le group ${game.group.get('asteroidGroup')}`);
|
||||
const gameServer = new GameServer(game, io);
|
||||
gameServer.setUp();
|
||||
gameServer.main();
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
import app from '../app';
|
||||
import debugLib from 'debug';
|
||||
import http from 'http';
|
||||
const debug = debugLib('your-project-name:server');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (port >= 0) {
|
||||
// port number
|
||||
return port;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
|
||||
function onListening() {
|
||||
var addr = server.address();
|
||||
var bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import express from 'express';
|
||||
var router = express.Router();
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', function(req, res, next) {
|
||||
res.render('index', { title: 'Express' });
|
||||
});
|
||||
export default router;
|
||||
@@ -0,0 +1,9 @@
|
||||
import express from 'express';
|
||||
var router = express.Router();
|
||||
|
||||
/* GET users listing. */
|
||||
router.get('/', function(req, res, next) {
|
||||
res.send('respond with a resource');
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,5 @@
|
||||
# Todo
|
||||
- Régler le problème des attributs static de classes pour webpack.
|
||||
- Utiliser [webpack](https://webpack.js.org/)
|
||||
- Regarder [Meteor](https://www.meteor.com/) pour propulser le projet vers l'infini
|
||||
- Utiliser du protobuf
|
||||
@@ -0,0 +1,129 @@
|
||||
@startuml asteroids
|
||||
skinparam style strictuml
|
||||
skinparam DefaultFontName Arial
|
||||
skinparam DefaultFontSize 16
|
||||
skinparam DefaultFontStyle bold
|
||||
skinparam ArrowThickness 2.2
|
||||
skinparam ArrowColor #gray
|
||||
hide empty members
|
||||
|
||||
class "GameServer" as GameServer {
|
||||
game : Game
|
||||
dt: float
|
||||
}
|
||||
|
||||
class "GameClient(Manager)" as GameClient {
|
||||
game : Game
|
||||
context: Context
|
||||
dt: float
|
||||
}
|
||||
|
||||
class "Context" as Context {
|
||||
plane: Plane
|
||||
context: Context
|
||||
}
|
||||
|
||||
class "Game" as Game {
|
||||
superGroup: SuperGroup
|
||||
gameMap: GameMap
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
limit()
|
||||
}
|
||||
|
||||
class "GameMap" as GameMap {
|
||||
show(ctx)
|
||||
limit()
|
||||
}
|
||||
|
||||
class "SuperGroup" as SuperGroup {
|
||||
spaceshipGroup : SpaceshipGroup
|
||||
asteroidGroup: AsteroidGroup
|
||||
missileGroup: MissileGroup
|
||||
meteorGroup: meteorGroup
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
collide()
|
||||
}
|
||||
|
||||
class "AsteroidGroup" as AsteroidGroup {
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "Asteroid" as Asteroid {
|
||||
form: Figure
|
||||
body: Body
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "SpaceshipGroup" as SpaceshipGroup {
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "Spaceship" as Spaceship {
|
||||
form : Figure
|
||||
body: Body
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
follow()
|
||||
shoot()
|
||||
explode()
|
||||
}
|
||||
|
||||
class "MeteorGroup" as MeteorGroup {
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "Meteor" as Meteor {
|
||||
form: Figure
|
||||
body: Body
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "MissileGroup" as MissileGroup {
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class "Missile" as Missile {
|
||||
form: Figure
|
||||
body: Body
|
||||
static random()
|
||||
update(dt)
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
|
||||
GameServer *-- Game
|
||||
GameClient *-- Game
|
||||
|
||||
GameClient *-- Context
|
||||
|
||||
Game *-- GameMap
|
||||
Game *-- SuperGroup
|
||||
|
||||
SuperGroup *-- SpaceshipGroup
|
||||
SuperGroup *-- MeteorGroup
|
||||
SuperGroup *-- AsteroidGroup
|
||||
SuperGroup *-- MissileGroup
|
||||
|
||||
SpaceshipGroup "1" *-- "*" Spaceship
|
||||
MissileGroup "1" *-- "*" Missile
|
||||
MeteorGroup "1" *-- "*" Meteor
|
||||
AsteroidGroup "1" *-- "*" Asteroid
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,117 @@
|
||||
@startuml forms
|
||||
skinparam style strictuml
|
||||
skinparam DefaultFontName Arial
|
||||
skinparam DefaultFontSize 16
|
||||
skinparam DefaultFontStyle bold
|
||||
skinparam ArrowThickness 2.2
|
||||
skinparam ArrowColor #gray
|
||||
hide empty members
|
||||
|
||||
class Figure {
|
||||
fill: Bool
|
||||
lineWidth: Number
|
||||
color: Color
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Form {
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class BasePolygon {
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
get/set segments()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Point {
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Vector {
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Segment {
|
||||
points: Matrix[Point]
|
||||
}
|
||||
|
||||
class Line {
|
||||
point: Point
|
||||
vector: Vector
|
||||
}
|
||||
|
||||
class Circle {
|
||||
center: Vector
|
||||
radius: Number
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Polygon {
|
||||
matrix: Matrix[Vector]
|
||||
get length = n
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
get/set points()
|
||||
get/set segments()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Triangle {
|
||||
matrix: Matrix[n][Vector]
|
||||
get length = 3
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
get/set points()
|
||||
get/set segments()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Rectangle {
|
||||
position: Vector
|
||||
size: Vector
|
||||
get length = 4
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
get/set points()
|
||||
get/set segments()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
class Square {
|
||||
x: Number
|
||||
y: Number
|
||||
s: Number
|
||||
get length = 4
|
||||
get/set center()
|
||||
get/set perimeter()
|
||||
get/set area()
|
||||
get/set points()
|
||||
get/set segments()
|
||||
show(ctx)
|
||||
}
|
||||
|
||||
Array <|-- Tensor
|
||||
Tensor <|-- Vector
|
||||
Vector <|-- Point
|
||||
Figure <|-- Form
|
||||
Figure <|-- Segment
|
||||
Figure <|-- Line
|
||||
Form <|-- BasePolygon
|
||||
Form <|-- Circle
|
||||
BasePolygon <|-- Polygon
|
||||
BasePolygon <|-- Square
|
||||
BasePolygon <|-- Rectangle
|
||||
Polygon <|-- Triangle
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,57 @@
|
||||
@startuml maths
|
||||
skinparam style strictuml
|
||||
skinparam DefaultFontName Arial
|
||||
skinparam DefaultFontSize 16
|
||||
skinparam DefaultFontStyle bold
|
||||
skinparam ArrowThickness 2.2
|
||||
skinparam ArrowColor #gray
|
||||
hide empty members
|
||||
|
||||
class Array {
|
||||
}
|
||||
|
||||
class Tensor {
|
||||
order: int
|
||||
}
|
||||
|
||||
class Vector {
|
||||
order:int = 1
|
||||
get/set x()
|
||||
get/set y()
|
||||
get/set z()
|
||||
get/set angle()
|
||||
get/set norm()
|
||||
add()
|
||||
sub()
|
||||
mul()
|
||||
show()
|
||||
}
|
||||
|
||||
class Matrix {
|
||||
order:int = 2
|
||||
get/set vectors()
|
||||
}
|
||||
|
||||
class Motion {
|
||||
order:int = 2
|
||||
get/set vectors()
|
||||
get/set width()
|
||||
get/set height()
|
||||
update()
|
||||
show()
|
||||
}
|
||||
|
||||
class Body {
|
||||
order: int = 3
|
||||
get/set motions()
|
||||
update()
|
||||
}
|
||||
|
||||
Array <|-- Tensor
|
||||
Tensor <|-- Vector
|
||||
Tensor <|-- Matrix
|
||||
Tensor <|-- Body
|
||||
Matrix <|-- Motion
|
||||
Vector <|-- Point
|
||||
|
||||
@enduml
|
||||
Reference in New Issue
Block a user