Fracture* VertPositionMutation::mutate(Fracture* fracture) { // get all verts that are not corners Array<Vertex*>* nonCorner = new Array<Vertex*>(); for(int i=0;i<fracture->getVerts()->getSize();i++) if(!fracture->getVerts()->get(i)->getIsCorner()) nonCorner->add(fracture->getVerts()->get(i)); int numVerts = nonCorner->getSize(); if(!numVerts) // if only corners return the original fracture return fracture; // get a random non-boundary vert int randVert = RNG::RandomInt(numVerts); Vertex* ranVert = nonCorner->get(randVert); // get the move amount and distance before making a jump real moveLimit = EvolutionSettings::getInstance()->getMaxMovePercent(); real distBeforeJump = EvolutionSettings::getInstance()->getDistBeforeJump(); if(ranVert->getBoundary()) { // move along boundary line Array<Edge*>* boundaryEdges = new Array<Edge*>(); for(int i=0;i<ranVert->getEdges()->getSize();i++) if(ranVert->getEdges()->get(i)->getIsBoundary()) boundaryEdges->add(ranVert->getEdges()->get(i)); // choose a random edge to move by int randDir = RNG::RandomInt(boundaryEdges->getSize()); Edge* edgeToMoveBy = boundaryEdges->get(randDir); // get the edge length real length = edgeToMoveBy->length(); // if the length is too small move the other direction // in the future this is going to check if it can jump i.e // it will jump around a corner. if(length < distBeforeJump) { for(int i=0;i<boundaryEdges->getSize();i++) { // this should always work since there should be at least // two boundary edges attached if(boundaryEdges->get(i)!=edgeToMoveBy) { edgeToMoveBy = boundaryEdges->get(i); i = boundaryEdges->getSize(); } } // recheck length and abort if the check fails real length = edgeToMoveBy->length(); if(length < distBeforeJump) return fracture; } // get a random mutation value real mutateScale = RNG::RandomFloat(moveLimit); // Get the two points on the edge to move on Point2 firstPoint = ranVert->getLocation(); Point2 secondPoint = edgeToMoveBy->getOtherPoint(ranVert->getID()); // calculate the vert's new location Point2 slope = secondPoint.minus(firstPoint); slope.scale(mutateScale); Point2 newLoc = firstPoint.add(slope); // set the new location ranVert->setLocation(newLoc); // tell the verts edges about its new location ranVert->updateEdges(); // clean up while(boundaryEdges->getSize()) boundaryEdges->removeLast(); delete boundaryEdges; } else { // maybe implement move along edges ??? // move about faces using barycentric coordinates // get the faces containing the point Array<Face*>* facesWithVert = fracture->getFacesWithVertex(ranVert); // create a face containing all points around the vert Face* faceToMutateAround = new Face(); for(int i=0;i<facesWithVert->getSize();i++) { Face* tmp = facesWithVert->get(i); for(int j=0;j<tmp->getEdges()->getSize();j++) if(!tmp->getEdges()->get(j)->eitherMatch(ranVert->getID())) faceToMutateAround->getEdges()->add(tmp->getEdges()->get(j)->copy()); for(int j=0;j<tmp->getVerts()->getSize();j++) if(tmp->getVerts()->get(j)->getID() != ranVert->getID()) faceToMutateAround->getVerts()->add(tmp->getVerts()->get(j)->copy(faceToMutateAround->getEdges())); } // create the container for the generated Tris Array<Tri*>* generatedTris = new Array<Tri*>(); // detect if the face is convex or not faceToMutateAround->detectIfConvex(); if(faceToMutateAround->getIsConvex()) { // TODO :: Do this the more efficient way // convex case mutation (easy) // generate tris for(int i=0;i<faceToMutateAround->getEdges()->getSize();i++) generatedTris->add(new Tri(faceToMutateAround->getEdges()->get(i),ranVert->getLocation(),ranVert->getID())); // create barycentric coordinates for mutation (2 * #tris) // TODO :: Combine this with the case below int numBarys = generatedTris->getSize()*2; real barys[numBarys]; for(int i=0;i<numBarys;i++) barys[i] = RNG::RandomFloat(moveLimit); // apply mutations Point2 newPos; newPos.xpos = 0.0; newPos.ypos = 0.0; int pointID = ranVert->getID(); for(int i=0;i<generatedTris->getSize();i++) { // get new position real baryOne = barys[i*2]; real baryTwo = barys[i*2-1]; real baryThree = 1.0 - baryOne - baryTwo; newPos = generatedTris->get(i)->interpolatePosition(baryOne,baryTwo,baryThree); // update the position in all the remaining tris for(int j=i;j<generatedTris->getSize();j++) generatedTris->get(j)->updatePosition(pointID,newPos); } // set new position of vert ranVert->setLocation(newPos); // update edges ranVert->updateEdges(); // clean up } else { // concave case mutation (harder) // create collection for trimesh shell Array<Vertex*>* vertsInView = new Array<Vertex*>(); Array<Vertex*>* sortedVertsInView = new Array<Vertex*>(); Array<Edge*>* trimeshShell = new Array<Edge*>(); // get all verts in view for(int i=0;i<faceToMutateAround->getVerts()->getSize();i++) { Vertex* vert = faceToMutateAround->getVerts()->get(i); // create a temp edge to check for intersections Edge* tmpEdge = new Edge(ranVert->getLocation(),vert->getLocation()); bool noIntersection = true; // check if intersections for(int i=0;i<faceToMutateAround->getEdges()->getSize();i++) if(faceToMutateAround->getEdges()->get(i)->intersects(tmpEdge)) noIntersection = false; // if no intersections add it to the list of verts in view if(noIntersection) vertsInView->add(vert); // Note :: vert is still a copy // clean up delete tmpEdge; } // sort verts Array<Integer>* sortedIDs = faceToMutateAround->sortVertIDsByPath(); for(int i=0;i<sortedIDs->getSize();i++) for(int j=0;j<vertsInView->getSize();i++) if(vertsInView->get(j)->getID() == sortedIDs->get(i).val) { sortedVertsInView->add(vertsInView->get(j)); j = vertsInView->getSize(); } // create shell from verts for(int i=0;i<sortedVertsInView->getSize();i++) { Vertex* one = sortedVertsInView->get(i); Vertex* two = i==sortedVertsInView->getSize()-1 ? sortedVertsInView->get(0) : sortedVertsInView->get(i+1); Edge* newEdge = new Edge(one->getLocation(),two->getLocation(),one->getID(),two->getID()); trimeshShell->add(newEdge); } // generate tris for(int i=0;i<trimeshShell->getSize();i++) generatedTris->add(new Tri(trimeshShell->get(i),ranVert->getLocation(),ranVert->getID())); // create barycentric coordinates for mutation (Only 2 for this case) // TODO :: Combine this with the case above //int numBarys = generatedTris->getSize()*2; int numBarys = 2; real barys[numBarys]; for(int i=0;i<numBarys;i++) barys[i] = RNG::RandomFloat(moveLimit); // apply mutations Point2 newPos; newPos.xpos = 0.0; newPos.ypos = 0.0; int pointID = ranVert->getID(); int ranTri = RNG::RandomInt(generatedTris->getSize()); //for(int i=0;i<generatedTris->getSize();i++) { // get new position //real baryOne = barys[i*2]; //real baryTwo = barys[i*2-1]; real baryOne = barys[0]; real baryTwo = barys[1]; real baryThree = 1.0 - baryOne - baryTwo; newPos = generatedTris->get(ranTri)->interpolatePosition(baryOne,baryTwo,baryThree); // update the position in all the remaining tris (skip for this step) // for(int j=i;j<generatedTris->getSize();j++) // generatedTris->get(j)->updatePosition(pointID,newPos); //} // set new position of vert ranVert->setLocation(newPos); // update edges ranVert->updateEdges(); // clean up while(vertsInView->getSize()) vertsInView->removeLast(); while(sortedIDs->getSize()) sortedIDs->removeLast(); while(sortedVertsInView->getSize()) sortedVertsInView->removeLast(); while(trimeshShell->getSize()) delete trimeshShell->removeLast(); delete vertsInView; delete trimeshShell; delete sortedVertsInView; delete sortedIDs; } // clean up while(nonCorner->getSize()) nonCorner->removeLast(); while(facesWithVert->getSize()) facesWithVert->removeLast(); while(generatedTris->getSize()) delete generatedTris->removeLast(); while(faceToMutateAround->getVerts()->getSize()) delete faceToMutateAround->getVerts()->removeLast(); while(faceToMutateAround->getEdges()->getSize()) delete faceToMutateAround->getEdges()->removeLast(); delete faceToMutateAround; delete generatedTris; delete facesWithVert; delete nonCorner; } return fracture; }