Exemplo n.º 1
0
bool EdgeCollapser::collapse_edge_introduces_volume_change( size_t source_vertex,
        size_t edge_index,
        const Vec3d& vertex_new_position )
{
    //
    // If any incident triangle has a tiny area, collapse the edge without regard to volume change
    //

    const std::vector<size_t>& inc_tris = m_surf.m_mesh.m_edge_to_triangle_map[edge_index];

    for ( size_t i = 0; i < inc_tris.size(); ++i )
    {
        if ( m_surf.get_triangle_area( inc_tris[i] ) < m_surf.m_min_triangle_area )
        {
            return false;
        }
    }

    //
    // Check volume change
    //

    const std::vector< size_t >& triangles_incident_to_vertex = m_surf.m_mesh.m_vertex_to_triangle_map[source_vertex];
    double volume_change = 0;

    for ( size_t i = 0; i < triangles_incident_to_vertex.size(); ++i )
    {
        const Vec3st& inc_tri = m_surf.m_mesh.get_triangle( triangles_incident_to_vertex[i] );
        volume_change += signed_volume( vertex_new_position, m_surf.get_position(inc_tri[0]), m_surf.get_position(inc_tri[1]), m_surf.get_position(inc_tri[2]) );
    }

    if ( fabs(volume_change) > m_surf.m_max_volume_change )
    {
        if ( m_surf.m_verbose ) {
            std::cout << "collapse edge introduces volume change"  << std::endl;
        }
        return true;
    }

    return false;

}
Exemplo n.º 2
0
bool EdgeFlipper::flip_edge( size_t edge, 
                            size_t tri0, 
                            size_t tri1, 
                            size_t third_vertex_0, 
                            size_t third_vertex_1 )
{     
    NonDestructiveTriMesh& m_mesh = m_surf.m_mesh;
    const std::vector<Vec3d>& xs = m_surf.get_positions();
    
    Vec2st& edge_vertices = m_mesh.m_edges[edge];
    
    // Find the vertices which will form the new edge
    Vec2st new_edge( third_vertex_0, third_vertex_1);
    
    // --------------
    
    // Control volume change
    double vol = fabs( signed_volume( xs[edge_vertices[0]], 
                                     xs[edge_vertices[1]], 
                                     xs[new_edge[0]], 
                                     xs[new_edge[1]] ) ); 
    
    if ( vol > m_surf.m_max_volume_change )
    {
        if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: volume change = " << vol << std::endl; }
        return false;
    }
    
    // --------------
    
    // Prevent non-manifold surfaces if we're not allowing them
    if ( false == m_surf.m_allow_non_manifold )
    {
        for ( size_t i = 0; i < m_mesh.m_vertex_to_edge_map[ third_vertex_0 ].size(); ++i )
        {
            if ( ( m_mesh.m_edges[ m_mesh.m_vertex_to_edge_map[third_vertex_0][i] ][0] == third_vertex_1 ) ||
                ( m_mesh.m_edges[ m_mesh.m_vertex_to_edge_map[third_vertex_0][i] ][1] == third_vertex_1 ) )
            {
                // edge already exists
                if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: edge exists" << std::endl;             }            
                return false;
            }
        }
    }
    
    // --------------
    
    // Don't flip edge on a degenerate tet
    if ( third_vertex_0 == third_vertex_1 )
    {
        if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: degenerate tet" << std::endl; }
        return false;
    }
    
    // --------------
    
    // Create the new triangles
    // new edge winding order == winding order of old triangle0 == winding order of new triangle0
    
    size_t new_triangle_third_vertex_0, new_triangle_third_vertex_1;
    if ( m_mesh.oriented( m_mesh.m_edges[edge][0], m_mesh.m_edges[edge][1], m_mesh.get_triangle(tri0) ) ) 
    {
		assert( m_mesh.oriented( m_mesh.m_edges[edge][1], m_mesh.m_edges[edge][0], m_mesh.get_triangle(tri1) ) );
        new_triangle_third_vertex_0 = m_mesh.m_edges[edge][1];
        new_triangle_third_vertex_1 = m_mesh.m_edges[edge][0];
    }
    else
    {
		assert( m_mesh.oriented( m_mesh.m_edges[edge][0], m_mesh.m_edges[edge][1], m_mesh.get_triangle(tri1) ) );
		assert( m_mesh.oriented( m_mesh.m_edges[edge][1], m_mesh.m_edges[edge][0], m_mesh.get_triangle(tri0) ) );
        new_triangle_third_vertex_0 = m_mesh.m_edges[edge][0];
        new_triangle_third_vertex_1 = m_mesh.m_edges[edge][1];
    }
    
    Vec3st new_triangle0( new_edge[0], new_edge[1], new_triangle_third_vertex_0 );
    Vec3st new_triangle1( new_edge[1], new_edge[0], new_triangle_third_vertex_1 );
    
    if ( m_surf.m_verbose )
    {
        std::cout << "flip --- new triangle 0: " << new_triangle0 << std::endl;
        std::cout << "flip --- new triangle 1: " << new_triangle1 << std::endl;
    }
    
    // --------------
    
    // if both triangle normals agree before flipping, make sure they agree after flipping
    if ( dot( m_surf.get_triangle_normal(tri0), m_surf.get_triangle_normal(tri1) ) > 0.0 ) 
    {
        if ( dot( m_surf.get_triangle_normal(new_triangle0), m_surf.get_triangle_normal(new_triangle1) ) < 0.0 )
        {
            if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: normal inversion" << std::endl; }
            return false;
        }
        
        if ( dot( m_surf.get_triangle_normal(new_triangle0), m_surf.get_triangle_normal(tri0) ) < 0.0 )
        {
            if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: normal inversion" << std::endl; }
            return false;
        }
        
        if ( dot( m_surf.get_triangle_normal(new_triangle1), m_surf.get_triangle_normal(tri1) ) < 0.0 )
        {
            if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: normal inversion" << std::endl; }
            return false;
        }
        
        if ( dot( m_surf.get_triangle_normal(new_triangle0), m_surf.get_triangle_normal(tri1) ) < 0.0 )
        {
            if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: normal inversion" << std::endl; }
            return false;
        }
        
        if ( dot( m_surf.get_triangle_normal(new_triangle1), m_surf.get_triangle_normal(tri0) ) < 0.0 )
        {
            if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: normal inversion" << std::endl; }
            return false;
        }
    }
    
    // --------------
    
    // Prevent intersection
    if ( m_surf.m_collision_safety && flip_introduces_collision( edge, new_edge, new_triangle0, new_triangle1 ) )
    {
        if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: intersection" << std::endl; }
        return false;
    }
    
    // --------------
    
    // Prevent degenerate triangles
    if ( triangle_area( xs[new_triangle0[0]], xs[new_triangle0[1]], xs[new_triangle0[2]] ) < m_surf.m_min_triangle_area )
    {
        if ( m_surf.m_verbose ) { std::cout << "edge flip rejected: area too small" << std::endl;    }
        return false;
    }
    
    if ( triangle_area( xs[new_triangle1[0]], xs[new_triangle1[1]], xs[new_triangle1[2]] ) < m_surf.m_min_triangle_area )
    {
        if ( m_surf.m_verbose ) {std::cout << "edge flip rejected: area too small" << std::endl; }
        return false;
    }
    
    
    // --------------
    
    // Control change in area
    
    double old_area = m_surf.get_triangle_area( tri0 ) + m_surf.get_triangle_area( tri1 );
    double new_area = triangle_area( xs[new_triangle0[0]], xs[new_triangle0[1]], xs[new_triangle0[2]] ) 
    + triangle_area( xs[new_triangle1[0]], xs[new_triangle1[1]], xs[new_triangle1[2]] );
    
    if ( fabs( old_area - new_area ) > 0.1 * old_area )
    {
        if ( m_surf.m_verbose ) {std::cout << "edge flip rejected: area change too great" << std::endl; }
        return false;
    }
    
    // --------------
    
    // Don't flip unless both vertices are on a smooth patch
    if ( ( m_surf.classify_vertex( edge_vertices[0] ) > 1 ) || ( m_surf.classify_vertex( edge_vertices[1] ) > 1 ) )
    {
        if ( m_surf.m_verbose ) {std::cout << "edge flip rejected: vertices not on smooth patch" << std::endl;  }
        return false;
    }        
    
    
    // --------------
    
    // Don't introduce a large or small angle
    
    double min_angle = min_triangle_angle( xs[new_triangle0[0]], xs[new_triangle0[1]], xs[new_triangle0[2]] );
    min_angle = min( min_angle, min_triangle_angle( xs[new_triangle1[0]], xs[new_triangle1[1]], xs[new_triangle1[2]] ) );
    
    if ( rad2deg(min_angle) < m_surf.m_min_triangle_angle )
    {
        return false;
    }
    
    double max_angle = max_triangle_angle( xs[new_triangle0[0]], xs[new_triangle0[1]], xs[new_triangle0[2]] );
    max_angle = max( max_angle, max_triangle_angle( xs[new_triangle1[0]], xs[new_triangle1[1]], xs[new_triangle1[2]] ) );
    
    if ( rad2deg(max_angle) > m_surf.m_max_triangle_angle )
    {
        return false;
    }
    
    // --------------
    
    PreEdgeFlipInfo pre_info( edge, tri0, tri1 );

    for ( size_t i = 0; i < m_observers.size(); ++i )
    {
        if ( false == m_observers[i]->operationOK(m_surf, pre_info) )
        {
            return false;
        }
    }
    
    // --------------
    
    // Okay, now do the actual operation
       
    Vec3st old_tri0 = m_mesh.get_triangle(tri0);
    Vec3st old_tri1 = m_mesh.get_triangle(tri1);
    
    m_surf.remove_triangle( tri0 );
    m_surf.remove_triangle( tri1 );
    
    size_t new_triangle_index_0 = m_surf.add_triangle( new_triangle0 );
    size_t new_triangle_index_1 = m_surf.add_triangle( new_triangle1 );
        
    if ( m_surf.m_collision_safety )
    {
        if ( m_surf.m_collision_pipeline.check_triangle_vs_all_triangles_for_intersection( new_triangle_index_0 ) )
        {
            std::cout << "missed an intersection.  New triangles: " << new_triangle0 << ", " << new_triangle1 << std::endl;
            std::cout << "old triangles: " << old_tri0 << ", " << old_tri1 << std::endl;
            assert(0);
        }
        
        if ( m_surf.m_collision_pipeline.check_triangle_vs_all_triangles_for_intersection( new_triangle_index_1 ) )
        {
            std::cout << "missed an intersection.  New triangles: " << new_triangle0 << ", " << new_triangle1 << std::endl;
            std::cout << "old triangles: " << old_tri0 << ", " << old_tri1 << std::endl;      
            assert(0);
        }
    }
    
    m_surf.m_dirty_triangles.push_back( new_triangle_index_0 );
    m_surf.m_dirty_triangles.push_back( new_triangle_index_1 );   
    
    if ( m_surf.m_verbose ) { std::cout << "edge flip: ok" << std::endl; }
    
    PostEdgeFlipInfo post_info( pre_info, new_triangle_index_0, new_triangle_index_1 );
    
    for ( size_t i = 0; i < m_observers.size(); ++i )
    {
        m_observers[i]->operationOccurred(m_surf, post_info);
    }

    
    return true;
    
}
Exemplo n.º 3
0
double tetrahedron_signed_volume(const Vec3d &x0, const Vec3d &x1, const Vec3d &x2, const Vec3d &x3)
{
   return signed_volume( x0, x1, x2, x3 );
}
Exemplo n.º 4
0
// All roots returned in interval [0,1]. Assumed geometry followed a linear
// trajectory between x and xnew. 
void getCoplanarityTimes( const Vec3& x0, const Vec3& x1, const Vec3& x2, const Vec3& x3,
        const Vec3& xnew0, const Vec3& xnew1, const Vec3& xnew2, const Vec3& xnew3,
        double* times, double* errors, unsigned &num_times )
{
    const double tol = 1e-8;
    num_times = 0;

    // cubic coefficients, A*t^3+B*t^2+C*t+D (for t in [0,1])
    const Vec3 x03 = x0 - x3;
    const Vec3 x13 = x1 - x3;
    const Vec3 x23 = x2 - x3;
    const Vec3 v03 = ( xnew0 - xnew3 ) - x03;
    const Vec3 v13 = ( xnew1 - xnew3 ) - x13;
    const Vec3 v23 = ( xnew2 - xnew3 ) - x23;

    double A = triple( v03, v13, v23 );
    double B = triple( x03, v13, v23 ) + triple( v03, x13, v23 ) + triple( v03, v13, x23 );
    double C = triple( x03, x13, v23 ) + triple( x03, v13, x23 ) + triple( v03, x13, x23 );
    double D = triple( x03, x13, x23 );

    const double convergence_tol = tol
            * ( std::fabs( A ) + std::fabs( B ) + std::fabs( C ) + std::fabs( D ) );

    // find intervals to check, or just solve it if it reduces to a quadratic =============================
    double interval_times[4];
    unsigned interval_times_size = 0;

    double discriminant = B * B - 3 * A * C; // of derivative of cubic, 3*A*t^2+2*B*t+C, divided by 4 for convenience
    if ( discriminant <= 0 )
    { // monotone cubic: only one root in [0,1] possible
      // so we just
        interval_times[0] = 0;
        interval_times[1] = 1;
        interval_times_size = 2;
    }
    else
    { // positive discriminant, B!=0
        if ( A == 0 )
        { // the cubic is just a quadratic, B*t^2+C*t+D ========================================
            discriminant = C * C - 4 * B * D; // of the quadratic
            if ( discriminant <= 0 )
            {
                double t = -C / ( 2 * B );
                if ( t >= -tol && t <= 1 + tol )
                {
                    t = clamp( t, 0., 1. );
                    double val = std::fabs(
                            signed_volume( ( 1 - t ) * x0 + t * xnew0, ( 1 - t ) * x1 + t * xnew1,
                                    ( 1 - t ) * x2 + t * xnew2, ( 1 - t ) * x3 + t * xnew3 ) );
                    if ( val < convergence_tol )
                    {
                        times[num_times++] = t;
                    }
                }
            }
            else
            { // two separate real roots
                double t0, t1;
                if ( C > 0 )
                    t0 = ( -C - std::sqrt( discriminant ) ) / ( 2 * B );
                else
                    t0 = ( -C + std::sqrt( discriminant ) ) / ( 2 * B );
                t1 = D / ( B * t0 );
                if ( t1 < t0 )
                    std::swap( t0, t1 );
                if ( t0 >= -tol && t0 <= 1 + tol )
                {
                    times[num_times++] = clamp( t0, 0., 1. );
                }
                if ( t1 >= -tol && t1 <= 1 + tol )
                {
                    addUnique( times, num_times, clamp( t1, 0., 1. ) );
                }
            }

            if ( errors )
            {
                for ( unsigned i = 0; i < num_times; ++i )
                {
                    double ti = times[i];
                    double val = std::fabs(
                            signed_volume( ( 1 - ti ) * x0 + ti * xnew0,
                                    ( 1 - ti ) * x1 + ti * xnew1, ( 1 - ti ) * x2 + ti * xnew2,
                                    ( 1 - ti ) * x3 + ti * xnew3 ) );
                    errors[i] = val;
                }
            }

            return;
        }
        else
        { // cubic is not monotone: divide up [0,1] accordingly =====================================
            double t0, t1;
            if ( B > 0 )
                t0 = ( -B - std::sqrt( discriminant ) ) / ( 3 * A );
            else
                t0 = ( -B + std::sqrt( discriminant ) ) / ( 3 * A );
            t1 = C / ( 3 * A * t0 );
            if ( t1 < t0 )
                std::swap( t0, t1 );

            interval_times[interval_times_size++] = 0;
            if ( t0 > 0 && t0 < 1 )
                interval_times[interval_times_size++] = t0;
            if ( t1 > 0 && t1 < 1 )
                interval_times[interval_times_size++] = t1;

            interval_times[interval_times_size++] = 1;
        }
    }

    // look for roots in indicated intervals ==============================================================
    // evaluate coplanarity more accurately at each endpoint of the intervals
    double interval_values[interval_times_size];
    for ( unsigned int i = 0; i < interval_times_size; ++i )
    {
        double t = interval_times[i];
        interval_values[i] = signed_volume( ( 1 - t ) * x0 + t * xnew0, ( 1 - t ) * x1 + t * xnew1,
                ( 1 - t ) * x2 + t * xnew2, ( 1 - t ) * x3 + t * xnew3 );
    }
    // first look for interval endpoints that are close enough to zero, without a sign change
    for ( unsigned int i = 0; i < interval_times_size; ++i )
    {
        if ( interval_values[i] == 0 )
        {
            times[num_times++] = interval_times[i];
        }
        else if ( std::fabs( interval_values[i] ) < convergence_tol )
        {
            if ( ( i == 0 || ( interval_values[i - 1] >= 0 && interval_values[i] >= 0 )
                    || ( interval_values[i - 1] <= 0 && interval_values[i] <= 0 ) )
                    && ( i == interval_times_size - 1
                            || ( interval_values[i + 1] >= 0 && interval_values[i] >= 0 )
                            || ( interval_values[i + 1] <= 0 && interval_values[i] <= 0 ) ) )
            {
                times[num_times++] = interval_times[i];
            }
        }
    }
    // and then search in intervals with a sign change
    for ( unsigned int i = 1; i < interval_times_size; ++i )
    {
        double tlo = interval_times[i - 1], thi = interval_times[i], tmid;
        double vlo = interval_values[i - 1], vhi = interval_values[i], vmid;
        if ( ( vlo < 0 && vhi > 0 ) || ( vlo > 0 && vhi < 0 ) )
        {
            // start off with secant approximation (in case the cubic is actually linear)
            double alpha = vhi / ( vhi - vlo );
            tmid = alpha * tlo + ( 1 - alpha ) * thi;
            for ( int iteration = 0; iteration < 50; ++iteration )
            {
                vmid = signed_volume( ( 1 - tmid ) * x0 + tmid * xnew0,
                        ( 1 - tmid ) * x1 + tmid * xnew1, ( 1 - tmid ) * x2 + tmid * xnew2,
                        ( 1 - tmid ) * x3 + tmid * xnew3 );
                if ( std::fabs( vmid ) < 1e-2 * convergence_tol )
                    break;
                if ( ( vlo < 0 && vmid > 0 ) || ( vlo > 0 && vmid < 0 ) )
                { // if sign change between lo and mid
                    thi = tmid;
                    vhi = vmid;
                }
                else
                { // otherwise sign change between hi and mid
                    tlo = tmid;
                    vlo = vmid;
                }
                if ( iteration % 2 )
                    alpha = 0.5; // sometimes go with bisection to guarantee we make progress
                else
                    alpha = vhi / ( vhi - vlo ); // other times go with secant to hopefully get there fast
                tmid = alpha * tlo + ( 1 - alpha ) * thi;
            }
            times[num_times++] = tmid;
        }
    }
    sort( times, num_times );

    if ( errors )
    {
        for ( unsigned i = 0; i < num_times; ++i )
        {
            double ti = times[i];
            double val = std::fabs(
                    signed_volume( ( 1 - ti ) * x0 + ti * xnew0, ( 1 - ti ) * x1 + ti * xnew1,
                            ( 1 - ti ) * x2 + ti * xnew2, ( 1 - ti ) * x3 + ti * xnew3 ) );
            errors[i] = val;
        }
    }
}
Exemplo n.º 5
0
void getIntersectionPoint( const Vec3& x_edge_0, const Vec3& x_edge_1, const Vec3& x_face_0,
        const Vec3& x_face_1, const Vec3& x_face_2, double* times, double* errors,
        unsigned &num_times )
{
    const double tol = 1e-12;
    num_times = 0;

    Vec3 x03 = x_edge_0 - x_face_2;
    Vec3 x13 = x_face_0 - x_face_2;
    Vec3 x23 = x_face_1 - x_face_2;
    Vec3 v03 = x_edge_1 - x_face_2 - x03;

    double C = triple( v03, x13, x23 );
    double D = triple( x03, x13, x23 );

    const double convergence_tol = tol
            * ( std::fabs( 0 ) + std::fabs( 0 ) + std::fabs( C ) + std::fabs( D ) );

    // find intervals to check, or just solve it if it reduces to a quadratic =============================
    const unsigned interval_times_size = 2;
    double interval_times[interval_times_size] = { 0., 1. };

    // look for roots in indicated intervals ==============================================================
    // evaluate coplanarity more accurately at each endpoint of the intervals
    double interval_values[interval_times_size];

    for ( unsigned int i = 0; i < interval_times_size; ++i )
    {
        double t = interval_times[i];
        interval_values[i] = signed_volume( ( 1 - t ) * x_edge_0 + t * x_edge_1, x_face_0, x_face_1,
                x_face_2 );
    }
    // first look for interval endpoints that are close enough to zero, without a sign change
    for ( unsigned int i = 0; i < interval_times_size; ++i )
    {
        if ( interval_values[i] == 0 )
        {
            times[num_times++] = interval_times[i];
        }
        else if ( std::fabs( interval_values[i] ) < convergence_tol )
        {
            if ( ( i == 0 || ( interval_values[i - 1] >= 0 && interval_values[i] >= 0 )
                    || ( interval_values[i - 1] <= 0 && interval_values[i] <= 0 ) )
                    && ( i == interval_times_size - 1
                            || ( interval_values[i + 1] >= 0 && interval_values[i] >= 0 )
                            || ( interval_values[i + 1] <= 0 && interval_values[i] <= 0 ) ) )
            {
                times[num_times++] = interval_times[i];
            }
        }
    }
    // and then search in intervals with a sign change
    for ( unsigned int i = 1; i < interval_times_size; ++i )
    {
        double tlo = interval_times[i - 1], thi = interval_times[i], tmid;
        double vlo = interval_values[i - 1], vhi = interval_values[i], vmid;
        if ( ( vlo < 0 && vhi > 0 ) || ( vlo > 0 && vhi < 0 ) )
        {
            // start off with secant approximation (in case the cubic is actually linear)
            double alpha = vhi / ( vhi - vlo );
            tmid = alpha * tlo + ( 1 - alpha ) * thi;
            for ( int iteration = 0; iteration < 50; ++iteration )
            {
                vmid = signed_volume( ( 1 - tmid ) * x_edge_0 + tmid * x_edge_1,
                        ( 1 - tmid ) * x_face_0 + tmid * x_face_0,
                        ( 1 - tmid ) * x_face_1 + tmid * x_face_1,
                        ( 1 - tmid ) * x_face_2 + tmid * x_face_2 );
                if ( std::fabs( vmid ) < 1e-2 * convergence_tol )
                    break;
                if ( ( vlo < 0 && vmid > 0 ) || ( vlo > 0 && vmid < 0 ) )
                { // if sign change between lo and mid
                    thi = tmid;
                    vhi = vmid;
                }
                else
                { // otherwise sign change between hi and mid
                    tlo = tmid;
                    vlo = vmid;
                }
                if ( iteration % 2 )
                    alpha = 0.5; // sometimes go with bisection to guarantee we make progress
                else
                    alpha = vhi / ( vhi - vlo ); // other times go with secant to hopefully get there fast
                tmid = alpha * tlo + ( 1 - alpha ) * thi;
            }
            times[num_times++] = tmid;
        }
    }
    sort( times, num_times );

    if ( errors )
    {
        for ( unsigned i = 0; i < num_times; ++i )
        {
            double ti = times[i];
            double val = std::fabs(
                    signed_volume( ( 1 - ti ) * x_edge_0 + ti * x_edge_1, x_face_0, x_face_1,
                            x_face_2 ) );
            errors[i] = val;
        }
    }
}
Exemplo n.º 6
0
  //! Calculate the signed volume for an atom.  If the atom has a valence of 3
  //! the coordinates of an attached hydrogen are calculated
  //! Puts attached Hydrogen last at the moment, like mol V3000 format.
  //! If ReZero=false (the default is true) always make pseudo z coords and leave them in mol
  double CalcSignedVolume(OBMol &mol,OBAtom *atm, bool ReZeroZ)
  {
    vector3 tmp_crd;
    vector<unsigned int> nbr_atms;
    vector<vector3> nbr_crds;
    bool use_central_atom = false,is2D=false;
    //   double hbrad = etab.CorrectedBondRad(1,0);
           
    if (!ReZeroZ || !mol.Has3D()) //give pseudo Z coords if mol is 2D
      {
        vector3 v,vz(0.0,0.0,1.0);
        is2D = true;
        OBAtom *nbr;
        OBBond *bond;
        vector<OBBond*>::iterator i;
        for (bond = atm->BeginBond(i);bond;bond = atm->NextBond(i))
          {
            nbr = bond->GetEndAtom();
            if (nbr != atm)
              {
                v = nbr->GetVector();
                if (bond->IsWedge())
                  v += vz;
                else
                  if (bond->IsHash())
                    v -= vz;

                nbr->SetVector(v);
              }
            else
              {
                nbr = bond->GetBeginAtom();
                v = nbr->GetVector();
                if (bond->IsWedge())
                  v -= vz;
                else
                  if (bond->IsHash())
                    v += vz;

                nbr->SetVector(v);
              }
          }
      }
    
    if (atm->GetHvyValence() < 3)
      {
        stringstream errorMsg;
        errorMsg << "Cannot calculate a signed volume for an atom with a heavy atom valence of " << atm->GetHvyValence() << endl;
        obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obInfo);
        return(0.0);
      }

    // Create a vector with the coordinates of the neighbor atoms
    // Also make a vector with Atom IDs
    OBAtom *nbr;
    vector<OBBond*>::iterator bint;
    for (nbr = atm->BeginNbrAtom(bint);nbr;nbr = atm->NextNbrAtom(bint))
      {
        nbr_atms.push_back(nbr->GetIdx());
      }
    // sort the neighbor atoms to insure a consistent ordering
    sort(nbr_atms.begin(),nbr_atms.end());
    for (unsigned int i = 0; i < nbr_atms.size(); ++i)
      {
        OBAtom *tmp_atm = mol.GetAtom(nbr_atms[i]);
        nbr_crds.push_back(tmp_atm->GetVector());
      }
    /*
    // If we have three heavy atoms we need to calculate the position of the fourth
    if (atm->GetHvyValence() == 3)
    {
    double bondlen = hbrad+etab.CorrectedBondRad(atm->GetAtomicNum(),atm->GetHyb());
    atm->GetNewBondVector(tmp_crd,bondlen);
    nbr_crds.push_back(tmp_crd);
    }
    */
    for(unsigned int j=0;j < nbr_crds.size();++j) // Checks for a neighbour having 0 co-ords (added hydrogen etc)
      {
        // are the coordinates zero to 6 or more significant figures
        if (nbr_crds[j].IsApprox(VZero, 1.0e-6) && use_central_atom==false)
          use_central_atom=true;
        else if (nbr_crds[j].IsApprox(VZero, 1.0e-6))
          {
            obErrorLog.ThrowError(__FUNCTION__, "More than 2 neighbours have 0 co-ords when attempting 3D chiral calculation", obInfo);
          }
      }

    // If we have three heavy atoms we can use the chiral center atom itself for the fourth
    // will always give same sign (for tetrahedron), magnitude will be smaller.
    if(nbr_atms.size()==3 || use_central_atom==true)
      {
        nbr_crds.push_back(atm->GetVector());
        nbr_atms.push_back(mol.NumAtoms()+1); // meed to add largest number on end to work
      }
    OBChiralData* cd=(OBChiralData*)atm->GetData(OBGenericDataType::ChiralData); //Set the output atom4refs to the ones used
    if(cd==NULL)
      {
        cd = new OBChiralData;
        cd->SetOrigin(perceived);
        atm->SetData(cd);
      }
    cd->SetAtom4Refs(nbr_atms,calcvolume);
    
    //re-zero psuedo-coords
    if (is2D && ReZeroZ)
      {
        vector3 v;
        OBAtom *atom;
        vector<OBAtom*>::iterator k;
        for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k))
          {
            v = atom->GetVector();
            v.SetZ(0.0);
            atom->SetVector(v);
          }
      }
    
    return(signed_volume(nbr_crds[0],nbr_crds[1],nbr_crds[2],nbr_crds[3]));
  }