=======Makefile====================
CC = gcc
.c.o:
${CC} -g -c $<
OBJS = main.o fuzzymom.o randutils.o
ecrv: ${OBJS}
${CC} ${OBJS} -o $@ -lm
=======Makefile====================
=======coords.h====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#ifndef COORDS_H
#define COORDS_H
typedef struct _coords
{
double x;
double y;
} COORDS;
extern COORDS MW_NodePosition[];
#define GetXPosition(id) MW_NodePosition[id].x
#define GetYPosition(id) MW_NodePosition[id].y
#define SetXPosition(id, q) MW_NodePosition[id].x = q
#define SetYPosition(id, v) MW_NodePosition[id].y = v
#endif
=======coords.h====================
=======fuzzymom.c====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#include <stdio.h>
#include <math.h>
#include "coords.h"
#include "fuzzymom.h"
#include "randutils.h"
FZGROUP MW_FzGroups[MAXFZGRP];
FZNODE MW_FzNodes[NUMNODES];
ECRVPARM MW_FzEcParms[MAXFZGRP];
long FzSeed;
extern FILE *NvOutput;
FILE *fzCannedMoves;
/* get the maximum Node Radius from the nodes */
static int FzGetMaxNodeRadius(minnode, maxnode)
int minnode;
int maxnode;
{
int i;
int maxR = 0;
for (i=minnode; i < maxnode; i++)
{
if(MW_FzNodes[i].origRadius > maxR)
maxR = MW_FzNodes[i].origRadius;
}
return(maxR);
}
/* For the end of the simulation - may want to dump out statistics here */
ECRVClose()
{
int i;
i = 0;
}
ECRVUpdateInf(tStep, groupId)
int tStep;
int groupId;
{
double xMove, yMove;
double radius;
double angle;
/* assign old values */
MW_FzGroups[groupId].prevRadius = MW_FzGroups[groupId].radius;
MW_FzGroups[groupId].prevAngle = MW_FzGroups[groupId].angle;
/* calculate new values */
MW_FzGroups[groupId].radius =
(MW_FzGroups[groupId].prevRadius * MW_FzGroups[groupId].yR) +
((MW_FzGroups[groupId].sigmaR * MW_FzGroups[groupId].speed) *
MW_FzGroups[groupId].rSqconst * GaussDev(&FzSeed));
MW_FzGroups[groupId].angle =
(MW_FzGroups[groupId].prevAngle * MW_FzGroups[groupId].yAng) +
((MW_FzGroups[groupId].sigmaAng * DegsToRads(360)) *
MW_FzGroups[groupId].angSqconst * GaussDev(&FzSeed));
/* this basically implements a bounce off the boundaries of our world */
xMove = MW_FzGroups[groupId].radius * cos(MW_FzGroups[groupId].angle);
yMove = MW_FzGroups[groupId].radius * sin(MW_FzGroups[groupId].angle);
/* calculate Group X = Xg*/
MW_FzGroups[groupId].curX += xMove;
MW_FzGroups[groupId].curY += yMove;
}
/* Bounded model - nodes have to bounce off the walls */
ECRVUpdate(tStep, groupId)
int tStep;
int groupId;
{
double xMove, yMove;
double radius;
double angle;
/* assign old values */
MW_FzGroups[groupId].prevRadius = MW_FzGroups[groupId].radius;
MW_FzGroups[groupId].prevAngle = MW_FzGroups[groupId].angle;
/* calculate new values */
MW_FzGroups[groupId].radius =
(MW_FzGroups[groupId].prevRadius * MW_FzGroups[groupId].yR) +
((MW_FzGroups[groupId].sigmaR * MW_FzGroups[groupId].speed) *
MW_FzGroups[groupId].rSqconst * GaussDev(&FzSeed));
MW_FzGroups[groupId].angle =
(MW_FzGroups[groupId].prevAngle * MW_FzGroups[groupId].yAng) +
((MW_FzGroups[groupId].sigmaAng * DegsToRads(360)) *
MW_FzGroups[groupId].angSqconst * GaussDev(&FzSeed));
/* this basically implements a bounce off the boundaries of our world */
xMove = MW_FzGroups[groupId].radius * cos(MW_FzGroups[groupId].angle);
yMove = MW_FzGroups[groupId].radius * sin(MW_FzGroups[groupId].angle);
/* calculate Group X = Xg*/
MW_FzGroups[groupId].curX += xMove;
/* if Xg + Rg >= Xmax || Xg - Rg <=0 then newangle = 180 - oldangle*/
if(MW_FzGroups[groupId].curX >= (double)(MW_FzGroups[groupId].maxX))
MW_FzGroups[groupId].angle =
(double)(DegsToRads(180) - MW_FzGroups[groupId].angle);
if(MW_FzGroups[groupId].curX <=0)
MW_FzGroups[groupId].angle =
(double)(DegsToRads(180) - MW_FzGroups[groupId].angle);
/* if Yg + Rg >= Ymax || Yg - Rg <=0 then newangle = -oldangle*/
MW_FzGroups[groupId].curY += yMove ;
if(MW_FzGroups[groupId].curY >= (double)MW_FzGroups[groupId].maxY)
MW_FzGroups[groupId].angle = (-MW_FzGroups[groupId].angle);
if(MW_FzGroups[groupId].curY <=0)
MW_FzGroups[groupId].angle = (-MW_FzGroups[groupId].angle);
}
/* Exponential Correlated Random Variable Model */
/* A node's position in time is Node(i) at n = Group Center + b at n
where b at n = Node's previous position * ecrvY +
sigma * sqrt ( 1 - pow(ecrvY, 2)) * random gaussian variable
ecrvY = exp(timestep/Tau)
timestep = 1 in this model; For values of large Tau, we get close to linear
movement for each node, for small Tau we get more random movements for
each node
There are 2 Tau and 2 Sigma's - one for the Radius and one for the Angle.
*/
ECRVMove(nodeId, tStep, x, y, nx, ny)
int nodeId;
int tStep;
double x;
double y;
double *nx;
double *ny;
{
int i, groupId, ecrvId;
groupId = MW_FzNodes[nodeId].groupId;
ecrvId = MW_FzNodes[nodeId].ecrvId;
MW_FzNodes[nodeId].prevRadius = MW_FzNodes[nodeId].curRadius;
MW_FzNodes[nodeId].prevAngle = MW_FzNodes[nodeId].curAngle;
MW_FzNodes[nodeId].curRadius =
(MW_FzNodes[nodeId].prevRadius * MW_FzEcParms[ecrvId].yR) +
((MW_FzEcParms[ecrvId].sigmaR * MW_FzNodes[nodeId].speed) *
MW_FzEcParms[ecrvId].rSqconst * GaussDev(&FzSeed));
MW_FzNodes[nodeId].curAngle =
(MW_FzNodes[nodeId].prevAngle * MW_FzEcParms[ecrvId].yAng) +
((MW_FzEcParms[ecrvId].sigmaAng * DegsToRads(360)) *
MW_FzEcParms[ecrvId].angSqconst * GaussDev(&FzSeed));
*nx = MW_FzGroups[groupId].curX + MW_FzNodes[nodeId].origConstX +
MW_FzNodes[nodeId].curRadius * cos(MW_FzNodes[nodeId].curAngle);
*ny = MW_FzGroups[groupId].curY + MW_FzNodes[nodeId].origConstY +
MW_FzNodes[nodeId].curRadius * sin(MW_FzNodes[nodeId].curAngle);
MW_NodePosition[nodeId].x = *nx;
MW_NodePosition[nodeId].y = *ny;
}
/************************************************************************/
/* ECRVInit */
/* Initialize the network */
/* Modify the global position arrays so that all nodes have a starting */
/* x and y */
/************************************************************************/
ECRVInit(totalNodes, numGroups, maxX, maxY)
{
char dum[100];
FILE *fp;
int i, j, nodesPerGrp, tauR, sigmaR, tauA, sigmaA, numTypes;
int start, end, maxR;
int x, y, sp, rad, ang, percentage;
/* read in the configuration */
/* Configuration is laid out with */
/* Group#: */
/* orgX, orgY, group speed, group radius limit, group angle in degs,*/
/* tauR, sigmaR, tauAng, sigmaAng */
FzSeed = -1;
fp = fopen("ECRVConfig", "r");
start = 0;
CFSkipComments(fp);
for (i = 0 ; i < numGroups; i++)
{
fscanf(fp,"%s %d %d %d %d %d %d %d %d %d %d\n", dum, &nodesPerGrp,
&x, &y, &sp, &rad, &ang, &tauR, &sigmaR, &tauA, &sigmaA);
MW_FzGroups[i].origX = (double)x;
MW_FzGroups[i].origY = (double)y;
MW_FzGroups[i].maxX = (double)maxX;
MW_FzGroups[i].maxY = (double)maxY;
MW_FzGroups[i].speed = (double)sp;
MW_FzGroups[i].origRadius = (double)rad;
MW_FzGroups[i].radius = (double)sp;
MW_FzGroups[i].origAngle = (double)DegsToRads(ang);
MW_FzGroups[i].angle = MW_FzGroups[i].origAngle;
MW_FzGroups[i].tauR = (double)tauR;
MW_FzGroups[i].sigmaR = ((double)sigmaR/100.0);
MW_FzGroups[i].tauAng = (double)tauA;
MW_FzGroups[i].sigmaAng = ((double)sigmaA/100.0);
MW_FzGroups[i].yR = exp(-1.0/((double)tauR));
MW_FzGroups[i].yAng = exp(-1.0/((double)tauA));
MW_FzGroups[i].rSqconst =
sqrt(1.0-(pow(MW_FzGroups[i].yR, 2.0)));
MW_FzGroups[i].angSqconst =
sqrt(1.0-(pow(MW_FzGroups[i].yAng, 2.0)));
MW_FzGroups[i].nodes = nodesPerGrp;
/* read in the ECRV stuff for the nodes */
fscanf(fp,"%s %d %d %d %d %d\n", dum,
&tauR, &sigmaR, &tauA, &sigmaA);
MW_FzEcParms[i].tauR = (double)tauR;
MW_FzEcParms[i].sigmaR = ((double)sigmaR/100.0);
MW_FzEcParms[i].tauAng = (double)tauA;
MW_FzEcParms[i].sigmaAng = ((double)sigmaA/100.0);
MW_FzEcParms[i].yR = exp(-1.0/((double)tauR));
MW_FzEcParms[i].yAng = exp(-1.0/((double)tauA));
MW_FzEcParms[i].rSqconst =
sqrt(1.0-(pow(MW_FzEcParms[i].yR, 2.0)));
MW_FzEcParms[i].angSqconst =
sqrt(1.0-(pow(MW_FzEcParms[i].yAng, 2.0)));
/* do the nodes per group */
end = start + nodesPerGrp;
for (j = start; j < end; j++)
{
fprintf(NvOutput, "node %d\tcolor %d;\n", j, i%MAXNETVIZCOLORS);
MW_FzNodes[j].groupId = i;
MW_FzNodes[j].ecrvId = i;
MW_FzNodes[j].speed = MW_FzGroups[i].speed;
MW_FzNodes[j].origRadius =
DoubleRandVal(1.0, MW_FzGroups[i].origRadius);
/* these angles are radians */
MW_FzNodes[j].origAngle =
(double)DegsToRads(DoubleRandVal(0.0,360.0));
/*ECRV init*/
MW_FzNodes[j].curRadius = MW_FzNodes[j].origRadius;
MW_FzNodes[j].curAngle = MW_FzNodes[j].origAngle;
}
maxR = FzGetMaxNodeRadius(start, end);
/* x[0] = GroupRadius + MaxRandom Radius of Node in group + center */
MW_FzGroups[i].origX += (maxR + MW_FzGroups[i].origRadius);
MW_FzGroups[i].origY += (maxR + MW_FzGroups[i].origRadius);
MW_FzGroups[i].curX = MW_FzGroups[i].origX;
MW_FzGroups[i].curY = MW_FzGroups[i].origY;
/* convert to x and y coordinates */
for (j = start; j < end; j++)
{
/* x = r*cos(theta) + maxR + X[0] */
MW_FzNodes[j].origConstX = MW_NodePosition[j].x =
MW_FzNodes[j].origRadius * cos(MW_FzNodes[j].origAngle);
MW_NodePosition[j].x += MW_FzGroups[i].origX;
/* y = r*sin(theta) + maxR + Y[0] */
MW_FzNodes[j].origConstY = MW_NodePosition[j].y =
MW_FzNodes[j].origRadius * sin(MW_FzNodes[j].origAngle);
MW_NodePosition[j].y += MW_FzGroups[i].origY;
}
start = end;
}
}
=======fuzzymom.c====================
=======fuzzymom.h====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#ifndef FZMOM_H
#define FZMOM_H
#define NUMNODES 60
#define MAXFZGRP 60
#define MAXNETVIZCOLORS 16
/* this describes the group for the fuzzy movement */
typedef struct _fzgroup
{
double origX;
double origY;
double origRadius;
double origAngle;
double speed;
double prevRadius;
double radius;
double prevAngle;
double angle;
double curX;
double curY;
double maxX;
double maxY;
double tauR; /* group parameters */
double yR;
double rSqconst;
double tauAng;
double yAng;
double angSqconst;
double sigmaR;
double sigmaAng;
int nodes;
} FZGROUP;
/* this describes the node for the fuzzy movement */
typedef struct _fznode
{
double speed;
double origRadius;
double origAngle;
double origConstX;
double origConstY;
double prevRadius;
double prevAngle;
double curRadius;
double curAngle;
int groupId;
int ecrvId;
} FZNODE;
typedef struct _ecrvParm
{
double tauR; /* node parameters for the ECRV model */
double yR;
double rSqconst;
double tauAng;
double yAng;
double angSqconst;
double sigmaR;
double sigmaAng;
int percentage; /* percentage of nodes in this group */
} ECRVPARM;
#define DegsToRads(x) x/57.296
#define RadsToDegs(x) x*57.296
extern int ECRVInit();
extern int ECRVUpdate();
extern int ECRVUpdateInf();
extern int ECRVMove();
extern int ECRVClose();
extern int MomCannedInit();
extern int MomCannedUpdate();
extern int MomCannedMove();
#endif
=======fuzzymom.h====================
=======main.c====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#include <stdio.h>
#include "fuzzymom.h"
#include "coords.h"
COORDS MW_NodePosition[NUMNODES];
FILE *NvOutput;
main(argc, argv)
int argc;
char **argv;
{
int numNodes; /* max number of nodes in the simulation */
int numGroups; /* max number of groups in the simulation */
int maxX; /* max X on grid */
int maxY; /* max Y on grid */
int maxMoves; /* max times to move */
int i, j, k;
double Nx;
double Ny;
numNodes = 9;
numGroups = 3;
maxMoves = 10;
maxX = 350;
maxY = 350;
NvOutput = fopen("try.nv", "w");
ECRVInit(numNodes, numGroups, maxX, maxY);
for(i = 0; i < maxMoves; i++)
{
/* for every move you want to make or every tick,
you want to call from this part */
fprintf(NvOutput, "time %d;\n",i);
for(j = 0; j < numGroups; j++)
{
/* this is for a bounded space */
ECRVUpdate(0, j);
/* call ECRVUpdateInf(0, j) for unbounded space */
}
for(k = 0; k < numNodes; k++)
{
ECRVMove(k, 0,
GetXPosition(k),
GetYPosition(k),
&Nx,
&Ny);
fprintf(NvOutput, "node %d\tposition %f,%f;\n",
k, GetXPosition(k), GetYPosition(k));
}
/* to this part */
}
}
/*****************************************************************/
/* CFSkipComments */
/* we want to skip comments in the canned files */
/* the canned files really need comments - otherwise we will */
/* forget what all the parameters in the file are */
/* Comments in all canned files are lines that begin with # */
/* Since this is for simulation - comments must fit on a line */
/* I don't deal with wrapped lines and comments are just at the */
/* beginning of the file */
/* Input: takes in an open file descriptor */
/* Output: void */
/* Side Effects: fp should be at the first character that is */
/* legitimate input */
/*****************************************************************/
void CFSkipComments(fp)
FILE *fp;
{
char dum[1];
if(fp == NULL)
return;
dum[0] = fgetc(fp);
while(dum[0] == '#')
{
dum[0] = fgetc(fp);
while((dum[0] != '#') && (dum[0] != '\n'))
dum[0] = fgetc(fp);
if(dum[0] == '\n')
dum[0] = fgetc(fp);
}
fseek(fp, -1, 1);
}
=======main.c====================
=======randutils.c====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#include <stdio.h>
#include <time.h>
#include <math.h>
#define IA 16807
#define IM 2147483647
#define AM (1.0/IM)
#define IQ 127773
#define IR 2836
#define NTAB 32
#define NDIV (1+(IM - 1)/NTAB)
#define EPS 1.2e-7
#define RNMX (1.0 - EPS)
/*******************************************************************
*Generates a random integer number between minval and maxval, inclusive.
*******************************************************************/
int IntRandVal(minvalue, maxvalue)
int minvalue;
int maxvalue;
{
int range, rnum;
range = maxvalue - minvalue + 1;
rnum = ((lrand48() % 100)*range)/100 ;
rnum += minvalue;
return(rnum);
}
/*******************************************************************
*Generates a random double number between minval and maxval, inclusive.
*******************************************************************/
double DoubleRandVal(minvalue, maxvalue)
double minvalue;
double maxvalue;
{
double range, rnum;
range = maxvalue - minvalue + 1;
rnum = ((lrand48() % 100)*range)/100 ;
rnum += minvalue;
return(rnum);
}
/* "Minimal" random number generator of Park and Miller with Bays-Durham
shuffle and added safeguards. Returns a uniform random deviate between
0.0 and 1.0 (exclusive of the endpoints). Call with idum as a negative
integer to initialize; thereafter, do not alter idum between successive
deivates in a sequence. RNMX should approximate the largest floating
value that is less than 1 - Taken from Numerical Recipes*/
double Rand1(idum)
long *idum;
{
int j;
long k;
static long iy = 0;
static long iv[NTAB];
float temp;
if (*idum <= 0 || !iy)
{
if (-(*idum) < 1)
*idum = 1;
else
*idum = -(*idum);
for (j = NTAB + 7; j>=0; j--)
{
k= (*idum)/IQ;
*idum = (IA*(*idum-k*IQ))-(IR*k);
if (*idum < 0)
*idum += IM;
if (j < NTAB)
iv[j] = *idum;
}
iy=iv[0];
}
k = (*idum)/IQ;
*idum = (IA*(*idum-k*IQ))-(IR*k);
if (*idum < 0)
*idum += IM;
j = iy/NDIV;
iy = iv[j];
iv[j] = *idum;
if ((temp = AM*iy) > RNMX)
return(RNMX);
else
return(temp);
}
/* returns a normally distributed deviate with zero mean and unit variance,
using ran1(idum) as the source of uniform deviates.
*/
double GaussDev(idum)
long *idum;
{
double Rand1();
static int iset = 0;
static double gset;
double fac, rsq, v1, v2;
double temp;
if(iset == 0)
{
do
{
v1 = 2.0 * Rand1(idum) - 1.0;
v2 = 2.0 * Rand1(idum) - 1.0;
rsq = v1 * v1 + v2 * v2;
}while (rsq >= 1.0 || rsq == 0);
temp = -2.0 * log(rsq)/rsq;
fac = sqrt(temp);
gset = v1 * fac;
iset = -1;
return(v2 *fac);
}
else
{
iset = 0;
return(gset);
}
}
/**********
**
** EXPON(): Returns a value with exponential distribution.
**
**********/
long expSeed = -1;
int expon(mean)
int mean;
{
extern double drand48();
extern double log();
srand48((long)time((time_t *)NULL) + GaussDev(&expSeed));
return(int)((-log((double)(1.0-drand48()))) * ((double) mean));
}
=======randutils.c====================
=======randutils.h====================
/*-------------------------------------------------------------------*
| Copyright (c) 1996 by BBN Systems and Technologies |
| |
| Permission to use, copy, modify, and distribute this |
| software and its documentation for any purpose is hereby |
| granted without fee, provided that the above copyright notice |
| and this permission appear in all copies and in supporting |
| documentation, and that the name of BBN Corporation not be |
| used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| BBN makes no representations about the suitability of this |
| software for any purposes. It is provided "AS IS" without |
| express or implied warranties. |
*-------------------------------------------------------------------*/
#ifndef RANDUTILS_H
#define RANDUTILS_H
extern double drand48();
extern double DoubleRandVal();
extern double Rand1();
extern double GaussDev();
extern int IntRandVal();
extern int expon();
#endif
=======randutils.h====================
=====ECRVConfig=======================
# Comments are lines beginning with # signs and they should be on a single line
# Comments can only be taken at the beginning of the file
# This file is the input file for the ECRV mobility model
#
# The model is described as
# b(t+1) = b(t)exp(-1/Tau) + sigma*sqrt(1 - (exp(-1/Tau))^2)*random Gauss var
# The model has groups of nodes. Each group is a circle containing nodes which
# can be described as having a certain number of nodes originally centered
# at a specific (x,y) with group speed s. The nodes can initially be spread
# out from the center with a radius r. Each group's movement is described
# as the equation above. We describe each group by moving a certain
# radius with a certain angle. We specify separate Tau and sigma variables
# for the radius and the angle of the group. From the equation, the
# smaller the Tau, the more random the movement. I use sigma to control
# what the spread is of the speed or the angle movement is. Really sigma
# is sigma * speed or sigma * 360.
#
# Inside each group the nodes can also do their own kind of movement. Each
# node gets to be described in the same way. Groups may move very linearly
# but the nodes may look like really crazy men inside the groups.
#
# 2 lines specify a group and its nodes
# Group: Num of Nodes, X, Y, Speed, Radius, Angle, TauRadius, SigmaRadius,
# TauAngle, SigmaAngle
# ECRV for Nodes in Group: TauRadius, SigmaRadius, TauAngle, SigmaAngle
#
G1: 3 0 0 5 100 0 100000 100 100000 100
E1: 1000 100 1000 100
G2: 3 50 0 5 100 0 1 100 1 100
E2: 1 100 1 100
G3: 3 50 50 0 100 0 0 0 0 0
E3: 0 0 0 0