]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/entity/curve.h
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / entity / curve.h
index c22a51aa899cb043602b3405c82cddfda6049057..43dc9b3bbc874c72d88850862f84468a55f0c15f 100644 (file)
 #include "render.h"
 #include "stringio.h"
 
-class RenderableCurve : public OpenGLRenderable
-{
+class RenderableCurve : public OpenGLRenderable {
 public:
-std::vector<PointVertex> m_vertices;
-void render( RenderStateFlags state ) const {
-       pointvertex_gl_array( &m_vertices.front() );
-       glDrawArrays( GL_LINE_STRIP, 0, GLsizei( m_vertices.size() ) );
-}
-};
-
-inline void plotBasisFunction( std::size_t numSegments, int point, int degree ){
-       Knots knots;
-       KnotVector_openUniform( knots, 4, degree );
-
-       globalOutputStream() << "plotBasisFunction point " << point << " of 4, knot vector:";
-       for ( Knots::iterator i = knots.begin(); i != knots.end(); ++i )
-       {
-               globalOutputStream() << " " << *i;
-       }
-       globalOutputStream() << "\n";
-       globalOutputStream() << "t=0 basis=" << BSpline_basis( knots, point, degree, 0.0 ) << "\n";
-       for ( std::size_t i = 1; i < numSegments; ++i )
-       {
-               double t = ( 1.0 / double(numSegments) ) * double(i);
-               globalOutputStream() << "t=" << t << " basis=" << BSpline_basis( knots, point, degree, t ) << "\n";
-       }
-       globalOutputStream() << "t=1 basis=" << BSpline_basis( knots, point, degree, 1.0 ) << "\n";
-}
+    std::vector<PointVertex> m_vertices;
 
-inline bool ControlPoints_parse( ControlPoints& controlPoints, const char* value ){
-       StringTokeniser tokeniser( value, " " );
-
-       std::size_t size;
-       if ( !string_parse_size( tokeniser.getToken(), size ) ) {
-               return false;
-       }
-
-       if ( size < 3 ) {
-               return false;
-       }
-       controlPoints.resize( size );
-
-       if ( !string_equal( tokeniser.getToken(), "(" ) ) {
-               return false;
-       }
-       for ( ControlPoints::iterator i = controlPoints.begin(); i != controlPoints.end(); ++i )
-       {
-               if ( !string_parse_float( tokeniser.getToken(), ( *i ).x() )
-                        || !string_parse_float( tokeniser.getToken(), ( *i ).y() )
-                        || !string_parse_float( tokeniser.getToken(), ( *i ).z() ) ) {
-                       return false;
-               }
-       }
-       if ( !string_equal( tokeniser.getToken(), ")" ) ) {
-               return false;
-       }
-       return true;
-}
+    void render(RenderStateFlags state) const
+    {
+        pointvertex_gl_array(&m_vertices.front());
+        glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_vertices.size()));
+    }
+};
 
