inline Vector2f VectorLocalization2D::attractorFunction(line2f l, Vector2f p, float attractorRange, float margin)
{
  static const bool debug = false;
  
  Vector2f attraction(0.0,0.0), dir(V2COMP(l.Dir())), p0(V2COMP(l.P0())), p1(V2COMP(l.P1()));
  
  float location = (p-p0).dot(dir);
  
  if(location<-margin || location>l.Length()+margin){
    return attraction;
  }
  
  attraction = (p-p0) - dir*location ;
  
  float d = attraction.norm();
  /*
  if(d>0.5*attractorRange){
    float d2 = max(0.0f, attractorRange - d);
    attraction *= 2.0f*d2/attractorRange;
  }
  */
  if(d>attractorRange){
    attraction = Vector2f::Zero();
  }
  if(debug){
    debugLines.push_back( line2f( vector2f(p.x(),p.y()) ,vector2f((p-attraction).x(),(p-attraction).y()) ) );
  }
  return attraction;
}
// 布の形状の更新
void updateCloth(void) {
    // ★ 次の手順で質点の位置を決定する
    //clothのみがグローバル変数
    // 1. 質点に働く力を求める
    // 質点のfの定義
    for(int y = 0; y < POINT_NUM; y++) {
        for(int x = 0; x < POINT_NUM; x++) {
            cloth->points[x][y].f.set(0,0,0);
        }
    }
    //バネによる力を考える
    for(int i = 0; i < cloth->springs.size(); i++) {
        Vector3d nowlength(cloth->springs[i]->p0->p - cloth->springs[i]->p1->p);
        double gap = nowlength.length() - cloth->springs[i]->restLength;
        double strpower = Ks * gap; 
        //p1-p0
        Vector3d attraction(cloth->springs[i]->p1->p.x - cloth->springs[i]->p0->p.x,
                            cloth->springs[i]->p1->p.y - cloth->springs[i]->p0->p.y,
                            cloth->springs[i]->p1->p.z - cloth->springs[i]->p0->p.z);  
        attraction.normalize();
        attraction.scale(strpower);
        cloth->springs[i]->p0->f += attraction; 
        //向きを反転
        attraction.scale(-1);
        cloth->springs[i]->p1->f += attraction;
    }
    //重力M*gを加える
    //ついでに空気抵抗を考える
    for(int y = 0; y < POINT_NUM; y++) { 
        for(int x = 0; x < POINT_NUM; x++) {
           Vector3d grav(Mass*gravity.x,Mass*gravity.y,Mass*gravity.z);
           cloth->points[x][y].f +=  grav;
           Vector3d airresister(Dk*cloth->points[x][y].v.x,Dk*cloth->points[x][y].v.y,Dk*cloth->points[x][y].v.z);
           cloth->points[x][y].f -= airresister;
        }
    }

            // 2. 質点の加速度を求める
            // 3. 質点の速度を更新する
            // 4. 質点の位置を更新する
    for(int y = 0; y < POINT_NUM; y++){
        for(int x = 0; x < POINT_NUM; x++) {
            //加速度accelを求めてdTを掛けた
            if(!(cloth->points[x][y].bFixed)){
                Vector3d accel(cloth->points[x][y].f.x/Mass*dT,cloth->points[x][y].f.y/Mass*dT,cloth->points[x][y].f.z/Mass*dT);
                //質点の速度
                cloth->points[x][y].v += accel;
                //質点の位置
                Vector3d xx(cloth->points[x][y].v.x*dT,cloth->points[x][y].v.y*dT,cloth->points[x][y].v.z*dT);
                cloth->points[x][y].p += xx;
            }
        }
    }
}
inline Vector2f VectorLocalization2D::observationFunction(line2f l, Vector2f p) const
{
  static const bool debug = false;
  Vector2f attraction(0.0,0.0), dir(V2COMP(l.Dir())), p0(V2COMP(l.P0())), p1(V2COMP(l.P1()));
  float location = (p-p0).dot(dir);
  attraction = (p-p0) - dir*location ;
  if(debug){
    debugLines.push_back( line2f( vector2f(p.x(),p.y()) ,vector2f((p-attraction).x(),(p-attraction).y()) ) );
    const float crossSize = 0.002;
    debugLines.push_back( line2f( vector2f(p.x()+crossSize,p.y()) , vector2f(p.x()-crossSize,p.y())) );
    debugLines.push_back( line2f( vector2f(p.x(),p.y()+crossSize) , vector2f(p.x(),p.y()-crossSize)) );
  }
  return attraction;
}
int 
DomainPartitioner::releaseVertex(int from, 
				 int vertexTag, 
				 Graph &theWeightedPartitionGraph,
				 bool mustReleaseToLighter,
				 double factorGreater,
				 bool adjacentVertexNotInOther)
{
  // check that the object did the partitioning
  if (partitionFlag == false) {
    opserr << "DomainPartitioner::balance(const Vector &load)";
    opserr << " - not partitioned or DomainPartitioner did not partition\n";
    return -1;
  }
  
  // we first check the vertex is on the fromBoundary
  Subdomain *fromSubdomain = myDomain->getSubdomainPtr(from);
  if (fromSubdomain == 0) {
    opserr << "DomainPartitioner::swapVertex - No from Subdomain: ";
    opserr << from << " exists\n";
    return -1;
  }

  // get the vertex from the boundary vertices of from
  // Graph &theEleGraph = myDomain->getElementGraph();    
  Graph *fromBoundary = theBoundaryElements[from-1];
    
  Vertex *vertexPtr = fromBoundary->getVertexPtr(vertexTag);
  if (vertexPtr == 0) 
    vertexPtr = theElementGraph->getVertexPtr(vertexTag);
  
  if (vertexPtr == 0)  // if still 0 no vertex given by tag exists
    return -3;

  ID attraction(numPartitions+1);
  attraction.Zero();

  // determine the attraction to the other partitions
  const ID &adjacent = vertexPtr->getAdjacency();
  int numAdjacent = adjacent.Size();
  for (int i=0; i<numAdjacent; i++) {
    int otherTag = adjacent(i);
    Vertex *otherVertex = theElementGraph->getVertexPtr(otherTag);
    int otherPartition = otherVertex->getColor();
    if (otherPartition != from)
      attraction(otherPartition) += 1;
  }
  
  // determine the other partition the vertex is most attracted to
  int partition = 1;
  int maxAttraction = attraction(1);
  for (int j=2; j<=numPartitions; j++)
    if (attraction(j) > maxAttraction) {
      partition = j;
      maxAttraction = attraction(j);
    }

  // swap the vertex
  if (mustReleaseToLighter == false)
    return swapVertex(from, partition, vertexTag, adjacentVertexNotInOther);
  
  else { // check the other partition has a lighter load
    Vertex *fromVertex = theWeightedPartitionGraph.getVertexPtr(from);
    Vertex *toVertex = theWeightedPartitionGraph.getVertexPtr(partition);	    
    
    double fromWeight = fromVertex->getWeight();
    double toWeight  = toVertex->getWeight();
    
    if (fromWeight == toWeight)
      opserr << "DomainPartitioner::releaseVertex - TO CHANGE >= to >\n";

    if (fromWeight >= toWeight) {
      if (toWeight == 0.0) 
	return swapVertex(from,partition,vertexTag,adjacentVertexNotInOther);	    
      if (fromWeight/toWeight > factorGreater)        
	return swapVertex(from,partition,vertexTag,adjacentVertexNotInOther);	    
    }
  }
  
  return 0;
}