[Racer-svn] SF.net SVN: racer:[90] trunk/libtrack
Status: Alpha
Brought to you by:
jlegg
From: <jl...@us...> - 2009-11-26 17:06:01
|
Revision: 90 http://racer.svn.sourceforge.net/racer/?rev=90&view=rev Author: jlegg Date: 2009-11-26 17:05:52 +0000 (Thu, 26 Nov 2009) Log Message: ----------- Give the Path* classes their own directory. Each class has separate files inside it. Modified Paths: -------------- trunk/libtrack/Makefile.am trunk/libtrack/PieceDistortion.cpp trunk/libtrack/Track.h trunk/libtrack/document/ChangeEdgeSegmentDelta.h trunk/libtrack/edit_base/VertexRotationHandle.cpp Added Paths: ----------- trunk/libtrack/path/ trunk/libtrack/path/Path.cpp trunk/libtrack/path/Path.h trunk/libtrack/path/PathEdge.cpp trunk/libtrack/path/PathEdge.h trunk/libtrack/path/PathEdgeEnd.cpp trunk/libtrack/path/PathEdgeEnd.h trunk/libtrack/path/PathVertex.cpp trunk/libtrack/path/PathVertex.h Removed Paths: ------------- trunk/libtrack/Path.cpp trunk/libtrack/Path.h Modified: trunk/libtrack/Makefile.am =================================================================== --- trunk/libtrack/Makefile.am 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/Makefile.am 2009-11-26 17:05:52 UTC (rev 90) @@ -1,7 +1,7 @@ -SUBDIRS = document edit_base +SUBDIRS = document edit_base path noinst_LIBRARIES = libtrack.a -libtrack_a_SOURCES = AxisAlignedBoundingBox.cpp AxisAlignedBoundingBox.h Debug.h DecoMesh.cpp DecoMesh.h DecoMeshInstance.cpp DecoMeshInstance.h Dragable.cpp Dragable.h Drawable.cpp Drawable.h DrawableMesh.cpp DrawableMesh.h MeshFaces.cpp MeshFaces.h Path.cpp Path.h PieceDistortion.cpp PieceDistortion.h SegmentConnection.cpp SegmentConnection.h Segment.cpp Segment.h Selectable.h Skybox.cpp Skybox.h stream_loader.h stream_loader.cpp Texture.cpp Texture.h Theme.cpp Theme.h Track.cpp Track.h TrackMesh.h UniqueIdentifier.h +libtrack_a_SOURCES = AxisAlignedBoundingBox.cpp AxisAlignedBoundingBox.h Debug.h DecoMesh.cpp DecoMesh.h DecoMeshInstance.cpp DecoMeshInstance.h Dragable.cpp Dragable.h Drawable.cpp Drawable.h DrawableMesh.cpp DrawableMesh.h MeshFaces.cpp MeshFaces.h PieceDistortion.cpp PieceDistortion.h SegmentConnection.cpp SegmentConnection.h Segment.cpp Segment.h Selectable.h Skybox.cpp Skybox.h stream_loader.h stream_loader.cpp Texture.cpp Texture.h Theme.cpp Theme.h Track.cpp Track.h TrackMesh.h UniqueIdentifier.h libtrack_a_CPPFLAGS = $(debug_specific_CFLAGS) $(libtrack_CFLAGS) -libtrack_a_LIBADD = document/libdocument.a edit_base/libedit_base.a +libtrack_a_LIBADD = document/libdocument.a edit_base/libedit_base.a path/libpath.a #we need a flattened archive rather than a nested one. libtrack_a_AR=$(AR) $(ARFLAGS)T Deleted: trunk/libtrack/Path.cpp =================================================================== --- trunk/libtrack/Path.cpp 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/Path.cpp 2009-11-26 17:05:52 UTC (rev 90) @@ -1,665 +0,0 @@ -/** @file libtrack/Path.cpp - * @brief Implement the Track::Path class. - * @author James Legg - */ -/* Copyright © 2009 James Legg. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -*/ - -#include <LinearMath/btMatrix3x3.h> - -#include "Path.h" -#include "stream_loader.h" - -#include "document/MoveNodeDelta.h" - -namespace Track -{ - -PathVertex::PathVertex() - : position(btVector3(0, 0, 0)) - , segment(0) - , segment_index(0) - , gradient(btVector3(1.0, 0.0, 0.0)) - , up_handle(get_name(), EditAssist::VertexRotationHandle::DIR_UP) - , forward_handle(get_name(), EditAssist::VertexRotationHandle::DIR_FORWARD) -{ - set_angle(btQuaternion::getIdentity()); - update_handles(); -} - -PathVertex::PathVertex(std::istream & source, const Theme & theme) - : up_handle(get_name(), EditAssist::VertexRotationHandle::DIR_UP) - , forward_handle(get_name(), EditAssist::VertexRotationHandle::DIR_FORWARD) -{ - source >> position >> gradient >> angle >> segment_index; - segment = &theme.get_segment(segment_index); - set_angle(angle); - update_handles(); -} - -PathVertex::~PathVertex() -{ - -} - -const btVector3 & PathVertex::get_position() const -{ - return position; -} - -void PathVertex::set_position(const btVector3 & position_in) -{ - position = position_in; - update_handles(); -} - -void PathVertex::update_handles() -{ - up_handle.set_centre(position); - up_handle.update_angle(angle); - - forward_handle.set_centre(position); - forward_handle.update_angle(angle); -} - -const btVector3 & PathVertex::get_gradient() const -{ - return gradient; -} - -const btVector3 & PathVertex::get_up() const -{ - return up; -} - -void PathVertex::set_angle(btQuaternion angle_in) -{ - angle = angle_in; - const btTransform transform(angle_in); - gradient = transform(btVector3(0, 1.0, 0.0)); - up = transform(btVector3(0, 0.0, 1.0)); - - update_handles(); -} - -const btQuaternion & PathVertex::get_angle() const -{ - return angle; -} - -bool PathVertex::is_here(btVector3 start, btVector3 stop, btScalar radius) const -{ - btVector3 line = stop - start; - btVector3 to_start = position - start; - btScalar u = to_start.dot(line) / line.length2(); - if (u < 0.0 || u > 1.0) - { - //Nearest to one of the end vertices. Not valid selection. - // Is either behind the viewer or depth clipped, either way - // not on screen so can't be selected. - return false; - } - btScalar distance2 = (u * line).distance2(to_start); - if (distance2 < radius * radius) - { - return true; - } - return false; -} - -boost::shared_ptr<Document::DocumentDelta> PathVertex::make_delta(btVector3 new_position) const -{ - return boost::shared_ptr<Document::DocumentDelta> - ( - new Document::MoveNodeDelta - ( - Document::NodePositionFinder(get_name()) - , new_position - ) - ); -} - -const EditAssist::ControlPoint * PathVertex::get_control_point_here(btVector3 start, btVector3 stop, btScalar radius) const -{ - if (up_handle.is_here(start, stop, radius)) - { - return &up_handle; - } - else if (forward_handle.is_here(start, stop, radius)) - { - return &forward_handle; - } - // There is no appropriate control point. - return 0; -} - -void PathVertex::set_handle_lengths(btScalar handle_length) -{ - up_handle.set_length(handle_length); - forward_handle.set_length(handle_length); - update_handles(); -} - -std::ostream & operator<<(std::ostream & destination, const PathVertex & path_vertex) -{ - destination << path_vertex.position << ' ' - << path_vertex.gradient << ' ' - << path_vertex.angle << ' ' - << path_vertex.segment_index; - return destination; -} - - -PathEdgeEnd::PathEdgeEnd() - : gradient_strength(10.0) - , segment_connection(0) -{ - -} - -PathEdgeEnd::PathEdgeEnd(std::istream & source, const Segment & segment) -{ - source >> gradient_strength >> segment_conection_index; - /// @todo implement segment connections - //segment_connection = &segment.get_connection(segment_conection_index); -} - -PathEdgeEnd::~PathEdgeEnd() -{ - -} - -std::ostream & operator<<(std::ostream & destination, const PathEdgeEnd & path_edge_end) -{ - destination << path_edge_end.gradient_strength << ' ' - << path_edge_end.segment_conection_index; - return destination; -} - -PathEdge::PathEdge(const Theme & theme) - : theme(theme) - , segment(0) -{ - -} - -PathEdge::PathEdge(std::istream & source, const Theme & theme) - : theme(theme) - , segment_index(int_from_stream(source)) - , segment(&theme.get_segment(segment_index)) - , start(source, *segment) - , finish(source, *segment) -{ -} - -PathEdge::~PathEdge() -{ - -} - -// calculate good sizes of steps to take in approximation of edge length. -template <int t> -struct one_over_two_to_the_power_of -{ - static const btScalar result = 0.5 * one_over_two_to_the_power_of<t - 1>::result; -}; - -template <> -struct one_over_two_to_the_power_of<0> -{ - static const btScalar result = 1.0; -}; - - -void PathEdge::update(const PathVertex & source_in, - const PathVertex & target_in) -{ - source = & source_in; - target = & target_in; - update(); -} - -void PathEdge::update() -{ - const PathVertex & source_in = *source; - const PathVertex & target_in = *target; - // A cubic bezier spline isn't analytically intergratable. - // Instead of hard numerical approximations, I'll do a piecewise linear - // estimation. - const btVector3 & p0 = source_in.get_position(); - const btVector3 p1 = p0 + source_in.get_gradient() * start.gradient_strength; - const btVector3 & p3 = target_in.get_position(); - const btVector3 p2 = p0 - target_in.get_gradient() * finish.gradient_strength; - - btVector3 last_position = p0; - - length = 0; - const btScalar step = one_over_two_to_the_power_of<5>::result; - for (btScalar t = step; t < 1.0; t += step) - { - const btScalar reverse_t = 1.0 - t; - const btScalar t_squared = t * t; - const btScalar reverse_t_squared = reverse_t * reverse_t; - const btScalar t_cubed = t_squared * t; - const btScalar reverse_t_cubed = reverse_t_squared * reverse_t; - const btVector3 this_position = reverse_t_cubed * p0 - + 3.0 * reverse_t_squared * t * p1 - + 3.0 * reverse_t * t_squared * p2 - + t_cubed * p3; - length += (this_position - last_position).length(); - last_position = this_position; - } - - // how many repetions of the segment do we need? - if (segment) - { - // round to the nearest whole number of segments. - number_of_repetions = int (length / segment->get_length() + 0.5); - } - if (number_of_repetions == 0) - { - // even short gaps between vertices must be filled. - number_of_repetions = 1; - } - if (number_of_repetions > 64) - { - DEBUG_MESSAGE("Warning: large number of meshes (" << number_of_repetions << ") needed for edge " << get_name()); - } - // make the meshes. - meshes.clear(); - meshes.reserve(number_of_repetions); - for (unsigned int i = 0; i < number_of_repetions; i++) - { - PieceDistortion transform(*this, i, - segment->get_length(), - segment->get_minimum_y()); - meshes.push_back(boost::shared_ptr<DrawableMesh>( - new DrawableMesh(segment->get_graphics_mesh().get_distorted_faces(transform)) - )); - } -} - -btScalar PathEdge::get_length() const -{ - return length; -} - -unsigned int PathEdge::get_number_of_repetions() const -{ - return number_of_repetions; -} - -btTransform PathEdge::get_transform(btScalar position) const -{ - const btScalar rposition = 1.0 - position; - const btScalar position_squared = position * position; - const btScalar rposition_squared = rposition * rposition; - const btScalar position_cubed = position_squared * position; - const btScalar rposition_cubed = rposition_squared * rposition; - const btVector3 & p0 = source->get_position(); - const btVector3 p1 = p0 + source->get_gradient() * start.gradient_strength; - const btVector3 & p3 = target->get_position(); - const btVector3 p2 = p3 - target->get_gradient() * finish.gradient_strength; - const btVector3 translation = rposition_cubed * p0 - + 3.0 * rposition_squared * position * p1 - + 3.0 * rposition * position_squared * p2 - + position_cubed * p3; - // find tangent for rotation. - const btVector3 start_tangent = p1 - p0; - const btVector3 end_tangent = p3 - p2; - /* The tangent's control points should be tripled, but this cancels since we - * immediately normalise. - */ - const btVector3 tangent = ( rposition_squared * start_tangent - + 2.0 * rposition * position * (p2 - p1) - + position_squared * end_tangent) - .normalize(); - // roatation is such that the xz plane is perpendicular to the tangent. - // rotation around the tangent is specified by an up vector. - // We get the up vector from slerp of the tangenets at the end vertices. - //const btQuaternion angle = source.get_angle().slerp(target.get_angle(), position); - - const btVector3 up = source->get_up().lerp(target->get_up(), position) - .normalize(); - // use up as tangent as x axis, up as z axis, and their cross product for y. - const btVector3 cross_axis = up.cross(tangent); - btMatrix3x3 rotation(cross_axis.x(), cross_axis.y(), cross_axis.z(), - tangent.x(), tangent.y(), tangent.z(), - up.x(), up.y(), up.z()); - return btTransform(rotation, translation); -} - -const boost::shared_ptr<DrawableMesh> PathEdge::get_graphics_mesh(std::size_t index) const -{ - assert(index < number_of_repetions); - return meshes[index]; -} - -bool PathEdge::is_here(btVector3 start, btVector3 stop, btScalar radius) const -{ - /* We should find the smallest distance between the line segment between - * start and stop and the cubic bezier spline of the edge. - * Instead, we'll find an approximation by using a piecewise linear - * approximation of the cubic bezier. - */ - btVector3 length = stop - start; - btVector3 e2, e1; - ///@todo we only need the position, not the full transformation. - e2 = get_transform(0).getOrigin(); - const btScalar step = one_over_two_to_the_power_of<4>::result; - for (btScalar t = step; t < 1.0; t += step) - { - e1 = e2; - e2 = get_transform(t).getOrigin(); - // now e1->e2 is the line segment we will test. - btVector3 ed = e2 - e1; - btVector3 w = start - e1; - btScalar a = length.dot(length); - btScalar b = length.dot(ed); - btScalar c = ed.dot(ed); - btScalar d = length.dot(w); - btScalar e = ed.dot(w); - btScalar denominator = a * c - b * b; - btScalar screen = b * e - c * d; - btScalar section = a * e - b * d; - // Now check if this is along the segments. - if (screen < 0) screen = 0; - if (screen > denominator) screen = denominator; - if (section < 0) section = 0; - if (section > denominator) section = denominator; - // find distance - btScalar distance = (w + (screen * length - section * ed) / denominator).length2(); - if (distance < radius * radius) - { - return true; - } - } - return false; -} - -std::ostream & operator<<(std::ostream & destination, const PathEdge & path_edge) -{ - destination << path_edge.segment_index - << ' ' << path_edge.start - << ' ' << path_edge.finish; - return destination; -} - - -Path::Path(std::istream & source, const Theme & theme) -// We store the graph in the stream as a vector of vertices, followed by a - // vector of edges. Start by getting the size of the vertices vector. - : graph(int_from_stream(source)) - , theme(theme) -{ - // add vertex descriptors - typedef boost::graph_traits<Graph>::vertex_iterator VertexIterator; - std::pair<VertexIterator, VertexIterator> vertex_range; - for (vertex_range = vertices(graph); - vertex_range.first != vertex_range.second; - vertex_range.first++) - { - graph[*(vertex_range.first)] = PathVertex(source, theme); - } - - // now on to the edges - std::size_t number_of_edges; - source >> number_of_edges; - for (std::size_t edge_index = 0; edge_index < number_of_edges; edge_index++) - { - // Read vertex indices - std::size_t vert_index_start, vert_index_end; - source >> vert_index_start >> vert_index_end; - if ( vert_index_start > num_vertices(graph) - || vert_index_end > num_vertices(graph)) - { - // File refers to vertices that don't exist, so it must be invalid. - throw std::istream::failure("Invalid data: bad vertex index in path's graph."); - } - - // Read edge data - PathEdge edge_data(source, theme); - - std::pair<Graph::edge_descriptor, bool> new_edge = - add_edge(vert_index_start, vert_index_end, edge_data, graph); - // update the edge's information (length, meshes, and so on). - graph[new_edge.first].update(graph[vert_index_start], graph[vert_index_end]); - } -} - -Path::Path(const Theme & theme) - : theme(theme) -{ - // add two vertices and two edges connecting them in a loop. - PathVertex vertex_1, vertex_2; - vertex_1.set_position(btVector3(300.0, 0.0, 200.0)); - vertex_2.set_position(btVector3(-300.0, 0.0, -200.0)); - vertex_1.set_angle(btQuaternion(0.0, 0.0, 0.0)); - vertex_2.set_angle(btQuaternion(0.0, 0.0, M_PI)); - vertex_1.segment = &(theme.get_segment(0)); - vertex_1.segment_index = 0; - vertex_2.segment = &(theme.get_segment(0)); - vertex_2.segment_index = 0; - Graph::vertex_descriptor - vertex_1_descriptor = boost::add_vertex(vertex_1, graph), - vertex_2_descriptor = boost::add_vertex(vertex_2, graph); - - PathEdge edge_1(theme), edge_2(theme); - edge_1.segment = &(theme.get_segment(1)); - edge_1.segment_index = 1; - edge_2.segment = &(theme.get_segment(1)); - edge_2.segment_index = 1; - edge_1.start.gradient_strength = 400.0; - edge_1.finish.gradient_strength = 400.0; - edge_2.start = edge_1.finish; - edge_2.finish = edge_1.start; - add_edge(vertex_2_descriptor, vertex_1_descriptor, edge_1, graph); - add_edge(vertex_1_descriptor, vertex_2_descriptor, edge_2, graph); - typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; - - // calculate the length of the edges. - typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator; - std::pair<EdgeIterator, EdgeIterator> edge_range(boost::edges(graph)); - // Get references to where the vertices are held. - PathVertex & vertex_1r = graph[vertex_1_descriptor]; - PathVertex & vertex_2r = graph[vertex_2_descriptor]; - graph[*(edge_range.first)].update(vertex_1r, vertex_2r); - edge_range.first++; - graph[*(edge_range.first)].update(vertex_2r, vertex_1r); -} - -Path::~Path() -{ - -} - -std::ostream & operator<<(std::ostream & destination, const Path & path) -{ - // vertices - destination << boost::num_vertices(path.graph) << ' '; - typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; - std::pair<VertexIterator, VertexIterator> vertex_range; - for (vertex_range = boost::vertices(path.graph); - vertex_range.first != vertex_range.second; - vertex_range.first++) - { - destination << path.graph[*(vertex_range.first)] << ' '; - } - - destination << boost::num_edges(path.graph) << ' '; - typedef boost::graph_traits<Path::Graph>::edge_iterator EdgeIterator; - std::pair<EdgeIterator, EdgeIterator> edge_range; - for (edge_range = boost::edges(path.graph); - edge_range.first != edge_range.second; - edge_range.first++) - { - // output the vertex descriptor of the source and target vertices, - // so the graph can be reconstructed. - destination << boost::source(*(edge_range.first), path.graph) << ' ' - << boost::target(*(edge_range.first), path.graph) << ' ' - // Now output the the PathEdge information. - << path.graph[*(edge_range.first)] << ' '; - } - - return destination; -} - -AxisAlignedBoundingBox Path::get_bounding_box() const -{ - AxisAlignedBoundingBox result; - - // find the bounding box of all the vertices. - typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; - std::pair<VertexIterator, VertexIterator> vertex_range; - for (vertex_range = boost::vertices(graph); - vertex_range.first != vertex_range.second; - vertex_range.first++) - { - result |= graph[*(vertex_range.first)].get_position(); - } - - // now expand to include control points - typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator; - std::pair<EdgeIterator, EdgeIterator> edge_range; - for (edge_range = boost::edges(graph); - edge_range.first != edge_range.second; - edge_range.first++) - { - btVector3 position = graph[source(*(edge_range.first), graph)].get_position(); - btVector3 gradient = graph[source(*(edge_range.first), graph)].get_gradient(); - btScalar magnitude = graph[*(edge_range.first)].start.gradient_strength; - result |= position + gradient * magnitude; - position = graph[target(*(edge_range.first), graph)].get_position(); - gradient = graph[target(*(edge_range.first), graph)].get_gradient(); - magnitude = graph[*(edge_range.first)].finish.gradient_strength; - result |= position - gradient * magnitude; - } - - // add some padding. - result.add_border(200.0); - - return result; -} - -PathVertex & Path::get_node(const std::size_t index) -{ - return graph[get_node_descriptor(index)]; -} - -const PathVertex & Path::get_node(const std::size_t index) const -{ - return graph[get_node_descriptor(index)]; -} - -Path::Graph::vertex_descriptor Path::get_node_descriptor(const std::size_t index) -{ - return get_node_descriptor_internal(index); -} - -const Path::Graph::vertex_descriptor Path::get_node_descriptor(const std::size_t index) const -{ - return get_node_descriptor_internal(index); -} - -Path::Graph::vertex_descriptor Path::get_node_descriptor_internal(const std::size_t index) const -{ - /// @todo Use a more efficent data structure? - typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; - std::pair<VertexIterator, VertexIterator> vertex_range; - for (vertex_range = boost::vertices(graph); - vertex_range.first != vertex_range.second; - vertex_range.first++) - { - const PathVertex & vertex = graph[*(vertex_range.first)]; - if (vertex.get_name() == index) - { - return *(vertex_range.first); - } - } - DEBUG_MESSAGE("No node has index " << index << ", aborting."); - assert(false); - throw; -} - - -PathEdge & Path::get_edge(const std::size_t index) -{ - return graph[get_edge_descriptor(index)]; -} - -const PathEdge & Path::get_edge(const std::size_t index) const -{ - return graph[get_edge_descriptor(index)]; -} - -Path::Graph::edge_descriptor Path::get_edge_descriptor(const std::size_t index) -{ - return get_edge_descriptor_internal(index); -} - -const Path::Graph::edge_descriptor Path::get_edge_descriptor(const std::size_t index) const -{ - return get_edge_descriptor_internal(index); -} - -Path::Graph::edge_descriptor Path::get_edge_descriptor_internal(const std::size_t index) const -{ - /// @todo Use a more efficent data structure? - typedef boost::graph_traits<Path::Graph>::edge_iterator EdgeIterator; - std::pair<EdgeIterator, EdgeIterator> edge_range; - for (edge_range = boost::edges(graph); - edge_range.first != edge_range.second; - edge_range.first++) - { - const PathEdge & edge = graph[*(edge_range.first)]; - if (edge.get_name() == index) - { - return *(edge_range.first); - } - } - DEBUG_MESSAGE("No edge has index " << index << ", aborting."); - assert(false); - throw; -} - -void Path::set_handle_lengths(btScalar handle_length) -{ - typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; - std::pair<VertexIterator, VertexIterator> vertex_range; - for (vertex_range = boost::vertices(graph); - vertex_range.first != vertex_range.second; - vertex_range.first++) - { - graph[*(vertex_range.first)].set_handle_lengths(handle_length); - } -} - -void Path::update_connected_edges(Path::Graph::vertex_descriptor vertex_descriptor) -{ - // edges out of the vertex - typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIterator; - std::pair<OutEdgeIterator, OutEdgeIterator> out_edge_range; - for (out_edge_range = boost::out_edges(vertex_descriptor, graph); - out_edge_range.first != out_edge_range.second; - out_edge_range.first++) - { - PathEdge & edge = graph[*(out_edge_range.first)]; - edge.update(); - } - // edges into the vertex - typedef boost::graph_traits<Graph>::in_edge_iterator InEdgeIterator; - std::pair<InEdgeIterator, InEdgeIterator> in_edge_range; - for (in_edge_range = boost::in_edges(vertex_descriptor, graph); - in_edge_range.first != in_edge_range.second; - in_edge_range.first++) - { - PathEdge & edge = graph[*(in_edge_range.first)]; - edge.update(); - } -} - -} Deleted: trunk/libtrack/Path.h =================================================================== --- trunk/libtrack/Path.h 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/Path.h 2009-11-26 17:05:52 UTC (rev 90) @@ -1,331 +0,0 @@ -/** @file libtrack/Path.h - * @brief Declare the Track::Path class. - * @author James Legg - */ -/* Copyright © 2009 James Legg. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -*/ - -#ifndef LIBTRACK_PATH_H_ -#define LIBTRACK_PATH_H_ - -#include <istream> -#include <ostream> - -/* gcc >= 4.3 Depreciates hash_set & hash_map, causing warnings to be emited - * when including boost graph library headers. - * We don't need the hash-based storage selectors anyway, so turn them off. - */ -#define BOOST_NO_HASH -#include <boost/graph/adjacency_list.hpp> -#undef BOOST_NO_HASH -#include <boost/shared_ptr.hpp> - -#include <LinearMath/btTransform.h> -#include <LinearMath/btVector3.h> -#include <LinearMath/btQuaternion.h> - -#include "Segment.h" -#include "Theme.h" -#include "AxisAlignedBoundingBox.h" -#include "DrawableMesh.h" -#include "UniqueIdentifier.h" -#include "Selectable.h" -#include "Dragable.h" -#include "edit_base/VertexRotationHandle.h" - -namespace Track -{ - -/** Information to store at the vertices. - */ -class PathVertex - : public UniqueIdentifier - , public Dragable -{ - friend std::ostream & operator<<(std::ostream & destination, - const PathVertex & path_vertex); -public: - PathVertex(); - PathVertex(std::istream & source, const Theme & theme); - virtual ~PathVertex(); - -protected: - /// The position of the vertex - btVector3 position; -public: - const btVector3 & get_position() const; - - void set_position(const btVector3 & position); - - /** Get the tangent of the arcs where they meet the vertex. - * It is unit length. - */ - const btVector3 & get_gradient() const; - - /** Get the up vector of the track at the vertex. - * It is perpendicular to gradient, and unit length. - */ - const btVector3 & get_up() const; - - /** Set the angle used to determine the gradient and up vector. - * The gradient is wherever the rotation takes y axis, and the up vector is - * wherever the rotation takes the z axis. - */ - void set_angle(btQuaternion angle); - - /** Get the angle used to determine the dradient and up vector. - */ - const btQuaternion & get_angle() const; - - /// The segment to use at the vertex - /** @todo Use the PathVertex's segment to make offseted positions for the - * places the arcs connect to. - */ - const Segment * segment; - - /// The index of the segment in the Theme - std::size_t segment_index; - - // Implement virtual functions from Selectable. - virtual bool is_here(btVector3 start, btVector3 stop, btScalar radius) const; - virtual boost::shared_ptr<Document::DocumentDelta> make_delta(btVector3 new_position) const; - - /** Return any vertex handles near a line segment. - * @param start The starting point of the line segment to scan. - * @param stop The end point of the the line segment to scan - * @param radius The radius around the line to consider. - * @return A pointer to a control point that alters this vertex within a - * cylinder around the line, with the request radius, or 0 if there is no - * such control point. - */ - const EditAssist::ControlPoint * get_control_point_here(btVector3 start, btVector3 stop, btScalar radius) const; - - /// Set the lengths of the rotation handles. - void set_handle_lengths(btScalar handle_length); -protected: - /// The normalised forward direction from this vertex. - btVector3 gradient; - /// The normalised up direction from this vertex. - btVector3 up; - /// The rotation from gradient (0, 1, 0) and up (0, 0, 1). - btQuaternion angle; - - /// control point used to change the up direction in the editor. - EditAssist::VertexRotationHandle up_handle; - /// control point used to change the forward direction in the editor. - EditAssist::VertexRotationHandle forward_handle; - - /// Update the properties of the vertices control points. - void update_handles(); -}; - -std::ostream & operator<<(std::ostream & destination, - const PathVertex & path_vertex); - -/**Information about an Edge that is recorded for each end of the edge, but can - * be different to multiple edges at the same vertex. - */ -class PathEdgeEnd -{ - friend std::ostream & operator<<(std::ostream & destination, - const PathEdgeEnd & path_edge_end); -public: - PathEdgeEnd(); - PathEdgeEnd(std::istream & source, const Segment & segment); - virtual ~PathEdgeEnd(); - - /** The strength at which to use the gradient at the vertex. - * It is dependent on the length of the edges. - */ - btScalar gradient_strength; - - /** The segment connection to use to connect the edge to the segment at the - * vertex. - */ - const SegmentConnection * segment_connection; - /// The index of the segment connection - std::size_t segment_conection_index; - - /** @todo It could make PathEdge cleaner if the relavent PathVertex was - * linked to in here. However the graph will then store the connections of - * each arc twice, and we'ld have to keep it up to date. - */ -}; - -std::ostream & operator<<(std::ostream & destination, - const PathEdgeEnd & path_edge_end); - -/** Information to store along the edges. - */ -class PathEdge - : public UniqueIdentifier - , public Selectable -{ - friend std::ostream & operator<<(std::ostream & destination, - const PathEdge & path_edge); -public: - PathEdge(const Theme & theme); - PathEdge(std::istream & source, const Theme & theme); - virtual ~PathEdge(); -protected: - const Theme & theme; -public: - /// The index of the segment in the Theme - std::size_t segment_index; - /// The segment to repeat to fill space along the edge. - const Segment * segment; - - /// Information about the connection to the starting vertex. - PathEdgeEnd start; - /// Information about the connection to the ending vertex. - PathEdgeEnd finish; - - ///@todo Addititional objects placed on track (e.g. booster, start position) - - /** Update various internal records. - * You should call this after changing any related geometry: the vertices at - * the ends of the arc, the gradient in the PathEdgeEnds, or the Segment - * that runs along the arc. - * After this has been called, the length, repetitions, and mesh will be - * up to date. - * The objects referenced by the parameters must not be freed when this - * object is still being used, unless this function is called again. - * @param source The information stored about the source node of the edge - * this PathEdge refers to. - * @param target The information stored about the target node. - */ - void update(const PathVertex & source, - const PathVertex & target); - - /** Update various internal records. - * Behaves like update(const PathVertex & source, const PathVertex & target), - * except it uses the reuses the same source and target previously - * passed in. The other version must have been used first to set - * source and target. - */ - void update(); - - /** Get the last calculated length. - * Use calculate_length() instead if the Edge or the vertices it connects - * have been modified since the last time it was called. - * If get_length is called before calculate_length, the result is undefined. - * @return the last value calculated by calculate_length(). - */ - btScalar get_length() const; - - /** Get the last calculated number of repetitions of the segment needed to - * fit the curve well. - * Call calculate_length() to update the value returned if the segment or - * curve's path has changed since the last call to calculate_length(). If - * calculate_length() has never been called, the returned value is undefined. - */ - unsigned int get_number_of_repetions() const; - - /** Get the transformation at a specific point along the arc. update() must - * have been called previously to set the source and target PathVertex. - * @param position point along the length of the arc from 0 (start) to 1 - * (finish). - * @return transformation to apply to a vertex along the path. - */ - btTransform get_transform(btScalar position) const; - - /** Get a mesh to draw part of the arc. The number of meshes is the number - * returned by get_number_of_repetitions(). The meshes are only created when - * update() is called. - * @param index The 0 based index specifying which mesh to get. It is an - * error to use a higher index than the value returned by - * get_number_of_repetitions(). - * @todo write similar functions for the other meshes. - */ - const boost::shared_ptr<DrawableMesh> get_graphics_mesh(std::size_t index) const; - - virtual bool is_here(btVector3 start, btVector3 stop, btScalar radius) const; -protected: - btScalar length; - unsigned int number_of_repetions; - std::vector<boost::shared_ptr<DrawableMesh> > meshes; - const PathVertex * source; - const PathVertex * target; -}; - -std::ostream & operator<<(std::ostream & destination, - const PathEdge & path_edge); - -/** A graph describing the track layout. - */ -class Path -{ - friend std::ostream & operator<<(std::ostream & destination, - const Path & path); -public: - /// initalise from a stream - Path(std::istream & source, const Theme & theme); - /// make a default track layout. - Path(const Theme & theme); - virtual ~Path(); - - /// Graph of track layout. - typedef boost::adjacency_list <boost::vecS, boost::vecS, - boost::bidirectionalS, - PathVertex, PathEdge> Graph; - Graph graph; - - /** Get the axis aligned bounding box of the vertices and control points in - * the path with some padding. - * - * @todo return the axis aligned bounding box of the path instead. - * @todo Monitor changes to graph, then only calculate when it has changed. - */ - AxisAlignedBoundingBox get_bounding_box() const; - - /// find a graph node by ID - PathVertex & get_node(const std::size_t index); - /// find a graph node by ID - const PathVertex & get_node(const std::size_t index) const; - - /// find a graph node descriptor by ID - Path::Graph::vertex_descriptor get_node_descriptor(const std::size_t index); - /// find a graph node descriptor by ID - const Path::Graph::vertex_descriptor get_node_descriptor(const std::size_t index) const; - - - /// find a graph edge by ID - PathEdge & get_edge(const std::size_t index); - /// find a graph edge by ID - const PathEdge & get_edge(const std::size_t index) const; - - /// find a graph edge descriptor by ID - Path::Graph::edge_descriptor get_edge_descriptor(const std::size_t index); - /// find a graph edge descriptor by ID - const Path::Graph::edge_descriptor get_edge_descriptor(const std::size_t index) const; - - /** Set the lengths of the rotation handles present in the path's - * vertices. - * @param handle_length The length the handles extend from the point they - * rotate. - */ - void set_handle_lengths(btScalar handle_length); - - /** Update the edges connected to a particular vertex. - * This recalculates the length, number of segments, and meshes. You - * should call this after changing a vertex's position, segment, or angle. - * @param vertex_descriptor The vertex descriptor of the vertex that was - * changed. - */ - void update_connected_edges(Path::Graph::vertex_descriptor vertex_descriptor); -private: - const Theme & theme; - Path::Graph::vertex_descriptor get_node_descriptor_internal(const std::size_t index) const; - Path::Graph::edge_descriptor get_edge_descriptor_internal(const std::size_t index) const; -}; - -std::ostream & operator<<(std::ostream & destination, - const Path & path); - -} - -#endif // LIBTRACK_PATH_H_ Modified: trunk/libtrack/PieceDistortion.cpp =================================================================== --- trunk/libtrack/PieceDistortion.cpp 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/PieceDistortion.cpp 2009-11-26 17:05:52 UTC (rev 90) @@ -9,7 +9,7 @@ (at your option) any later version. */ #include "PieceDistortion.h" -#include "Path.h" +#include "path/Path.h" namespace Track { Modified: trunk/libtrack/Track.h =================================================================== --- trunk/libtrack/Track.h 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/Track.h 2009-11-26 17:05:52 UTC (rev 90) @@ -16,7 +16,7 @@ #include <boost/shared_ptr.hpp> #include "Theme.h" -#include "Path.h" +#include "path/Path.h" #include "DecoMeshInstance.h" /** Used in libtrack, a library used by both the game and track editor. Modified: trunk/libtrack/document/ChangeEdgeSegmentDelta.h =================================================================== --- trunk/libtrack/document/ChangeEdgeSegmentDelta.h 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/document/ChangeEdgeSegmentDelta.h 2009-11-26 17:05:52 UTC (rev 90) @@ -15,7 +15,7 @@ #include <cstddef> #include "ChangePropertyDelta.h" -#include "../Path.h" +#include "../path/Path.h" namespace Document { Modified: trunk/libtrack/edit_base/VertexRotationHandle.cpp =================================================================== --- trunk/libtrack/edit_base/VertexRotationHandle.cpp 2009-11-26 00:45:30 UTC (rev 89) +++ trunk/libtrack/edit_base/VertexRotationHandle.cpp 2009-11-26 17:05:52 UTC (rev 90) @@ -10,9 +10,12 @@ */ #include "VertexRotationHandle.h" +#include <cmath> +#include <Debug.h> + #include <LinearMath/btQuaternion.h> -#include "../Path.h" +#include "../path/Path.h" #include "../document/RotateVertexDelta.h" namespace Track Copied: trunk/libtrack/path/Path.cpp (from rev 89, trunk/libtrack/Path.cpp) =================================================================== --- trunk/libtrack/path/Path.cpp (rev 0) +++ trunk/libtrack/path/Path.cpp 2009-11-26 17:05:52 UTC (rev 90) @@ -0,0 +1,290 @@ +/** @file libtrack/Path.cpp + * @brief Implement the Track::Path class. + * @author James Legg + */ +/* Copyright © 2009 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "Path.h" +#include "../stream_loader.h" + +namespace Track +{ + +Path::Path(std::istream & source, const Theme & theme) +// We store the graph in the stream as a vector of vertices, followed by a + // vector of edges. Start by getting the size of the vertices vector. + : graph(int_from_stream(source)) + , theme(theme) +{ + // add vertex descriptors + typedef boost::graph_traits<Graph>::vertex_iterator VertexIterator; + std::pair<VertexIterator, VertexIterator> vertex_range; + for (vertex_range = vertices(graph); + vertex_range.first != vertex_range.second; + vertex_range.first++) + { + graph[*(vertex_range.first)] = PathVertex(source, theme); + } + + // now on to the edges + std::size_t number_of_edges; + source >> number_of_edges; + for (std::size_t edge_index = 0; edge_index < number_of_edges; edge_index++) + { + // Read vertex indices + std::size_t vert_index_start, vert_index_end; + source >> vert_index_start >> vert_index_end; + if ( vert_index_start > num_vertices(graph) + || vert_index_end > num_vertices(graph)) + { + // File refers to vertices that don't exist, so it must be invalid. + throw std::istream::failure("Invalid data: bad vertex index in path's graph."); + } + + // Read edge data + PathEdge edge_data(source, theme); + + std::pair<Graph::edge_descriptor, bool> new_edge = + add_edge(vert_index_start, vert_index_end, edge_data, graph); + // update the edge's information (length, meshes, and so on). + graph[new_edge.first].update(graph[vert_index_start], graph[vert_index_end]); + } +} + +Path::Path(const Theme & theme) + : theme(theme) +{ + // add two vertices and two edges connecting them in a loop. + PathVertex vertex_1, vertex_2; + vertex_1.set_position(btVector3(300.0, 0.0, 200.0)); + vertex_2.set_position(btVector3(-300.0, 0.0, -200.0)); + vertex_1.set_angle(btQuaternion(0.0, 0.0, 0.0)); + vertex_2.set_angle(btQuaternion(0.0, 0.0, M_PI)); + vertex_1.segment = &(theme.get_segment(0)); + vertex_1.segment_index = 0; + vertex_2.segment = &(theme.get_segment(0)); + vertex_2.segment_index = 0; + Graph::vertex_descriptor + vertex_1_descriptor = boost::add_vertex(vertex_1, graph), + vertex_2_descriptor = boost::add_vertex(vertex_2, graph); + + PathEdge edge_1(theme), edge_2(theme); + edge_1.segment = &(theme.get_segment(1)); + edge_1.segment_index = 1; + edge_2.segment = &(theme.get_segment(1)); + edge_2.segment_index = 1; + edge_1.start.gradient_strength = 400.0; + edge_1.finish.gradient_strength = 400.0; + edge_2.start = edge_1.finish; + edge_2.finish = edge_1.start; + add_edge(vertex_2_descriptor, vertex_1_descriptor, edge_1, graph); + add_edge(vertex_1_descriptor, vertex_2_descriptor, edge_2, graph); + typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; + + // calculate the length of the edges. + typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator; + std::pair<EdgeIterator, EdgeIterator> edge_range(boost::edges(graph)); + // Get references to where the vertices are held. + PathVertex & vertex_1r = graph[vertex_1_descriptor]; + PathVertex & vertex_2r = graph[vertex_2_descriptor]; + graph[*(edge_range.first)].update(vertex_1r, vertex_2r); + edge_range.first++; + graph[*(edge_range.first)].update(vertex_2r, vertex_1r); +} + +Path::~Path() +{ + +} + +std::ostream & operator<<(std::ostream & destination, const Path & path) +{ + // vertices + destination << boost::num_vertices(path.graph) << ' '; + typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; + std::pair<VertexIterator, VertexIterator> vertex_range; + for (vertex_range = boost::vertices(path.graph); + vertex_range.first != vertex_range.second; + vertex_range.first++) + { + destination << path.graph[*(vertex_range.first)] << ' '; + } + + destination << boost::num_edges(path.graph) << ' '; + typedef boost::graph_traits<Path::Graph>::edge_iterator EdgeIterator; + std::pair<EdgeIterator, EdgeIterator> edge_range; + for (edge_range = boost::edges(path.graph); + edge_range.first != edge_range.second; + edge_range.first++) + { + // output the vertex descriptor of the source and target vertices, + // so the graph can be reconstructed. + destination << boost::source(*(edge_range.first), path.graph) << ' ' + << boost::target(*(edge_range.first), path.graph) << ' ' + // Now output the the PathEdge information. + << path.graph[*(edge_range.first)] << ' '; + } + + return destination; +} + +AxisAlignedBoundingBox Path::get_bounding_box() const +{ + AxisAlignedBoundingBox result; + + // find the bounding box of all the vertices. + typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; + std::pair<VertexIterator, VertexIterator> vertex_range; + for (vertex_range = boost::vertices(graph); + vertex_range.first != vertex_range.second; + vertex_range.first++) + { + result |= graph[*(vertex_range.first)].get_position(); + } + + // now expand to include control points + typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator; + std::pair<EdgeIterator, EdgeIterator> edge_range; + for (edge_range = boost::edges(graph); + edge_range.first != edge_range.second; + edge_range.first++) + { + btVector3 position = graph[source(*(edge_range.first), graph)].get_position(); + btVector3 gradient = graph[source(*(edge_range.first), graph)].get_gradient(); + btScalar magnitude = graph[*(edge_range.first)].start.gradient_strength; + result |= position + gradient * magnitude; + position = graph[target(*(edge_range.first), graph)].get_position(); + gradient = graph[target(*(edge_range.first), graph)].get_gradient(); + magnitude = graph[*(edge_range.first)].finish.gradient_strength; + result |= position - gradient * magnitude; + } + + // add some padding. + result.add_border(200.0); + + return result; +} + +PathVertex & Path::get_node(const std::size_t index) +{ + return graph[get_node_descriptor(index)]; +} + +const PathVertex & Path::get_node(const std::size_t index) const +{ + return graph[get_node_descriptor(index)]; +} + +Path::Graph::vertex_descriptor Path::get_node_descriptor(const std::size_t index) +{ + return get_node_descriptor_internal(index); +} + +const Path::Graph::vertex_descriptor Path::get_node_descriptor(const std::size_t index) const +{ + return get_node_descriptor_internal(index); +} + +Path::Graph::vertex_descriptor Path::get_node_descriptor_internal(const std::size_t index) const +{ + /// @todo Use a more efficent data structure? + typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; + std::pair<VertexIterator, VertexIterator> vertex_range; + for (vertex_range = boost::vertices(graph); + vertex_range.first != vertex_range.second; + vertex_range.first++) + { + const PathVertex & vertex = graph[*(vertex_range.first)]; + if (vertex.get_name() == index) + { + return *(vertex_range.first); + } + } + DEBUG_MESSAGE("No node has index " << index << ", aborting."); + assert(false); + throw; +} + + +PathEdge & Path::get_edge(const std::size_t index) +{ + return graph[get_edge_descriptor(index)]; +} + +const PathEdge & Path::get_edge(const std::size_t index) const +{ + return graph[get_edge_descriptor(index)]; +} + +Path::Graph::edge_descriptor Path::get_edge_descriptor(const std::size_t index) +{ + return get_edge_descriptor_internal(index); +} + +const Path::Graph::edge_descriptor Path::get_edge_descriptor(const std::size_t index) const +{ + return get_edge_descriptor_internal(index); +} + +Path::Graph::edge_descriptor Path::get_edge_descriptor_internal(const std::size_t index) const +{ + /// @todo Use a more efficent data structure? + typedef boost::graph_traits<Path::Graph>::edge_iterator EdgeIterator; + std::pair<EdgeIterator, EdgeIterator> edge_range; + for (edge_range = boost::edges(graph); + edge_range.first != edge_range.second; + edge_range.first++) + { + const PathEdge & edge = graph[*(edge_range.first)]; + if (edge.get_name() == index) + { + return *(edge_range.first); + } + } + DEBUG_MESSAGE("No edge has index " << index << ", aborting."); + assert(false); + throw; +} + +void Path::set_handle_lengths(btScalar handle_length) +{ + typedef boost::graph_traits<Path::Graph>::vertex_iterator VertexIterator; + std::pair<VertexIterator, VertexIterator> vertex_range; + for (vertex_range = boost::vertices(graph); + vertex_range.first != vertex_range.second; + vertex_range.first++) + { + graph[*(vertex_range.first)].set_handle_lengths(handle_length); + } +} + +void Path::update_connected_edges(Path::Graph::vertex_descriptor vertex_descriptor) +{ + // edges out of the vertex + typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIterator; + std::pair<OutEdgeIterator, OutEdgeIterator> out_edge_range; + for (out_edge_range = boost::out_edges(vertex_descriptor, graph); + out_edge_range.first != out_edge_range.second; + out_edge_range.first++) + { + PathEdge & edge = graph[*(out_edge_range.first)]; + edge.update(); + } + // edges into the vertex + typedef boost::graph_traits<Graph>::in_edge_iterator InEdgeIterator; + std::pair<InEdgeIterator, InEdgeIterator> in_edge_range; + for (in_edge_range = boost::in_edges(vertex_descriptor, graph); + in_edge_range.first != in_edge_range.second; + in_edge_range.first++) + { + PathEdge & edge = graph[*(in_edge_range.first)]; + edge.update(); + } +} + +} Copied: trunk/libtrack/path/Path.h (from rev 89, trunk/libtrack/Path.h) =================================================================== --- trunk/libtrack/path/Path.h (rev 0) +++ trunk/libtrack/path/Path.h 2009-11-26 17:05:52 UTC (rev 90) @@ -0,0 +1,110 @@ +/** @file libtrack/Path/Path.h + * @brief Declare the Track::Path class. + * @author James Legg + */ +/* Copyright © 2009 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#ifndef LIBTRACK_PATH_PATH_H_ +#define LIBTRACK_PATH_PATH_H_ + +#include <istream> +#include <ostream> + +/* gcc >= 4.3 Depreciates hash_set & hash_map, causing warnings to be emited + * when including boost graph library headers. + * We don't need the hash-based storage selectors anyway, so turn them off. + */ +#define BOOST_NO_HASH +#include <boost/graph/adjacency_list.hpp> +#undef BOOST_NO_HASH + +#include <LinearMath/btScalar.h> + +#include "../Theme.h" +#include "../AxisAlignedBoundingBox.h" + +#include "PathEdge.h" +#include "PathVertex.h" + +namespace Track +{ + +/** A graph describing the track layout. + */ +class Path +{ + friend std::ostream & operator<<(std::ostream & destination, + const Path & path); +public: + /// initalise from a stream + Path(std::istream & source, const Theme & theme); + /// make a default track layout. + Path(const Theme & theme); + virtual ~Path(); + + /// Graph of track layout. + typedef boost::adjacency_list <boost::vecS, boost::vecS, + boost::bidirectionalS, + PathVertex, PathEdge> Graph; + Graph graph; + + /** Get the axis aligned bounding box of the vertices and control points in + * the path with some padding. + * + * @todo return the axis aligned bounding box of the path instead. + * @todo Monitor changes to graph, then only calculate when it has changed. + */ + AxisAlignedBoundingBox get_bounding_box() const; + + /// find a graph node by ID + PathVertex & get_node(const std::size_t index); + /// find a graph node by ID + const PathVertex & get_node(const std::size_t index) const; + + /// find a graph node descriptor by ID + Path::Graph::vertex_descriptor get_node_descriptor(const std::size_t index); + /// find a graph node descriptor by ID + const Path::Graph::vertex_descriptor get_node_descriptor(const std::size_t index) const; + + + /// find a graph edge by ID + PathEdge & get_edge(const std::size_t index); + /// find a graph edge by ID + const PathEdge & get_edge(const std::size_t index) const; + + /// find a graph edge descriptor by ID + Path::Graph::edge_descriptor get_edge_descriptor(const std::size_t index); + /// find a graph edge descriptor by ID + const Path::Graph::edge_descriptor get_edge_descriptor(const std::size_t index) const; + + /** Set the lengths of the rotation handles present in the path's + * vertices. + * @param handle_length The length the handles extend from the point they + * rotate. + */ + void set_handle_lengths(btScalar handle_length); + + /** Update the edges connected to a particular vertex. + * This recalculates the length, number of segments, and meshes. You + * should call this after changing a vertex's position, segment, or angle. + * @param vertex_descriptor The vertex descriptor of the vertex that was + * changed. + */ + void update_connected_edges(Path::Graph::vertex_descriptor vertex_descriptor); +private: + const Theme & theme; + Path::Graph::vertex_descriptor get_node_descriptor_internal(const std::size_t index) const; + Path::Graph::edge_descriptor get_edge_descriptor_internal(const std::size_t index) const; +}; + +std::ostream & operator<<(std::ostream & destination, + const Path & path); + +} + +#endif // LIBTRACK_PATH_PATH_H_ Added: trunk/libtrack/path/PathEdge.cpp =================================================================== --- trunk/libtrack/path/PathEdge.cpp (rev 0) +++ trunk/libtrack/path/PathEdge.cpp 2009-11-26 17:05:52 UTC (rev 90) @@ -0,0 +1,227 @@ +/** @file libtrack/Path/PathEdgeEnd.h + * @brief Implement the Track::PathEdgeEnd class. + * @author James Legg + */ +/* Copyright © 2009 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "PathEdge.h" +#include "../stream_loader.h" + +namespace Track +{ + +PathEdge::PathEdge(const Theme & theme) + : theme(theme) + , segment(0) +{ + +} + +PathEdge::PathEdge(std::istream & source, const Theme & theme) + : theme(theme) + , segment_index(int_from_stream(source)) + , segment(&theme.get_segment(segment_index)) + , start(source, *segment) + , finish(source, *segment) +{ +} + +PathEdge::~PathEdge() +{ + +} + +// calculate good sizes of steps to take in approximation of edge length. +template <int t> +struct one_over_two_to_the_power_of +{ + static const btScalar result = 0.5 * one_over_two_to_the_power_of<t - 1>::result; +}; + +template <> +struct one_over_two_to_the_power_of<0> +{ + static const btScalar result = 1.0; +}; + + +void PathEdge::update(const PathVertex & source_in, + const PathVertex & target_in) +{ + source = & source_in; + target = & target_in; + update(); +} + +void PathEdge::update() +{ + const PathVertex & source_in = *source; + const PathVertex & target_in = *target; + // A cubic bezier spline isn't analytically intergratable. + // Instead of hard numerical approximations, I'll do a piecewise linear + // estimation. + const btVector3 & p0 = source_in.get_position(); + const btVector3 p1 = p0 + source_in.get_gradient() * start.gradient_strength; + const btVector3 & p3 = target_in.get_position(); + const btVector3 p2 = p0 - target_in.get_gradient() * finish.gradient_strength; + + btVector3 last_position = p0; + + length = 0; + const btScalar step = one_over_two_to_the_power_of<5>::result; + for (btScalar t = step; t < 1.0; t += step) + { + const btScalar reverse_t = 1.0 - t; + const btScalar t_squared = t * t; + const btScalar reverse_t_squared = reverse_t * reverse_t; + const btScalar t_cubed = t_squared * t; + const btScalar reverse_t_cubed = reverse_t_squared * reverse_t; + const btVector3 this_position = reverse_t_cubed * p0 + + 3.0 * reverse_t_squared * t * p1 + + 3.0 * reverse_t * t_squared * p2 + + t_cubed * p3; + length += (this_position - last_position).length(); + last_position = this_position; + } + + // how many repetions of the segment do we need? + if (segment) + { + // round to the nearest whole number of segments. + number_of_repetions = int (length / segment->get_length() + 0.5); + } + if (number_of_repetions == 0) + { + // even short gaps between vertices must be filled. + number_of_repetions = 1; + } + if (number_of_repetions > 64) + { + DEBUG_MESSAGE("Warning: large number of meshes (" << number_of_repetions << ") needed for edge " << get_name()); + } + // make the meshes. + meshes.clear(); + meshes.reserve(number_of_repetions); + for (unsigned int i = 0; i < number_of_repetions; i++) + { + PieceDistortion transform(*this, i, + segment->get_length(), + segment->get_minimum_y()); + meshes.push_back(boost::shared_ptr<DrawableMesh>( + new DrawableMesh(segment->get_graphics_mesh().get_distorted_faces(transform)) + )); + } +} + +btScalar PathEdge::get_length() const +{ + return length; +} + +unsigned int PathEdge::get_number_of_repetions() const +{ + return number_of_repetions; +} + +btTransform PathEdge::get_transform(btScalar position) const +{ + const btScalar rposition = 1.0 - position; + const btScalar position_squared = position * position; + const btScalar rposition_squared = rposition * rposition; + const btScalar position_cubed = position_squared * position; + const btScalar rposition_cubed = rposition_squared * rposition; + const btVector3 & p0 = source->get_position(); + const btVector3 p1 = p0 + source->get_gradient() * start.gradient_strength; + const btVector3 & p3 = target->get_position(); + const btVector3 p2 = p3 - target->get_gradient() * finish.gradient_strength; + const btVector3 translation = rposition_cubed * p0 + + 3.0 * rposition_squared * position * p1 + + 3.0 * rposition * position_squared * p2 + + position_cubed * p3; + // find tangent for rotation. + const btVector3 start_tangent = p1 - p0; + const btVector3 end_tangent = p3 - p2; + /* The tangent's control p... [truncated message content] |