-inline void ControlPoints_write( const ControlPoints& controlPoints, StringOutputStream& value ){
-       value << Unsigned( controlPoints.size() ) << " (";
-       for ( ControlPoints::const_iterator i = controlPoints.begin(); i != controlPoints.end(); ++i )
-       {
-               value << " " << ( *i ).x() << " " << ( *i ).y() << " " << ( *i ).z() << " ";
-       }
-       value << ")";
+inline void plotBasisFunction(std::size_t numSegments, int point, int degree)
+{
+    Knots knots;
+    KnotVector_openUniform(knots, 4, degree);
+
+    globalOutputStream() << "plotBasisFunction point " << point << " of 4, knot vector:";
+    for (Knots::iterator i = knots.begin(); i != knots.end(); ++i) {
+        globalOutputStream() << " " << *i;
+    }
+    globalOutputStream() << "\n";
+    globalOutputStream() << "t=0 basis=" << BSpline_basis(knots, point, degree, 0.0) << "\n";
+    for (std::size_t i = 1; i < numSegments; ++i) {
+        double t = (1.0 / double(numSegments)) * double(i);
+        globalOutputStream() << "t=" << t << " basis=" << BSpline_basis(knots, point, degree, t) << "\n";
+    }
+    globalOutputStream() << "t=1 basis=" << BSpline_basis(knots, point, degree, 1.0) << "\n";
+}
+
+inline bool ControlPoints_parse(ControlPoints &controlPoints, const char *value)
+{
+    StringTokeniser tokeniser(value, " ");
+
+    std::size_t size;
+    if (!string_parse_size(tokeniser.getToken(), size)) {
+        return false;
+    }
+
+    if (size < 3) {
+        return false;
+    }
+    controlPoints.resize(size);
+
+    if (!string_equal(tokeniser.getToken(), "(")) {
+        return false;
+    }
+    for (ControlPoints::iterator i = controlPoints.begin(); i != controlPoints.end(); ++i) {
+        if (!string_parse_float(tokeniser.getToken(), (*i).x())
+            || !string_parse_float(tokeniser.getToken(), (*i).y())
+            || !string_parse_float(tokeniser.getToken(), (*i).z())) {
+            return false;
+        }
+    }
+    if (!string_equal(tokeniser.getToken(), ")")) {
+        return false;
+    }
+    return true;
+}
+
+inline void ControlPoints_write(const ControlPoints &controlPoints, StringOutputStream &value)
+{
+    value << Unsigned(controlPoints.size()) << " (";
+    for (ControlPoints::const_iterator i = controlPoints.begin(); i != controlPoints.end(); ++i) {
+        value << " " << (*i).x() << " " << (*i).y() << " " << (*i).z() << " ";
+    }
+    value << ")";
 }
 
-inline void ControlPoint_testSelect( const Vector3& point, ObservedSelectable& selectable, Selector& selector, SelectionTest& test ){
-       SelectionIntersection best;
-       test.TestPoint( point, best );
-       if ( best.valid() ) {
-               Selector_add( selector, selectable, best );
-       }
+inline void
+ControlPoint_testSelect(const Vector3 &point, ObservedSelectable &selectable, Selector &selector, SelectionTest &test)
+{
+    SelectionIntersection best;
+    test.TestPoint(point, best);
+    if (best.valid()) {
+        Selector_add(selector, selectable, best);
+    }
 }
 
-class CurveEditType
-{
+class CurveEditType {
 public:
-Shader* m_controlsShader;
-Shader* m_selectedShader;
+    Shader *m_controlsShader;
+    Shader *m_selectedShader;
 };
 
-inline void ControlPoints_write( ControlPoints& controlPoints, const char* key, Entity& entity ){
-       StringOutputStream value( 256 );
-       if ( !controlPoints.empty() ) {
-               ControlPoints_write( controlPoints, value );
-       }
-       entity.setKeyValue( key, value.c_str() );
-}
-
-class CurveEdit
+inline void ControlPoints_write(ControlPoints &controlPoints, const char *key, Entity &entity)
 {
-SelectionChangeCallback m_selectionChanged;
-ControlPoints& m_controlPoints;
-typedef Array<ObservedSelectable> Selectables;
-Selectables m_selectables;
-
-RenderablePointVector m_controlsRender;
-mutable RenderablePointVector m_selectedRender;
-
-public:
-typedef Static<CurveEditType> Type;
-
-CurveEdit( ControlPoints& controlPoints, const SelectionChangeCallback& selectionChanged ) :
-       m_selectionChanged( selectionChanged ),
-       m_controlPoints( controlPoints ),
-       m_controlsRender( GL_POINTS ),
-       m_selectedRender( GL_POINTS ){
+    StringOutputStream value(256);
+    if (!controlPoints.empty()) {
+        ControlPoints_write(controlPoints, value);
+    }
+    entity.setKeyValue(key, value.c_str());
 }
 
-template<typename Functor>
-const Functor& forEachSelected( const Functor& functor ){
-       ASSERT_MESSAGE( m_controlPoints.size() == m_selectables.size(), "curve instance mismatch" );
-       ControlPoints::iterator p = m_controlPoints.begin();
-       for ( Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p )
-       {
-               if ( ( *i ).isSelected() ) {
-                       functor( *p );
-               }
-       }
-       return functor;
-}
-template<typename Functor>
-const Functor& forEachSelected( const Functor& functor ) const {
-       ASSERT_MESSAGE( m_controlPoints.size() == m_selectables.size(), "curve instance mismatch" );
-       ControlPoints::const_iterator p = m_controlPoints.begin();
-       for ( Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p )
-       {
-               if ( ( *i ).isSelected() ) {
-                       functor( *p );
-               }
-       }
-       return functor;
-}
-template<typename Functor>
-const Functor& forEach( const Functor& functor ) const {
-       for ( ControlPoints::const_iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i )
-       {
-               functor( *i );
-       }
-       return functor;
-}
-
-void testSelect( Selector& selector, SelectionTest& test ){
-       ASSERT_MESSAGE( m_controlPoints.size() == m_selectables.size(), "curve instance mismatch" );
-       ControlPoints::const_iterator p = m_controlPoints.begin();
-       for ( Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p )
-       {
-               ControlPoint_testSelect( *p, *i, selector, test );
-       }
-}
-
-bool isSelected() const {
-       for ( Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i )
-       {
-               if ( ( *i ).isSelected() ) {
-                       return true;
-               }
-       }
-       return false;
-}
-void setSelected( bool selected ){
-       for ( Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i )
-       {
-               ( *i ).setSelected( selected );
-       }
-}
-
-void write( const char* key, Entity& entity ){
-       ControlPoints_write( m_controlPoints, key, entity );
-}
+class CurveEdit {
+    SelectionChangeCallback m_selectionChanged;
+    ControlPoints &m_controlPoints;
+    typedef Array<ObservedSelectable> Selectables;
+    Selectables m_selectables;
 
-void transform( const Matrix4& matrix ){
-       forEachSelected([&](Vector3 &point) {
-               matrix4_transform_point(matrix, point);
-       });
-}
-void snapto( float snap ){
-       forEachSelected([&](Vector3 &point) {
-               vector3_snap(point, snap);
-       });
-}
+    RenderablePointVector m_controlsRender;
+    mutable RenderablePointVector m_selectedRender;
 
-void updateSelected() const {
-       m_selectedRender.clear();
-       forEachSelected([&](const Vector3 &point) {
-               m_selectedRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_selected));
-       });
-}
-
-void renderComponents( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
-       renderer.SetState( Type::instance().m_controlsShader, Renderer::eWireframeOnly );
-       renderer.SetState( Type::instance().m_controlsShader, Renderer::eFullMaterials );
-       renderer.addRenderable( m_controlsRender, localToWorld );
-}
-
-void renderComponentsSelected( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
-       updateSelected();
-       if ( !m_selectedRender.empty() ) {
-               renderer.Highlight( Renderer::ePrimitive, false );
-               renderer.SetState( Type::instance().m_selectedShader, Renderer::eWireframeOnly );
-               renderer.SetState( Type::instance().m_selectedShader, Renderer::eFullMaterials );
-               renderer.addRenderable( m_selectedRender, localToWorld );
-       }
-}
-
-void curveChanged(){
-       m_selectables.resize( m_controlPoints.size(), m_selectionChanged );
-
-       m_controlsRender.clear();
-       m_controlsRender.reserve( m_controlPoints.size() );
-       forEach([&](const Vector3 &point) {
-               m_controlsRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_vertex));
-       });
-
-       m_selectedRender.reserve( m_controlPoints.size() );
-}
-typedef MemberCaller<CurveEdit, void(), &CurveEdit::curveChanged> CurveChangedCaller;
+public:
+    typedef Static<CurveEditType> Type;
+
+    CurveEdit(ControlPoints &controlPoints, const SelectionChangeCallback &selectionChanged) :
+            m_selectionChanged(selectionChanged),
+            m_controlPoints(controlPoints),
+            m_controlsRender(GL_POINTS),
+            m_selectedRender(GL_POINTS)
+    {
+    }
+
+    template<typename Functor>
+    const Functor &forEachSelected(const Functor &functor)
+    {
+        ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
+        ControlPoints::iterator p = m_controlPoints.begin();
+        for (Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) {
+            if ((*i).isSelected()) {
+                functor(*p);
+            }
+        }
+        return functor;
+    }
+
+    template<typename Functor>
+    const Functor &forEachSelected(const Functor &functor) const
+    {
+        ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
+        ControlPoints::const_iterator p = m_controlPoints.begin();
+        for (Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) {
+            if ((*i).isSelected()) {
+                functor(*p);
+            }
+        }
+        return functor;
+    }
+
+    template<typename Functor>
+    const Functor &forEach(const Functor &functor) const
+    {
+        for (ControlPoints::const_iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i) {
+            functor(*i);
+        }
+        return functor;
+    }
+
+    void testSelect(Selector &selector, SelectionTest &test)
+    {
+        ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch");
+        ControlPoints::const_iterator p = m_controlPoints.begin();
+        for (Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) {
+            ControlPoint_testSelect(*p, *i, selector, test);
+        }
+    }
+
+    bool isSelected() const
+    {
+        for (Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i) {
+            if ((*i).isSelected()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void setSelected(bool selected)
+    {
+        for (Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i) {
+            (*i).setSelected(selected);
+        }
+    }
+
+    void write(const char *key, Entity &entity)
+    {
+        ControlPoints_write(m_controlPoints, key, entity);
+    }
+
+    void transform(const Matrix4 &matrix)
+    {
+        forEachSelected([&](Vector3 &point) {
+            matrix4_transform_point(matrix, point);
+        });
+    }
+
+    void snapto(float snap)
+    {
+        forEachSelected([&](Vector3 &point) {
+            vector3_snap(point, snap);
+        });
+    }
+
+    void updateSelected() const
+    {
+        m_selectedRender.clear();
+        forEachSelected([&](const Vector3 &point) {
+            m_selectedRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_selected));
+        });
+    }
+
+    void renderComponents(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const
+    {
+        renderer.SetState(Type::instance().m_controlsShader, Renderer::eWireframeOnly);
+        renderer.SetState(Type::instance().m_controlsShader, Renderer::eFullMaterials);
+        renderer.addRenderable(m_controlsRender, localToWorld);
+    }
+
+    void renderComponentsSelected(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const
+    {
+        updateSelected();
+        if (!m_selectedRender.empty()) {
+            renderer.Highlight(Renderer::ePrimitive, false);
+            renderer.SetState(Type::instance().m_selectedShader, Renderer::eWireframeOnly);
+            renderer.SetState(Type::instance().m_selectedShader, Renderer::eFullMaterials);
+            renderer.addRenderable(m_selectedRender, localToWorld);
+        }
+    }
+
+    void curveChanged()
+    {
+        m_selectables.resize(m_controlPoints.size(), m_selectionChanged);
+
+        m_controlsRender.clear();
+        m_controlsRender.reserve(m_controlPoints.size());
+        forEach([&](const Vector3 &point) {
+            m_controlsRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_vertex));
+        });
+
+        m_selectedRender.reserve(m_controlPoints.size());
+    }
+
+    typedef MemberCaller<CurveEdit, void(), &CurveEdit::curveChanged> CurveChangedCaller;
 };
 
 
-
 const int NURBS_degree = 3;
 
-class NURBSCurve
-{
-Signal0 m_curveChanged;
-Callback<void()> m_boundsChanged;
+class NURBSCurve {
+    Signal0 m_curveChanged;
+    Callback<void()> m_boundsChanged;
 public:
-ControlPoints m_controlPoints;
-ControlPoints m_controlPointsTransformed;
-NURBSWeights m_weights;
-Knots m_knots;
-RenderableCurve m_renderCurve;
-AABB m_bounds;
-
-NURBSCurve( const Callback<void()>& boundsChanged ) : m_boundsChanged( boundsChanged ){
-}
-
-SignalHandlerId connect( const SignalHandler& curveChanged ){
-       curveChanged();
-       return m_curveChanged.connectLast( curveChanged );
-}
-void disconnect( SignalHandlerId id ){
-       m_curveChanged.disconnect( id );
-}
-void notify(){
-       m_curveChanged();
-}
-
-void tesselate(){
-       if ( !m_controlPointsTransformed.empty() ) {
-               const std::size_t numSegments = ( m_controlPointsTransformed.size() - 1 ) * 16;
-               m_renderCurve.m_vertices.resize( numSegments + 1 );
-               m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3( m_controlPointsTransformed[0] );
-               for ( std::size_t i = 1; i < numSegments; ++i )
-               {
-                       m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3( NURBS_evaluate( m_controlPointsTransformed, m_weights, m_knots, NURBS_degree, ( 1.0 / double(numSegments) ) * double(i) ) );
-               }
-               m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3( m_controlPointsTransformed[m_controlPointsTransformed.size() - 1] );
-       }
-       else
-       {
-               m_renderCurve.m_vertices.clear();
-       }
-}
-
-void curveChanged(){
-       tesselate();
-
-       m_bounds = AABB();
-       for ( ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i )
-       {
-               aabb_extend_by_point_safe( m_bounds, ( *i ) );
-       }
-
-       m_boundsChanged();
-       notify();
-}
-
-bool parseCurve( const char* value ){
-       if ( !ControlPoints_parse( m_controlPoints, value ) ) {
-               return false;
-       }
-
-       m_weights.resize( m_controlPoints.size() );
-       for ( NURBSWeights::iterator i = m_weights.begin(); i != m_weights.end(); ++i )
-       {
-               ( *i ) = 1;
-       }
-
-       KnotVector_openUniform( m_knots, m_controlPoints.size(), NURBS_degree );
-
-       //plotBasisFunction(8, 0, NURBS_degree);
-
-       return true;
-}
-
-void curveChanged( const char* value ){
-       if ( string_empty( value ) || !parseCurve( value ) ) {
-               m_controlPoints.resize( 0 );
-               m_knots.resize( 0 );
-               m_weights.resize( 0 );
-       }
-       m_controlPointsTransformed = m_controlPoints;
-       curveChanged();
-}
-typedef MemberCaller<NURBSCurve, void(const char*), &NURBSCurve::curveChanged> CurveChangedCaller;
+    ControlPoints m_controlPoints;
+    ControlPoints m_controlPointsTransformed;
+    NURBSWeights m_weights;
+    Knots m_knots;
+    RenderableCurve m_renderCurve;
+    AABB m_bounds;
+
+    NURBSCurve(const Callback<void()> &boundsChanged) : m_boundsChanged(boundsChanged)
+    {
+    }
+
+    SignalHandlerId connect(const SignalHandler &curveChanged)
+    {
+        curveChanged();
+        return m_curveChanged.connectLast(curveChanged);
+    }
+
+    void disconnect(SignalHandlerId id)
+    {
+        m_curveChanged.disconnect(id);
+    }
+
+    void notify()
+    {
+        m_curveChanged();
+    }
+
+    void tesselate()
+    {
+        if (!m_controlPointsTransformed.empty()) {
+            const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16;
+            m_renderCurve.m_vertices.resize(numSegments + 1);
+            m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]);
+            for (std::size_t i = 1; i < numSegments; ++i) {
+                m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(
+                        NURBS_evaluate(m_controlPointsTransformed, m_weights, m_knots, NURBS_degree,
+                                       (1.0 / double(numSegments)) * double(i)));
+            }
+            m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(
+                    m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]);
+        } else {
+            m_renderCurve.m_vertices.clear();
+        }
+    }
+
+    void curveChanged()
+    {
+        tesselate();
+
+        m_bounds = AABB();
+        for (ControlPoints::iterator i = m_controlPointsTransformed.begin();
+             i != m_controlPointsTransformed.end(); ++i) {
+            aabb_extend_by_point_safe(m_bounds, (*i));
+        }
+
+        m_boundsChanged();
+        notify();
+    }
+
+    bool parseCurve(const char *value)
+    {
+        if (!ControlPoints_parse(m_controlPoints, value)) {
+            return false;
+        }
+
+        m_weights.resize(m_controlPoints.size());
+        for (NURBSWeights::iterator i = m_weights.begin(); i != m_weights.end(); ++i) {
+            (*i) = 1;
+        }
+
+        KnotVector_openUniform(m_knots, m_controlPoints.size(), NURBS_degree);
+
+        //plotBasisFunction(8, 0, NURBS_degree);
+
+        return true;
+    }
+
+    void curveChanged(const char *value)
+    {
+        if (string_empty(value) || !parseCurve(value)) {
+            m_controlPoints.resize(0);
+            m_knots.resize(0);
+            m_weights.resize(0);
+        }
+        m_controlPointsTransformed = m_controlPoints;
+        curveChanged();
+    }
+
+    typedef MemberCaller<NURBSCurve, void(const char *), &NURBSCurve::curveChanged> CurveChangedCaller;
 };
 
