From 13f9a6bffbbe65506cbaf3467426cc5e49e867f5 Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 02:55:09 -0400 Subject: [PATCH 1/9] Project 7 Completed --- .gitignore | 3 + README.md | 48 ++----- index.html | 19 +++ package.json | 30 ++++ src/agent.js | 242 ++++++++++++++++++++++++++++++++ src/framework.js | 74 ++++++++++ src/main.js | 347 ++++++++++++++++++++++++++++++++++++++++++++++ src/marker.js | 188 +++++++++++++++++++++++++ webpack.config.js | 28 ++++ 9 files changed, 944 insertions(+), 35 deletions(-) create mode 100755 .gitignore mode change 100644 => 100755 README.md create mode 100755 index.html create mode 100755 package.json create mode 100644 src/agent.js create mode 100755 src/framework.js create mode 100755 src/main.js create mode 100644 src/marker.js create mode 100755 webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..efed4dbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +*.map +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 40864757..762fe767 --- a/README.md +++ b/README.md @@ -1,40 +1,18 @@ # BioCrowds -Biocrowds is a crowd simulation algorithm based on the formation of veination patterns on leaves. It prevents agents from colliding with each other on their way to their goal points using a notion of "personal space". Personal space is modelled with a space colonization algorithm. Markers (just points) are scattered throughout the simulation space, on the ground. At each simulation frame, each marker becomes "owned" by the agent closest to it (with some max distance representing an agent's perception). Agent velocity at the next frame is then computed using a sum of the displacement vectors to each of its markers. Because a marker can only be owned by one agent at a time, this technique prevents agents from colliding. -## Agent Representation (15 pts) -Create an agent class to hold properties used for simulating and drawing the agent. Some properties you may want to consider include the following: -- Position -- Velocity -- Goal -- Orientation -- Size -- Markers +A Biocrowds implementation with 3 different scenarios: -## Grid/Marker Representation (25 pts) -Markers should be scattered randomly across a uniform grid. You should implement an efficient way of determining the nearest agent to a given marker. Based on an marker's location, you should be able to get the nearest four grid cells and loop through all the agents contained in them. +- Opposite - Two opposing groups of agents assigned to corners with goals on opposite corners. +- Lined - Two opposing lines of agents with goals on opposite sides of the grid. +- Home - Agents move towards a single goal point. -## Setup (10 pts) -- Create a scene (standard, with camera controls) and scatter markers across the entire ground plane -- Spawn agents with specified goal points +The GUI supports modifying the following parameters: -## Per Frame (35 pts) -- Assign markers to the nearest agent within a given radius. Be sure that a marker is only assigned to a single, unique agent. -- Compute velocity for each agent -- New velocity is determined by summing contributions from all the markers the agent "owns". Each marker contribution consists of the displacement vector between the agent and the marker multiplied by some (non-negative) weight. The weighting is based on - - Similarity between the displacement vector and the vector to agent's goal (the more similar, the higher the weight. A dot product works well) - - Distance from agent (the further away, the less contribution) -Each contribution is normalized by the total marker contributions (divide each contribution by sum total) - - Clamp velocity to some maximum (you probably want to choose a max speed such that you agent will never move further than its marker radius) -- Move agents by their newly computed velocity * time step -- Draw a ground plane and cylinders to represent the agents. -- For a more thorough explanation, see [HERE](http://www.inf.pucrs.br/~smusse/Animacao/2016/CrowdTalk.pdf) and [HERE](http://www.sciencedirect.com/science/article/pii/S0097849311001713) and [HERE](https://books.google.com/books?id=3Adh_2ZNGLAC&pg=PA146&lpg=PA146&dq=biocrowds%20algorithm&source=bl&ots=zsM86iYTot&sig=KQJU7_NagMK4rbpY0oYc3bwCh9o&hl=en&sa=X&ved=0ahUKEwik9JfPnubSAhXIxVQKHUybCxUQ6AEILzAE#v=onepage&q=biocrowds%20algorithm&f=false) and [HERE](https://cis700-procedural-graphics.github.io/files/biocrowds_3_21_17.pdf) - -## Two scenarios -- Create two different scenarios (initial agent placement, goals) to show off the collision avoidance. Try to pick something interesting! Classics include two opposing lines of agents with goals on opposite sides, or agents that spawn in a circle, which each agent's goal directly across. -- Provide some way to switch between scenarios - -## Deploy your code! (5 pts) -- Your demo should run on your gh-pages branch - -## Extra credit -- Add obstacles to your scene, such that agents avoid them \ No newline at end of file +- Grid Width - Modify the size of the grid in which the agents lie. +- Agent Density - Modify the density of the agent groups. +- Agent Speed - Change the agent speed. +- Marker Density - Increase the number of markers per tile in the grid. +- Scenario - Choose from one of three scenarios mentioned above. +- Goal X Position - Change the x component of the goal positions of all the agents (overwrites the predefined goal points of the scenarios). +- Goal Z Position - Change the z component of the goal positions of all the agents (overwrites the predefined goal points of the scenarios). +- Pause - Pause movement of agents. \ No newline at end of file diff --git a/index.html b/index.html new file mode 100755 index 00000000..5f502ced --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + HW: Curves and Instancing + + + + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100755 index 00000000..c80e8a32 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "scripts": { + "start": "webpack-dev-server --hot --inline", + "build": "webpack", + "deploy": "rm -rf npm-debug.log && git checkout master && git commit -am 'update' && gh-pages-deploy" + }, + "gh-pages-deploy": { + "prep": [ + "build" + ], + "noprompt": true + }, + "dependencies": { + "dat-gui": "^0.5.0", + "gl-matrix": "^2.3.2", + "stats-js": "^1.0.0-alpha1", + "three": "^0.82.1", + "three-orbit-controls": "^82.1.0", + "three-obj-loader": "^1.0.2" + }, + "devDependencies": { + "babel-core": "^6.18.2", + "babel-loader": "^6.2.8", + "babel-preset-es2015": "^6.18.0", + "gh-pages-deploy": "^0.4.2", + "webpack": "^1.13.3", + "webpack-dev-server": "^1.16.2", + "webpack-glsl-loader": "^1.0.1" + } +} diff --git a/src/agent.js b/src/agent.js new file mode 100644 index 00000000..eb53c8eb --- /dev/null +++ b/src/agent.js @@ -0,0 +1,242 @@ +const THREE = require('three'); + +export function Agent(pos) { + this.position = pos;//[pos.x,pos.y,pos.z]; + this.forward = new THREE.Vector3(1,0,0); + this.velocity = new THREE.Vector3(0,0,0); + this.orientation = [1,1,1]; + this.size = [1,1,1]; + this.goal = new THREE.Vector3(0,0,0); + this.color = 0xFFFFFF; + this.markers = []; + this.mesh = null; + + this.updatePosition = function(agents) { + + var x = Math.floor(this.position.x); + var z = Math.floor(this.position.z); + var tile_agents = agents[x][z]; + + var index = tile_agents.indexOf(this); + + if (index > -1) { + tile_agents.splice(index, 1); + } else { + console.log("WARNING: Agent index should always exist.") + } + + this.position.x += this.velocity.x; + this.position.y += this.velocity.y; + this.position.z += this.velocity.z; + + if (this.mesh) { + this.mesh.scale.set(0.3,0.3,0.3); + this.mesh.position.x = this.position.x; + this.mesh.position.z = this.position.z; + } + + var new_x = Math.floor(this.position.x); + var new_z = Math.floor(this.position.z); + + if (typeof agents[new_x] == 'undefined') { + agents[new_x] = []; + } + + if (typeof agents[new_x][new_z] == 'undefined') { + agents[new_x][new_z] = []; + } + agents[new_x][new_z].push(this); + + } +} + +function createAgent(pos) { + + var agent = new Agent(pos); + +// var triangleGeometry = new THREE.Geometry(); +// triangleGeometry.vertices.push(new THREE.Vector3(0.0, 0, 0.4)); +// triangleGeometry.vertices.push(new THREE.Vector3(1.0, 0, 0.0)); +// triangleGeometry.vertices.push(new THREE.Vector3( 0.0, 0, -0.4)); +// triangleGeometry.vertices.push(new THREE.Vector3(0.0, 0.4, 0.0)); +// +// triangleGeometry.faces.push(new THREE.Face3(0, 1, 2)); +// triangleGeometry.faces.push(new THREE.Face3(0, 3, 1)); +// triangleGeometry.faces.push(new THREE.Face3(1, 3, 2)); +// triangleGeometry.faces.push(new THREE.Face3(2, 3, 0)); + var cylinder = new THREE.CylinderGeometry(0.4, 0.4, 2); + + var material = new THREE.MeshBasicMaterial({ + color:0xFF0000, + side:THREE.DoubleSide + }); + + var mesh = new THREE.Mesh(cylinder, material); + mesh.position.set(pos.x, pos.y+0.21, pos.z); + mesh.scale.set(0.3,0.3,0.3); + + agent.mesh = mesh; + + return agent; +} + +export default function Crowd(agent_density, grid_length) { + + this.agentsData = []; + //this.spacing = Math.sqrt(grid_length * grid_length / num_agents); + this.spacing = 1.0/agent_density; + this.gridWidth = grid_length; + this.scenario = 1; + + this.createAgents = function() { + var spacing_ceil = Math.ceil(this.spacing); + + switch (this.scenario) { + case 1: + for (var i = 0; i < this.gridWidth/2; i+=spacing_ceil) { + + var row = Math.floor(i); + + for (var j = 0; j < this.gridWidth/2-row; j+=spacing_ceil) { + + var col = Math.floor(j); + + var x = i+Math.random()*this.spacing; + var z = j+Math.random()*this.spacing; + var agent = createAgent(new THREE.Vector3(x,0,z)); + agent.goal.x = this.gridWidth-1; + agent.goal.z = this.gridWidth-1; + + var floor_x = Math.floor(x); + var floor_z = Math.floor(z); + + if (!this.agentsData[floor_x]) { + this.agentsData[floor_x] = []; + } + + if (!this.agentsData[floor_x][floor_z]) { + this.agentsData[floor_x][floor_z] = []; + } + this.agentsData[floor_x][floor_z].push(agent); + } + } + for (var i = this.gridWidth-spacing_ceil; i >= this.gridWidth/2; i-=spacing_ceil) { + + var row = Math.floor(i); + + for (var j = this.gridWidth-spacing_ceil; j >= this.gridWidth/2+(this.gridWidth-spacing_ceil-row); j-=spacing_ceil) { + + var col = Math.floor(j); + + var x = i+Math.random()*this.spacing; + var z = j+Math.random()*this.spacing; + var agent = createAgent(new THREE.Vector3(x,0,z)); + agent.goal.x = 0; + agent.goal.z = 0; + + var floor_x = Math.floor(x); + var floor_z = Math.floor(z); + + if (!this.agentsData[floor_x]) { + this.agentsData[floor_x] = []; + } + + if (!this.agentsData[floor_x][floor_z]) { + this.agentsData[floor_x][floor_z] = []; + } + this.agentsData[floor_x][floor_z].push(agent); + } + } + break; + case 2: + for (var i = 0; i < this.gridWidth; i+=spacing_ceil) { + + var row = Math.floor(i); + + for (var j = 0; j < 3; j+=spacing_ceil) { + + var col = Math.floor(j); + + var x = i+Math.random()*this.spacing; + var z = j+Math.random()*this.spacing; + var agent = createAgent(new THREE.Vector3(x,0,z)); + agent.goal.x = x; + agent.goal.z = this.gridWidth; + + var floor_x = Math.floor(x); + var floor_z = Math.floor(z); + + if (!this.agentsData[floor_x]) { + this.agentsData[floor_x] = []; + } + + if (!this.agentsData[floor_x][floor_z]) { + this.agentsData[floor_x][floor_z] = []; + } + this.agentsData[floor_x][floor_z].push(agent); + } + } + for (var i = this.gridWidth-spacing_ceil; i >= 0; i-=spacing_ceil) { + + var row = Math.floor(i); + + for (var j = this.gridWidth-spacing_ceil; j >= this.gridWidth-spacing_ceil-2; j-=spacing_ceil) { + + var col = Math.floor(j); + + var x = i+Math.random()*this.spacing; + var z = j+Math.random()*this.spacing; + var agent = createAgent(new THREE.Vector3(x,0,z)); + agent.goal.x = x; + agent.goal.z = 0; + + var floor_x = Math.floor(x); + var floor_z = Math.floor(z); + + if (!this.agentsData[floor_x]) { + this.agentsData[floor_x] = []; + } + + if (!this.agentsData[floor_x][floor_z]) { + this.agentsData[floor_x][floor_z] = []; + } + this.agentsData[floor_x][floor_z].push(agent); + } + } + break; + case 3: + for (var i = 0; i < this.gridWidth; i+=spacing_ceil) { + + var row = Math.floor(i); + + for (var j = 0; j < this.gridWidth; j+=spacing_ceil) { + + var col = Math.floor(j); + + var x = i+Math.random()*this.spacing; + var z = j+Math.random()*this.spacing; + var agent = createAgent(new THREE.Vector3(x,0,z)); + + var floor_x = Math.floor(x); + var floor_z = Math.floor(z); + + if (typeof this.agentsData[floor_x] == 'undefined') { + this.agentsData[floor_x] = []; + } + + if (typeof this.agentsData[floor_x][floor_z] == 'undefined') { + this.agentsData[floor_x][floor_z] = []; + } + this.agentsData[floor_x][floor_z].push(agent); + } + } + break; + default: + console.log('Not an existing scenario.'); + break; + } + + return this.agentsData; + } + +} \ No newline at end of file diff --git a/src/framework.js b/src/framework.js new file mode 100755 index 00000000..427e2d19 --- /dev/null +++ b/src/framework.js @@ -0,0 +1,74 @@ + +const THREE = require('three'); +const OrbitControls = require('three-orbit-controls')(THREE) +const OBJLoader = require('three-obj-loader')(THREE) +import Stats from 'stats-js' +import DAT from 'dat-gui' + +// when the scene is done initializing, the function passed as `callback` will be executed +// then, every frame, the function passed as `update` will be executed +function init(callback, update) { + var stats = new Stats(); + stats.setMode(1); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + document.body.appendChild(stats.domElement); + + var gui = new DAT.GUI(); + + var framework = { + gui: gui, + stats: stats + }; + + // run this function after the window loads + window.addEventListener('load', function() { + + var scene = new THREE.Scene(); + var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 ); + var renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0xdddddd, 1); + + var controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.enableZoom = true; + controls.target.set(0, 0, 0); + controls.rotateSpeed = 0.3; + controls.zoomSpeed = 1.0; + controls.panSpeed = 2.0; + + document.body.appendChild(renderer.domElement); + + // resize the canvas when the window changes + window.addEventListener('resize', function() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }, false); + + // assign THREE.js objects to the object we will return + framework.scene = scene; + framework.camera = camera; + framework.renderer = renderer; + framework.controls = controls; + + // begin the animation loop + (function tick() { + stats.begin(); + update(framework); // perform any requested updates + renderer.render(scene, camera); // render the scene + stats.end(); + requestAnimationFrame(tick); // register to call this again when the browser renders a new frame + })(); + + // we will pass the scene, gui, renderer, camera, etc... to the callback function + return callback(framework); + }); +} + +export default { + init: init +} \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100755 index 00000000..438cc9be --- /dev/null +++ b/src/main.js @@ -0,0 +1,347 @@ + +// Skybox texture from: https://github.com/mrdoob/three.js/tree/master/examples/textures/cube/skybox + +const THREE = require('three'); // older modules are imported like this. You shouldn't have to worry about this much +import Framework from './framework' +import Crowd, {Agent} from './agent.js' +import MarkerField, {Marker} from './marker.js' + +var crowd = null; +var markerField = null; +var scene = null; + +var line_material = new THREE.LineBasicMaterial({ + color: 0xFFFFFF + }); +var line_geometry = new THREE.Geometry(); +var line_segments = new THREE.LineSegments(line_geometry, line_material); +var temp_vel = new THREE.Vector3(0,0,0); +var temp_displacement = new THREE.Vector3(0,0,0); +var temp_vector_to_goal = new THREE.Vector3(0,0,0); + +var config = { + pause : false, + grid_width : 20, + marker_density : 5.0, + agent_density : 0.5, + agent_speed : 1, + goal_x : 0, + goal_y : 0, + goal_z : 0, + scenario: 1 +} + +// called after the scene loads +function onLoad(framework) { + scene = framework.scene; + var camera = framework.camera; + var renderer = framework.renderer; + var gui = framework.gui; + var stats = framework.stats; + var controls = framework.controls; + + // Basic Lambert white + var lambertWhite = new THREE.MeshLambertMaterial({ color: 0xaaaaaa, side: THREE.DoubleSide }); + + // Set light + var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 ); + directionalLight.color.setHSL(0.1, 1, 0.95); + directionalLight.position.set(1, 3, 2); + directionalLight.position.multiplyScalar(10); + + var grid_width = 20; + var marker_density = 5; + var agent_density = 0.5; + + //TODO: look at center and offset grid data + camera.position.set(grid_width/2, grid_width, grid_width/2); + camera.lookAt(new THREE.Vector3(grid_width/2,0,grid_width/2)); + controls.target.set(grid_width/2, 0, grid_width/2); + + scene.add(directionalLight); + + markerField = new MarkerField(marker_density, grid_width); + var grid = markerField.createGridLines(); + var field = markerField.createField(); + var markers = markerField.createMarkers(); + field.position.y = -0.1; + scene.add(grid); + scene.add(field); + scene.add(markers); + + crowd = new Crowd(agent_density, grid_width); + var agents = crowd.createAgents(); + //console.log(agents); + + console.log(agents); + + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + scene.add(tile_agents[k].mesh); + } + } + } + } + + + markerField.assignMarkersToAgents(crowd.agentsData); + + scene.add(line_segments); + + line_segments.name = "debug"; + + gui.add(config, 'grid_width', 10, 50).step(10).name("Grid Width").onChange( function(width) + { + config.grid_width = width; + reset(); + }); + + gui.add(config, 'agent_density', 0.1, 1.0).name("Agent Density").onChange( function(agentDensity) + { + config.agent_density = agentDensity; + reset(); + }); + + gui.add(config, 'agent_speed', 1, 5).name("Agent Speed").onChange( function(agentSpeed) + { + config.agent_speed = agentSpeed; + reset(); + }); + + gui.add(config, 'marker_density', 3, 10, 1).name("Marker Density").onChange( function(markerDensity) + { + config.marker_density = markerDensity; + reset(); + }); + + gui.add(config, 'scenario', { Opposite: 1, Lined: 2, Home: 3 } ).name("Scenario").onChange(function(newScenario) { + config.scenario = newScenario; + reset(); + }); + + gui.add(config, 'goal_x', 0, 50).name("Goal X Position").onChange( function(goal_x) + { + config.goal_x = goal_x; + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + tile_agents[k].goal.x = Math.min(goal_x-1,config.grid_width-1); + } + } + } + } + }); + + gui.add(config, 'goal_z', 0, 50).name("Goal Z Position").onChange( function(goal_z) + { + config.goal_z = goal_z; + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + tile_agents[k].goal.z = Math.min(goal_z-1,config.grid_width-1); + } + } + } + } + }); + + gui.add(config, 'pause').name("Pause").onChange(function(value) { + + if (value) { + config.pause = value; + } else { + config.pause = value; + } + }); + + var reset = function() { + scene.remove(markers); + scene.remove(grid); + scene.remove(field); + camera.position.set( config.grid_width/2, config.grid_width, config.grid_width/2); + camera.lookAt(new THREE.Vector3( config.grid_width/2,0, config.grid_width/2)); + controls.target.set( config.grid_width/2, 0, config.grid_width/2); + + markerField = new MarkerField(config.marker_density, config.grid_width); + grid = markerField.createGridLines(); + scene.add(grid); + field = markerField.createField(); + field.position.y = -0.1; + scene.add(field); + markers = markerField.createMarkers(); + scene.add(markers); + + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + scene.remove(tile_agents[k].mesh); + } + } + } + } + crowd = new Crowd(config.agent_density, config.grid_width); + crowd.scenario = Math.round(config.scenario); + console.log(crowd.scenario); + agents = crowd.createAgents(); + + + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + scene.add(tile_agents[k].mesh); + } + } + } + } + } +} + + +var frame = 0; +// called on frame updates +function onUpdate(framework) { + + if (crowd && markerField && !config.pause) { + + markerField.assignMarkersToAgents(crowd.agentsData); + + var agents = crowd.agentsData; + + line_geometry.vertices = []; + line_geometry.faces = []; + + for (var i = 0; i <= agents.length; i++) { + + if (!agents[i]) { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + var agent = tile_agents[k]; + + temp_vel.x = 0; + temp_vel.y = 0; + temp_vel.z = 0; + + var markers = agent.markers; + var sum_weight = 0; + + temp_vector_to_goal.x = agent.goal.x - agent.position.x; + temp_vector_to_goal.y = agent.goal.y - agent.position.y; + temp_vector_to_goal.z = agent.goal.z - agent.position.z; + + if (temp_vector_to_goal.length() < 0.5) + continue; + + for (var m = 0; m < markers.length; m++) { + + var marker = markers[m]; + temp_displacement.x = marker.position.x - agent.position.x; + temp_displacement.y = marker.position.y - agent.position.y; + temp_displacement.z = marker.position.z - agent.position.z; + + var dot = temp_displacement.dot(temp_vector_to_goal); + + var cos_theta = dot / (temp_displacement.length() * temp_vector_to_goal.length()); + + marker.weight = (1.0 + cos_theta) / (1.0 + temp_displacement.length()); + + sum_weight += marker.weight; + + } + + for (var m = 0; m < markers.length; m++) { + + var marker = markers[m]; + temp_displacement.x = marker.position.x - agent.position.x; + temp_displacement.y = marker.position.y - agent.position.y; + temp_displacement.z = marker.position.z - agent.position.z; + + marker.weight = marker.weight / sum_weight; + + temp_vel.add(temp_displacement.multiplyScalar(marker.weight)); + } + + agent.velocity = temp_vel.multiplyScalar(0.1*config.agent_speed); + + + agent.updatePosition(agents); + + //Uncomment to draw debug lines +// for (var m = 0; m < agent.markers.length; m++) { +// +// line_geometry.vertices.push( +// agent.position, +// agent.markers[m].position +// ); +// +// } + + } + } + } + } + + line_segments.geometry.verticesNeedUpdate = true; + line_segments.geometry.dynamic = true; + line_segments.geometry.elementsNeedUpdate = true; + + } + + frame++; + +} + +// when the scene is done initializing, it will call onLoad, then on frame updates, call onUpdate +Framework.init(onLoad, onUpdate); \ No newline at end of file diff --git a/src/marker.js b/src/marker.js new file mode 100644 index 00000000..90d114f7 --- /dev/null +++ b/src/marker.js @@ -0,0 +1,188 @@ +const THREE = require('three'); + +export function Marker(pos) { + this.position = pos; + this.color = 0xFFFFFF; + this.agent = null; + this.weight = 0; +} + +function assignAgentTo(marker, agents, availability, width) { + + var x = Math.floor(marker.position.x); + var z = Math.floor(marker.position.z); + var min = [x-1, z-1]; + var max = [x+1, z+1]; + if (min[0] < 0) + min[0] = 0; + + if (min[1] < 0) + min[1] = 0; + + if (max[0] > width-1) + max[0] = width-1; + + if (max[1] > width-1) + max[1] = width-1; + + var closest_agent = null; + var closest_dist = Number.MAX_VALUE; + + for (var i = min[0]; i <= max[0]; i++) { + + if (typeof agents[i] == 'undefined') { + continue; + } + + for (var j = min[1]; j <= max[1]; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + + var dist = tile_agents[k].position.distanceTo(marker.position); + + if (dist < closest_dist && dist < 2.0) { + closest_agent = tile_agents[k]; + closest_dist = dist; + } + + } + } + + } + } + + if (closest_agent) { + closest_agent.markers.push(marker); + marker.agent = closest_agent; + } + +} + +function assignMarkersTo(agents, markers, availability, width) { + + //clear all markers assigned to agents + for (var i = 0; i <= agents.length; i++) { + + if (typeof agents[i] == 'undefined') { + continue; + } + + for (var j = 0; j <= agents[i].length; j++) { + + var tile_agents = agents[i][j]; + if (tile_agents) { + + for (var k = 0; k < tile_agents.length; k++) { + var dist = tile_agents[k].markers = []; + } + } + } + } + + + for (var i = 0; i < markers.length; i++) { + + for (var j = 0; j < markers.length; j++) { + + var tile_markers = markers[i][j]; + if (tile_markers) { + + for (var k = 0; k < tile_markers.length; k++) { + + assignAgentTo(tile_markers[k], agents, availability, width); + + } + } + + } + } + +} + +export default function MarkerField(num_markers_per_field, field_width) { + + this.markers = null; + this.density = num_markers_per_field; + this.width = field_width; + this.markersData = []; + this.markersAvailability = []; + + this.createGridLines = function() { + + var plane = new THREE.Mesh( + new THREE.PlaneGeometry( this.width, this.width, this.width, this.width), + new THREE.MeshBasicMaterial( { + color: 0xFFFFFF, + wireframe: true + } ) + ); + plane.rotation.x = Math.PI/2; + plane.position.x = this.width/2; + plane.position.y = 0; + plane.position.z = this.width/2; + return plane; + } + + this.createField = function() { + + var squareGeometry = new THREE.Geometry(); + squareGeometry.vertices.push(new THREE.Vector3(0.0, 0.0, this.width)); + squareGeometry.vertices.push(new THREE.Vector3(this.width, 0.0, this.width)); + squareGeometry.vertices.push(new THREE.Vector3(this.width, 0.0, 0.0)); + squareGeometry.vertices.push(new THREE.Vector3(0.0, 0.0, 0.0)); + squareGeometry.faces.push(new THREE.Face3(0, 1, 2)); + squareGeometry.faces.push(new THREE.Face3(0, 2, 3)); + var squareMaterial = new THREE.MeshBasicMaterial({ + color:0x0000000, + side:THREE.DoubleSide + }); + var squareMesh = new THREE.Mesh(squareGeometry, squareMaterial); + squareMesh.position.set(0.0, 0.0, 0.0); + return squareMesh; + + } + + this.createMarkers = function() { + + var markersGeo = new THREE.Geometry(); + var markerMaterial = new THREE.PointsMaterial({ + color: 0xFFFFFF, size: 0.05 }); + + for (var i = 0; i < this.width; i+= 1.0/this.density) { + var row = Math.floor(i); + + if (typeof this.markersData[row] == 'undefined') { + this.markersData[row] = []; + this.markersAvailability[row] = []; + } + + for (var j = 0; j < this.width; j+= 1.0/this.density) { + + var col = Math.floor(j); + if (typeof this.markersData[row][col] == 'undefined') { + this.markersData[row][col] = []; + this.markersAvailability[row][col] = []; + } + + var x = i+Math.random()/this.density; + var z = j+Math.random()/this.density; + this.markersData[row][col].push(new Marker(new THREE.Vector3(x, 0, z))); + this.markersAvailability[row][col].push(1); + + markersGeo.vertices.push(new THREE.Vector3(x,0,z)); + } + } + + console.log(this.markersData); + this.markers = new THREE.Points(markersGeo, markerMaterial); + return this.markers; + } + + this.assignMarkersToAgents = function(agents) { + assignMarkersTo(agents, this.markersData, this.markersAvailability, this.width); + }; + +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100755 index 00000000..57dce485 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,28 @@ +const path = require('path'); + +module.exports = { + entry: path.join(__dirname, "src/main"), + output: { + filename: "./bundle.js" + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /(node_modules|bower_components)/, + loader: 'babel', + query: { + presets: ['es2015'] + } + }, + { + test: /\.glsl$/, + loader: "webpack-glsl" + }, + ] + }, + devtool: 'source-map', + devServer: { + port: 7000 + } +} \ No newline at end of file From 905b32e0c49df538ed4ced556673cc692e77c123 Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 02:59:22 -0400 Subject: [PATCH 2/9] Add deploy.js --- deploy.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 deploy.js diff --git a/deploy.js b/deploy.js new file mode 100644 index 00000000..57204bec --- /dev/null +++ b/deploy.js @@ -0,0 +1,22 @@ +var colors = require('colors'); +var path = require('path'); +var git = require('simple-git')(__dirname); + +git.status(function(err, status) { + if (err) throw err; + if (!status.isClean()) { + console.error('Error: You have uncommitted changes! Please commit them first'.red); + } else { + git.raw(['subtree', 'split', '--prefix', 'build', '-b', 'gh-pages'], function(err, result) { + if (err) console.warn(err.toString().yellow); + + git.push(['origin', 'gh-pages', '-f'], function(err, result) { + if (err) { + console.error(err.toString().red); + } else { + console.log('Deployed!'.green); + } + }); + }) + } +}) \ No newline at end of file From f3c8c8b21ef0b60c05871619b54806cdad83e35e Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 03:02:54 -0400 Subject: [PATCH 3/9] Update deploy.js --- deploy.js | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/deploy.js b/deploy.js index 57204bec..9defe7c3 100644 --- a/deploy.js +++ b/deploy.js @@ -1,22 +1,38 @@ var colors = require('colors'); var path = require('path'); var git = require('simple-git')(__dirname); +var deploy = require('gh-pages-deploy'); +var packageJSON = require('require-module')('./package.json'); -git.status(function(err, status) { - if (err) throw err; - if (!status.isClean()) { - console.error('Error: You have uncommitted changes! Please commit them first'.red); - } else { - git.raw(['subtree', 'split', '--prefix', 'build', '-b', 'gh-pages'], function(err, result) { - if (err) console.warn(err.toString().yellow); +var success = 1; +git.fetch('origin', 'master', function(err) { + if (err) throw err; + git.status(function(err, status) { + if (err) throw err; + if (!status.isClean()) { + success = 0; + console.error('Error: You have uncommitted changes! Please commit them first'.red); + } - git.push(['origin', 'gh-pages', '-f'], function(err, result) { - if (err) { - console.error(err.toString().red); - } else { - console.log('Deployed!'.green); + if (status.current !== 'master') { + success = 0; + console.warn('Warning: Please deploy from the master branch!'.yellow) } - }); + + git.diffSummary(['origin/master'], function(err, diff) { + if (err) throw err; + + if (diff.files.length || diff.insertions || diff.deletions) { + success = 0; + console.error('Error: Current branch is different from origin/master! Please push all changes first'.red) + } + + if (success) { + var cfg = packageJSON['gh-pages-deploy'] || {}; + var buildCmd = deploy.getFullCmd(cfg); + deploy.displayCmds(deploy.getFullCmd(cfg)); + deploy.execBuild(buildCmd, cfg); + } + }) }) - } }) \ No newline at end of file From cbf2d413b985ea4fdfedbdd902bb17f4f5bbd375 Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 03:13:12 -0400 Subject: [PATCH 4/9] Update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index efed4dbc..cf9733fa 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules *.map -.DS_Store \ No newline at end of file +.DS_Store +bundle.js \ No newline at end of file From db71673b75337bf36705fa2e17212088f51659e6 Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 03:17:12 -0400 Subject: [PATCH 5/9] Clean comment --- src/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.js b/src/main.js index 438cc9be..13636c3b 100755 --- a/src/main.js +++ b/src/main.js @@ -53,7 +53,6 @@ function onLoad(framework) { var marker_density = 5; var agent_density = 0.5; - //TODO: look at center and offset grid data camera.position.set(grid_width/2, grid_width, grid_width/2); camera.lookAt(new THREE.Vector3(grid_width/2,0,grid_width/2)); controls.target.set(grid_width/2, 0, grid_width/2); From 254293150731c5dd2aabc282f998242dfa86a41c Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 03:23:26 -0400 Subject: [PATCH 6/9] Update package.json --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) mode change 100755 => 100644 package.json diff --git a/package.json b/package.json old mode 100755 new mode 100644 index c80e8a32..be683fcb --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "scripts": { "start": "webpack-dev-server --hot --inline", "build": "webpack", - "deploy": "rm -rf npm-debug.log && git checkout master && git commit -am 'update' && gh-pages-deploy" + "deploy": "node deploy.js" }, "gh-pages-deploy": { "prep": [ @@ -15,14 +15,15 @@ "gl-matrix": "^2.3.2", "stats-js": "^1.0.0-alpha1", "three": "^0.82.1", - "three-orbit-controls": "^82.1.0", - "three-obj-loader": "^1.0.2" + "three-orbit-controls": "^82.1.0" }, "devDependencies": { "babel-core": "^6.18.2", "babel-loader": "^6.2.8", "babel-preset-es2015": "^6.18.0", + "colors": "^1.1.2", "gh-pages-deploy": "^0.4.2", + "simple-git": "^1.65.0", "webpack": "^1.13.3", "webpack-dev-server": "^1.16.2", "webpack-glsl-loader": "^1.0.1" From 652e3c7bfa3729dc14a5252f3699a3bd36472202 Mon Sep 17 00:00:00 2001 From: Mohamed Soudy Date: Wed, 29 Mar 2017 03:25:51 -0400 Subject: [PATCH 7/9] Update index.html title --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 5f502ced..af9a49a0 100755 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - HW: Curves and Instancing + BioCrowds: Mohamed Soudy