Commit a253f32e authored by Vladimir Ulman's avatar Vladimir Ulman
Browse files

Merge branch 'meltedAuxBranchesForDistributed-MPI-basics' -- one branch/path again!

parents 71178601 dd6e5a13
......@@ -16,6 +16,7 @@ libs/libzmq
libs/DEPLOY
# Various build folders #
VIZU
BUILD*
bin*
......@@ -29,3 +30,7 @@ src/git_hash.h
# VIM editor #
.*.swp
# CLION
.idea
cmake-build-*
......@@ -2,8 +2,9 @@
#
# Cross platform CMake configure script for 'EmbryoGen' simulator
#
# authors: Vladimir Ulman
# authors: Vladimir Ulman, Pavel Moravec
# Max Planck Institute of Cell Biology and Genetics (c) 2018
# IT4Innovations National Supercomputing Center, VSB-TUO (c) 2020
#
#------------------------------------------------------------------------
......@@ -16,21 +17,51 @@ set(CMAKE_VERBOSE_MAKEFILE ON CACHE STRING "Tracing the compilation process" FOR
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wconversion")
option(DEBUG_VERSION "Shall I display debug information?" OFF)
#------------------------------------------------
# ALL OPTIONS ARE SUMMARIZED HERE (CONTROL PANEL)
#------------------------------------------------
option(DEBUG_VERSION "Shall I display debug information?" OFF)
option(DEBUG_DIST_VERSION "Shall I display debug information for distributed communication?" OFF)
option(FEATURE_ENABLEPHOTOBLEACHING "Shall texture particles be loosing their intensity in the phantom image?" OFF)
option(FEATURE_USEFILOGENSYNTHOSCOPY "Shall synthoscopy code from FiloGen be used instead of the one from MitoGen?" ON)
option(FEATURE_RUNDISTRIBUTED "Shall the simulation use MPI (message passing interface) and run distributed?" OFF)
option(FEATURE_USEOpenMP "Use OpenMP for single node parallelization" OFF)
option(SEARCH_ALL_DEPLIBS "Shall all dependency libraries be explicitly linked? Useful mainly for static builds." OFF)
option(SEARCH_FOR_STATIC "Shall CMAKE look for static variants of the dependency libraries?" OFF)
#-------------------------------------------------------
# TRANSLATE FEATURE REQUESTS INTO PREPROCESSOR CONSTANTS
#-------------------------------------------------------
if (DEBUG_VERSION)
add_definitions(-DDEBUG)
set(CMAKE_BUILD_TYPE "DEBUG" CACHE STATIC "" FORCE)
else (DEBUG_VERSION)
set(CMAKE_BUILD_TYPE "RELEASE" CACHE STATIC "" FORCE)
add_definitions(-DDEBUG)
endif (DEBUG_VERSION)
if (DEBUG_DIST_VERSION)
add_definitions(-DDISTRIBUTED_DEBUG)
endif (DEBUG_DIST_VERSION)
if (FEATURE_ENABLEPHOTOBLEACHING)
add_definitions(-DENABLED_PHOTOBLEACHING)
endif (FEATURE_ENABLEPHOTOBLEACHING)
if (FEATURE_USEFILOGENSYNTHOSCOPY)
add_definitions(-DENABLE_FILOGEN_PHASEIIandIII)
else (FEATURE_USEFILOGENSYNTHOSCOPY)
add_definitions(-DENABLE_MITOGEN_FINALPREVIEW)
endif (FEATURE_USEFILOGENSYNTHOSCOPY)
if (FEATURE_RUNDISTRIBUTED)
add_definitions(-DDISTRIBUTED)
endif (FEATURE_RUNDISTRIBUTED)
#-------------------------
# USE STATIC LIBS OR NOT?
#-------------------------
option(SEARCH_ALL_DEPLIBS "Shall all dependency libraries be explicitly linked? Useful mainly for static builds." OFF)
option(SEARCH_FOR_STATIC "Shall CMAKE look for static variants of the dependency libraries?" OFF)
if (SEARCH_FOR_STATIC)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(CMAKE_LIBRARY_PATH "/reset/path/to/myLibs/" CACHE PATH "optional additional search path where static libs might be gathered")
......@@ -47,12 +78,31 @@ else (SEARCH_FOR_STATIC)
endif (APPLE)
endif (SEARCH_FOR_STATIC)
#-------------------------
# TARGET COMPILATION SETUP
#-------------------------
if (DEBUG_VERSION)
set(CMAKE_BUILD_TYPE "DEBUG" CACHE STATIC "" FORCE)
else (DEBUG_VERSION)
set(CMAKE_BUILD_TYPE "RELEASE" CACHE STATIC "" FORCE)
endif (DEBUG_VERSION)
if (FEATURE_RUNDISTRIBUTED)
find_package(MPI REQUIRED)
message(STATUS "===============================================================================")
message(STATUS "==> MPI Run: ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS} ${MPIEXEC_PREFLAGS} EXECUTABLE ${MPIEXEC_POSTFLAGS} ARGS")
message(STATUS "===============================================================================")
set(CMAKE_CXX_COMPILER ${MPI_CXX_COMPILER})
endif (FEATURE_RUNDISTRIBUTED)
#-----------------------------------
# TARGET COMPILATION - SOURCE FILES
#-----------------------------------
set(SOURCES
src/util/Vector3d.cpp
src/util/strings.cpp
src/util/rnd_generators.cpp
src/util/flowfields.cpp
src/util/report.cpp
......@@ -64,13 +114,39 @@ set(SOURCES
src/util/synthoscopy/SNR.cpp
src/DisplayUnits/SceneryDisplayUnit.cpp
src/DisplayUnits/SceneryBufferedDisplayUnit.cpp
src/Geometries/Geometry.cpp
src/Geometries/Spheres.cpp
src/Geometries/ScalarImg.cpp
src/Geometries/VectorImg.cpp
src/Geometries/Mesh.cpp
src/Agents/util/CellCycle.cpp
src/Agents/util/Texture.cpp
src/Simulation.cpp
src/Agents/util/TextureFunctions.cpp
src/Agents/NucleusAgent.cpp
src/Agents/Nucleus4SAgent.cpp
src/Agents/NucleusNSAgent.cpp
src/Agents/ShapeHinter.cpp
src/Agents/TrajectoriesHinter.cpp
src/Scenarios/common/Scenario.cpp
src/TrackRecord.cpp
src/Communication/DistributedCommunicator.cpp
src/Director.cpp
src/FrontOfficer.cpp
src/main.cpp)
if (FEATURE_RUNDISTRIBUTED)
set(D_FO_SOURCES
src/Communication/DirectorMPI.cpp
src/Communication/FrontOfficerMPI.cpp)
else (FEATURE_RUNDISTRIBUTED)
set(D_FO_SOURCES
src/Communication/DirectorSMP.cpp
src/Communication/FrontOfficerSMP.cpp)
endif (FEATURE_RUNDISTRIBUTED)
file(GLOB SCENARIOSOURCES src/Scenarios/*.cpp)
add_executable(embryogen ${SOURCES} ${SCENARIOSOURCES})
add_executable(embryogen ${SOURCES} ${D_FO_SOURCES} ${SCENARIOSOURCES})
add_custom_target(commitHash ALL "../imprintGitHash.sh")
#--------------------------------------------
......@@ -87,7 +163,6 @@ if (UNIX AND CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Xlinker -defsym -Xlinker MAIN__=main")
endif (UNIX AND CMAKE_COMPILER_IS_GNUCXX)
# GSL library
find_path(INC_GSL "gsl/gsl_randist.h")
find_library(LIB_GSL gsl)
......@@ -108,6 +183,7 @@ if(NOT ZeroMQ_FOUND)
list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config)
find_package(ZeroMQ REQUIRED)
endif()
set(LIBS ${LIBS} libzmq-static)
# cppzmq header only (that C++-ish wraps around the original libzmq)
find_path(INC_CPPZMQ "zmq.hpp" DOC "EmbryoGen, being a C++ project, needs cppzmq (zmq.hpp) installed around libzmq.")
......@@ -119,6 +195,10 @@ find_library(LIB_DAISwp13 DAIS-wp13.a)
include_directories(${INC_DAISwp13})
set(LIBS ${LIBS} ${LIB_DAISwp13})
if (FEATURE_RUNDISTRIBUTED)
set(LIBS ${LIBS} MPI::MPI_CXX)
endif (FEATURE_RUNDISTRIBUTED)
#----------------------------------------------
# TARGET LINKING - FURTHER-LEVEL REQUIRED LIBS
#----------------------------------------------
......@@ -143,12 +223,31 @@ if (SEARCH_ALL_DEPLIBS)
find_library(LIB_FFTW_DOUBLE fftw3)
find_library(LIB_FFTW_LDOUBLE_THREADS fftw3l_threads)
find_library(LIB_FFTW_LDOUBLE fftw3l)
set(LIBS ${LIBS} ${LIB_FFTW_SINGLE_THREADS} ${LIB_FFTW_SINGLE}
${LIB_FFTW_DOUBLE_THREADS} ${LIB_FFTW_DOUBLE}
${LIB_FFTW_LDOUBLE_THREADS} ${LIB_FFTW_LDOUBLE})
set(LIBS ${LIBS} ${LIB_FFTW_SINGLE_THREADS}
${LIB_FFTW_SINGLE}
${LIB_FFTW_DOUBLE_THREADS}
${LIB_FFTW_DOUBLE}
${LIB_FFTW_LDOUBLE_THREADS}
${LIB_FFTW_LDOUBLE})
endif (SEARCH_ALL_DEPLIBS)
#----------------
# TARGET LINKING
#----------------
target_link_libraries(embryogen ${LIBS} libzmq-static)
if(FEATURE_USEOpenMP)
if (APPLE)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(OpenMP_CXX "${CMAKE_CXX_COMPILER}")
set(OpenMP_CXX_FLAGS "-fopenmp=libomp -Wno-unused-command-line-argument")
set(OpenMP_CXX_LIB_NAMES "libomp")
find_library(OpenMP_libomp_LIBRARY omp)
set(LIBS ${LIBS} OpenMP_libomp_LIBRARY)
endif()
endif (APPLE)
find_package(OpenMP)
set(LIBS ${LIBS} OpenMP::OpenMP_CXX)
endif(FEATURE_USEOpenMP)
target_link_libraries(embryogen ${LIBS})
Workflow w.r.t. agent types that is represent as variable-length strings:
We will utilize a Dictionary, a map: int agentTypeID -> string agentTypeStr.
The motivation for this to exist is two-fold:
a) broadcastAABB is broadcasting also an agent type (a string),
that would consume way too much bandwidth (memory)
b) variable-length strings are complicated for MPI,
reduce transfers of strings as much as possible
So we gonna use fixed-size agentTypeID to represent underlying agentTypeStr.
When it is not possible to use the agentTypeID, we will imprint the agentTypeStr
into 256 Bytes long buffer (extending with zeros and trimming to this length).
The imprint is again an attempt to solve the technical problem of MPI, and
the buffer length is artificially and purposely set to be rather large.
This way, we had eliminated the variability of length of the agentTypeStr
when it comes down to MPI communication, yet we avoid (inefficient) transferring
of the fixed-length string whenever we can (by using associated agentTypeID instead).
To represent strings, we gonna use a hashedString class that is essentially the
std::string baked together with its hash of size_t. The class makes sure that the
string and its hash are kept synced. The purpose of the hash is to complexity-wise
improve the comparison of strings (faster to compare two hashes than two strings).
This however assumes that hash is "compressed" and unique representation of the string,
much like ID! Hence, we gonna use this: agentTypeID = hash( agentTypeStr ).
ShadowAgents, however for convenience, will always use their agentTypeStr and
not agentTypeID -- the latter is purely a technical glitch with no semantics or
relevance for the simulation as such (besides it does not bring any new information
because it is a hash of the string). This limits the necessity to deal with
agentTypeID only during the AABB and ShadowAgent work/transfers...
The Dictionary can only grow, no remove or replace operations will be supported
nor allowed (exception is the cleanUp event, see below).
FOs will have their Dictionaries synchronized.
Since agentTypeIDs are hashes of their agentTypeStrs, there is no centralized
distribution of IDs as anyone can compute ID on its own, and two FOs shall arrive
to the same ID for the same strings. One only has to notify everyone else whenever
new string, that was not previously seen in the Dictionary, is added.
The changes/additions in the Dictionary can happen anytime, the Dictionary
is updated immediately (and it does not matter if two FOs add the same string
because it will render exactly the same item/pair in their Dictionary-ies,
thus no conflict or inconsistency occurs). Until updateAndPublishAgents() takes
place, other FOs need not know about these changes/updates. Therefore,
the broadcast of the newly added Dictionary items can happen as late as during
the updateAndPublishAgents(). To utilize the MPI philosophy, each message that
broadcasts AABB may include a count field as part of it, and that field signals
how many Dictionary items will be broadcast next. The plan is that this extra
field will be zero except for the last AABB broadcast message (from a particular
FO) that would store the actual count (which may be zero again). The broadcast
listeners would know what to listen for next.
The cleanUp event:
Actually, if all FOs have, after every updateAndPublishAgents(), have their
lists of AABBs synchronized, we can actually use their lists to figure out
what items from their Dictionary-ies are no longer in use, and could be deleted
- this happens separately by each FO, yet synchronized. Direktor, is currently
not maintaining its own Dictionary.
What queries into the Dictionary can we ever get from the simulation:
translateIdToString() -- when nearby AABBs are found, an agent might be curious to
see who is actually "behind this AABB", whom it is representing
-- it is agent-to-FO (local) communication only,
assumes that FO is always up to date!
-- it is thus important that FOs have own copies and that
this communication is purely local
-- essentially, reads from the Dictionary
[ When submitting AABB, the hash from inside the hashedString is used;
instead of computing it with some hypothetical getIdOfString() ]
registerThisString() -- when new agent is created, or purpose/semantics of existing agent
is changed, its agentTypeStr changes and this method must be used
-- is called often when starting the simulation
-- is called rather rarely during the simulation
-- agent->FO and (not always) FO->Direktor communication,
does nothing if FO already recognizes the input agentTypeStr
-- essentially, (possibly) adds to the Dictionary
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
find_package(PkgConfig)
pkg_check_modules(PC_LIBZMQ QUIET libzmq)
set(ZeroMQ_VERSION ${PC_LIBZMQ_VERSION})
find_library(ZeroMQ_LIBRARY NAMES libzmq.so libzmq.dylib libzmq.dll
PATHS ${PC_LIBZMQ_LIBDIR} ${PC_LIBZMQ_LIBRARY_DIRS})
find_library(ZeroMQ_STATIC_LIBRARY NAMES libzmq-static.a libzmq.a libzmq.dll.a
PATHS ${PC_LIBZMQ_LIBDIR} ${PC_LIBZMQ_LIBRARY_DIRS})
if(ZeroMQ_LIBRARY OR ZeroMQ_STATIC_LIBRARY)
set(ZeroMQ_FOUND ON)
endif()
if (TARGET libzmq)
# avoid errors defining targets twice
return()
endif()
add_library(libzmq SHARED IMPORTED)
set_property(TARGET libzmq PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PC_LIBZMQ_INCLUDE_DIRS})
set_property(TARGET libzmq PROPERTY IMPORTED_LOCATION ${ZeroMQ_LIBRARY})
add_library(libzmq-static STATIC IMPORTED ${PC_LIBZMQ_INCLUDE_DIRS})
set_property(TARGET libzmq-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PC_LIBZMQ_INCLUDE_DIRS})
set_property(TARGET libzmq-static PROPERTY IMPORTED_LOCATION ${ZeroMQ_STATIC_LIBRARY})
#ifndef ABSTRACTAGENT_H
#define ABSTRACTAGENT_H
#ifndef AGENTS_ABSTRACTAGENT_H
#define AGENTS_ABSTRACTAGENT_H
#include <i3d/image3d.h>
#include "../util/report.h"
#include "../DisplayUnits/DisplayUnit.h"
#include "../Geometries/Geometry.h"
class Simulation;
#include "../FrontOfficer.h"
#include "../util/strings.h"
/**
* This class is essentially only a read-only representation of
......@@ -19,12 +21,13 @@ class Simulation;
*/
class ShadowAgent
{
protected:
public:
/** Construct the object (which is an agent shape and position representation)
by giving it a concrete implementation of Geometry, e.g. Mesh or Spheres object.
The reference to this object is kept and used, i.e. no new object is created. */
ShadowAgent(Geometry& geom, const std::string& type)
: geometry(geom), agentType(type) {};
ShadowAgent(Geometry& geom, const int id, const std::string& type)
: geometry(geom), ID(id), agentType(type) {};
protected:
/** The geometry of an agent that is exposed to the world.
It might be a light-weight version of the agent's exact geometry.
......@@ -32,6 +35,11 @@ protected:
distances between agents. See also the discussion AbstractAgent::drawMask(). */
Geometry& geometry;
public:
/** label of this agent */
const int ID;
protected:
/** The type designation of this agent (that is represented with this->geometry).
Simulation agents may decide to "pay attention to"/smell/interact with only
certain types of agents and this attribute is a way to identify/distinguish
......@@ -42,7 +50,7 @@ protected:
it might want to be registered in the system for a while (to simulate its dissolution)
while (by changing its designation) to "communicate" other agents its new "state" and
to allow them act accordingly). */
std::string agentType;
hashedString agentType;
public:
/** returns read-only reference to the agent's (axis aligned) bounding box */
......@@ -51,17 +59,42 @@ public:
return geometry.AABB;
}
/** constructs an extra object with agent's (axis aligned) named bounding box
and returns pointer on it, caller MUST delete this object eventually */
NamedAxisAlignedBoundingBox* createNamedAABB(void) const
{
return new NamedAxisAlignedBoundingBox(geometry.AABB,ID,agentType.getHash());
}
/** returns read-only reference to the agent's geometry */
const Geometry& getGeometry(void) const
{
return geometry;
}
/** returns agent's ID */
int getID(void) const
{
return ID;
}
/** returns read-only reference on agent's designation */
const std::string& getAgentType(void) const
const hashedString& getAgentType_hashedString(void) const
{
return agentType;
}
/** returns read-only reference on agent's designation */
const std::string& getAgentType(void) const
{
return agentType.getString();
}
/** returns ID of agent's designation */
size_t getAgentTypeID(void) const
{
return agentType.getHash();
}
};
......@@ -99,9 +132,8 @@ protected:
AbstractAgent(const int _ID, const std::string& _type,
Geometry& geometryContainer,
const float _currTime, const float _incrTime)
: ShadowAgent(geometryContainer,_type),
currTime(_currTime), incrTime(_incrTime),
ID(_ID) {};
: ShadowAgent(geometryContainer,_ID,_type),
currTime(_currTime), incrTime(_incrTime) {};
public:
/** Please, override in inherited classes (see docs of AbstractAgent). */
......@@ -111,7 +143,7 @@ public:
// ------------- interaction from the Simulation class -------------
/** (re)sets the officer to which this agent belongs to, this is also
a communaction handler back to the Simulation class */
void setOfficer(Simulation* _officer)
void setOfficer(FrontOfficer* _officer)
{
if (_officer == NULL)
throw ERROR_REPORT("got NULL reference on my associated Officer.");
......@@ -134,7 +166,7 @@ public:
}
protected:
Simulation* Officer = NULL;
FrontOfficer* Officer = NULL;
bool detailedDrawingMode = false;
bool detailedReportingMode = false;
......@@ -240,9 +272,6 @@ public:
// ------------- rendering -------------
/** label of this agent */
const int ID;
/** Should render the current detailed shape, i.e. the futureGeometry, into
the DisplayUnit; may use this->ID or its state somehow for colors.
......
#include "Nucleus4SAgent.h"
void Nucleus4SAgent::getCurrentOffVectorsForCentres(Vector3d<G_FLOAT> offs[4])
{
//the centre point
Vector3d<G_FLOAT> refCentre(futureGeometry.centres[1]);
refCentre += futureGeometry.centres[2];
refCentre *= 0.5;
//the axis/orientation between 2nd and 3rd sphere
Vector3d<G_FLOAT> refAxis(futureGeometry.centres[2]);
refAxis -= futureGeometry.centres[1];
//make it half-of-the-expected-distance long
refAxis *= 0.5f*centreDistance[1] / refAxis.len();
//calculate how much are the 2nd and 3rd spheres off their expected positions
offs[1] = refCentre;
offs[1] -= refAxis; //the expected position
offs[1] -= futureGeometry.centres[1]; //the difference vector
offs[2] = refCentre;
offs[2] += refAxis;
offs[2] -= futureGeometry.centres[2];
//calculate how much is the 1st sphere off its expected position
offs[0] = refCentre;
offs[0] -= (centreDistance[0]/(0.5f*centreDistance[1]) +1.0f) * refAxis;
offs[0] -= futureGeometry.centres[0];
//calculate how much is the 4th sphere off its expected position
offs[3] = refCentre;
offs[3] += (centreDistance[2]/(0.5f*centreDistance[1]) +1.0f) * refAxis;
offs[3] -= futureGeometry.centres[3];
}
void Nucleus4SAgent::advanceAndBuildIntForces(const float futureGlobalTime)
{
//check bending of the spheres (how much their position deviates from a line,
//includes also checking the distance), and add, if necessary, another
//forces to the list
Vector3d<G_FLOAT> sOff[4];
getCurrentOffVectorsForCentres(sOff);
//tolerated mis-position (no "adjustment" s2s forces are created within this radius)
const G_FLOAT keepCalmDistanceSq = (G_FLOAT)0.01; // 0.01 = 0.1^2
if (sOff[0].len2() > keepCalmDistanceSq)
{
//properly scaled force acting on the 1st sphere: body_scale * len()
sOff[0] *= fstrength_body_scale;
forces.emplace_back( sOff[0], futureGeometry.centres[0],0, ftype_s2s );
sOff[0] *= -1.0;
forces.emplace_back( sOff[0], futureGeometry.centres[1],1, ftype_s2s );
}
if (sOff[1].len2() > keepCalmDistanceSq)
{
//properly scaled force acting on the 2nd sphere: body_scale * len()
sOff[1] *= fstrength_body_scale;
forces.emplace_back( sOff[1], futureGeometry.centres[1],1, ftype_s2s );
sOff[1] *= -0.5;
forces.emplace_back( sOff[1], futureGeometry.centres[0],0, ftype_s2s );
forces.emplace_back( sOff[1], futureGeometry.centres[2],2, ftype_s2s );
}
if (sOff[2].len2() > keepCalmDistanceSq)
{
//properly scaled force acting on the 2nd sphere: body_scale * len()
sOff[2] *= fstrength_body_scale;
forces.emplace_back( sOff[2], futureGeometry.centres[2],2, ftype_s2s );
sOff[2] *= -0.5;
forces.emplace_back( sOff[2], futureGeometry.centres[1],1, ftype_s2s );
forces.emplace_back( sOff[2], futureGeometry.centres[3],3, ftype_s2s );
}
if (sOff[3].len2() > keepCalmDistanceSq)
{
//properly scaled force acting on the 1st sphere: body_scale * len()
sOff[3] *= fstrength_body_scale;
forces.emplace_back( sOff[3], futureGeometry.centres[3],3, ftype_s2s );
sOff[3] *= -1.0;
forces.emplace_back( sOff[3], futureGeometry.centres[2],2, ftype_s2s );
}
NucleusAgent::advanceAndBuildIntForces(futureGlobalTime);
}
void Nucleus4SAgent::drawForDebug(DisplayUnit& du)
{
//first the general NucleusAgent agent debug stuff...
NucleusAgent::drawForDebug(du);
//second the 4S-special additions...
//(but also render only if under inspection)
if (detailedDrawingMode)
{
int dID = DisplayUnit::firstIdForAgentDebugObjects(ID);
dID += 32767; //we will be using IDs from the upper end of the reserved interval
//shape deviations:
//blue lines to show deviations from the expected geometry
Vector3d<G_FLOAT> sOff[4];
getCurrentOffVectorsForCentres(sOff);
du.DrawLine(dID++, futureGeometry.centres[0],futureGeometry.centres[0]+sOff[0], 3);
du.DrawLine(dID++, futureGeometry.centres[1],futureGeometry.centres[1]+sOff[1], 3);
du.DrawLine(dID++, futureGeometry.centres[2],futureGeometry.centres[2]+sOff[2], 3);
du.DrawLine(dID++, futureGeometry.centres[3],futureGeometry.centres[3]+sOff[3], 3);
}
}
#ifndef NUCLEUS4SAGENT_H
#define NUCLEUS4SAGENT_H
#ifndef AGENTS_NUCLEUS4SAGENT_H
#define AGENTS_NUCLEUS4SAGENT_H
#include "NucleusAgent.h"
......@@ -29,122 +29,20 @@ protected:
should maintain during geometry changes during the simulation */
float centreDistance[3];
void getCurrentOffVectorsForCentres(Vector3d<FLOAT> offs[4])
{
//the centre point
Vector3d<FLOAT> refCentre(futureGeometry.centres[1]);
refCentre += futureGeometry.centres[2];