-class CatmullRomSpline
-{
-Signal0 m_curveChanged;
-Callback<void()> m_boundsChanged;
+class CatmullRomSpline {
+    Signal0 m_curveChanged;
+    Callback<void()> m_boundsChanged;
 public:
-ControlPoints m_controlPoints;
-ControlPoints m_controlPointsTransformed;
-RenderableCurve m_renderCurve;
-AABB m_bounds;
-
-CatmullRomSpline( const Callback<void()>& boundsChanged ) : m_boundsChanged( boundsChanged ){
-}
-
-SignalHandlerId connect( const SignalHandler& curveChanged ){
-       curveChanged();
-       return m_curveChanged.connectLast( curveChanged );
-}
-void disconnect( SignalHandlerId id ){
-       m_curveChanged.disconnect( id );
-}
-void notify(){
-       m_curveChanged();
-}
-
-void tesselate(){
-       if ( !m_controlPointsTransformed.empty() ) {
-               const std::size_t numSegments = ( m_controlPointsTransformed.size() - 1 ) * 16;
-               m_renderCurve.m_vertices.resize( numSegments + 1 );
-               m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3( m_controlPointsTransformed[0] );
-               for ( std::size_t i = 1; i < numSegments; ++i )
-               {
-                       m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3( CatmullRom_evaluate( m_controlPointsTransformed, ( 1.0 / double(numSegments) ) * double(i) ) );
-               }
-               m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3( m_controlPointsTransformed[m_controlPointsTransformed.size() - 1] );
-       }
-       else
-       {
-               m_renderCurve.m_vertices.clear();
-       }
-}
-
-bool parseCurve( const char* value ){
-       return ControlPoints_parse( m_controlPoints, value );
-}
-
-void curveChanged(){
-       tesselate();
-
-       m_bounds = AABB();
-       for ( ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i )
-       {
-               aabb_extend_by_point_safe( m_bounds, ( *i ) );
-       }
-
-       m_boundsChanged();
-       notify();
-}
-
-void curveChanged( const char* value ){
-       if ( string_empty( value ) || !parseCurve( value ) ) {
-               m_controlPoints.resize( 0 );
-       }
-       m_controlPointsTransformed = m_controlPoints;
-       curveChanged();
-}
-typedef MemberCaller<CatmullRomSpline, void(const char*), &CatmullRomSpline::curveChanged> CurveChangedCaller;
+    ControlPoints m_controlPoints;
+    ControlPoints m_controlPointsTransformed;
+    RenderableCurve m_renderCurve;
+    AABB m_bounds;
+
+    CatmullRomSpline(const Callback<void()> &boundsChanged) : m_boundsChanged(boundsChanged)
+    {
+    }
+
+    SignalHandlerId connect(const SignalHandler &curveChanged)
+    {
+        curveChanged();
+        return m_curveChanged.connectLast(curveChanged);
+    }
+
+    void disconnect(SignalHandlerId id)
+    {
+        m_curveChanged.disconnect(id);
+    }
+
+    void notify()
+    {
+        m_curveChanged();
+    }
+
+    void tesselate()
+    {
+        if (!m_controlPointsTransformed.empty()) {
+            const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16;
+            m_renderCurve.m_vertices.resize(numSegments + 1);
+            m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]);
+            for (std::size_t i = 1; i < numSegments; ++i) {
+                m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(
+                        CatmullRom_evaluate(m_controlPointsTransformed, (1.0 / double(numSegments)) * double(i)));
+            }
+            m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(
+                    m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]);
+        } else {
+            m_renderCurve.m_vertices.clear();
+        }
+    }
+
+    bool parseCurve(const char *value)
+    {
+        return ControlPoints_parse(m_controlPoints, value);
+    }
+
+    void curveChanged()
+    {
+        tesselate();
+
+        m_bounds = AABB();
+        for (ControlPoints::iterator i = m_controlPointsTransformed.begin();
+             i != m_controlPointsTransformed.end(); ++i) {
+            aabb_extend_by_point_safe(m_bounds, (*i));
+        }
+
+        m_boundsChanged();
+        notify();
+    }
+
+    void curveChanged(const char *value)
+    {
+        if (string_empty(value) || !parseCurve(value)) {
+            m_controlPoints.resize(0);
+        }
+        m_controlPointsTransformed = m_controlPoints;
+        curveChanged();
+    }
+
+    typedef MemberCaller<CatmullRomSpline, void(const char *), &CatmullRomSpline::curveChanged> CurveChangedCaller;
 };
 
-const charconst curve_Nurbs = "curve_Nurbs";
-const charconst curve_CatmullRomSpline = "curve_CatmullRomSpline";
+const char *const curve_Nurbs = "curve_Nurbs";
+const char *const curve_CatmullRomSpline = "curve_CatmullRomSpline";
 
 
 #endif