diff --git a/src/osgPlugins/dxf/ReaderWriterDXF.cpp b/src/osgPlugins/dxf/ReaderWriterDXF.cpp index 56f8f3058..d7c5801dc 100644 --- a/src/osgPlugins/dxf/ReaderWriterDXF.cpp +++ b/src/osgPlugins/dxf/ReaderWriterDXF.cpp @@ -17,8 +17,10 @@ #include #include #include + #include #include +#include #include "dxfFile.h" @@ -46,10 +48,38 @@ REGISTER_OSGPLUGIN(dxf, ReaderWriterdxf) // read file and convert to OSG. osgDB::ReaderWriter::ReadResult -ReaderWriterdxf::readNode(const std::string& filename, const osgDB::ReaderWriter::Options*) const +ReaderWriterdxf::readNode(const std::string& filename, const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getFileExtension(filename); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + // extract accuracy options if available + if (options) { + bool useAccuracy=false; // if we specify accuracy of curve rendering or not + double maxError=0.0; // if useAccuracy - the accuracy (max deviation) from the arc + bool improveAccuracyOnly=false; // if true only use the given accuracy if it would improve the curve compared to the previous implementation + // Thus you can ensure that large curves get rendered better but small ones don't get worse + + std::string optionsstring=options->getOptionString(); + + size_t accstart=optionsstring.find("Accuracy("); + if (accstart>=0) { + const char* start=optionsstring.c_str() + accstart + strlen("Accuracy("); + if (sscanf(start,"%lf",&maxError)==1) useAccuracy=true; + } + if (useAccuracy) { + // Option to only use the new accuracy code when it would improve on the accuracy of the old method + if (optionsstring.find("ImproveAccuracyOnly") != std::string::npos) { + improveAccuracyOnly=true; + } + // Pull out the initial dxfArc copy from the registry and set accuracy there. + // When actual dxfArcs/Circles are created they will inherit these parameters from the exemplar + dxfEntity::getRegistryEntity("ARC")->setAccuracy(true,maxError,improveAccuracyOnly); + dxfEntity::getRegistryEntity("CIRCLE")->setAccuracy(true,maxError,improveAccuracyOnly); + } // accuracy options exists + } // options exist + + // Open dxfFile df(filename); if (df.parseFile()) { diff --git a/src/osgPlugins/dxf/dxfEntity.cpp b/src/osgPlugins/dxf/dxfEntity.cpp index a0c678daf..55d4c8008 100644 --- a/src/osgPlugins/dxf/dxfEntity.cpp +++ b/src/osgPlugins/dxf/dxfEntity.cpp @@ -16,6 +16,8 @@ #include "dxfBlock.h" #include "codeValue.h" +#include // just for debugging + using namespace std; using namespace osg; @@ -173,27 +175,41 @@ dxfCircle::drawScene(scene* sc) getOCSMatrix(_ocs, m); sc->ocs(m); std::vector vlist; - int numsteps = 360/5; // baaarghf. - double angle_step = osg::DegreesToRadians((double)360.0 / (double) numsteps); - double angle1 = 0.0f; - double angle2 = 0.0f; + + double theta=5.0; // we generate polyline from "spokes" at theta degrees at arc's center + + if (_useAccuracy) { + // we generate points on a polyline where each point lies on the arc, thus the maximum error occurs at the midpoint of each line segment where it lies furthest inside the arc + // If we divide the segment in half and connect the bisection point to the arc's center, we have two rightangled triangles with + // one side=r-maxError, hypotenuse=r, and internal angle at center is half the angle we will step with: + double maxError=min(_maxError,_radius); // Avoid offending acos() in the edge case where allowable deviation is greater than radius. + double newtheta=acos( (_radius-maxError) / _radius); + newtheta=osg::RadiansToDegrees(newtheta)*2.0; + + // Option to only use the new accuracy code when it would improve on the accuracy of the old method + if (_improveAccuracyOnly) { + theta=min(newtheta,theta); + } else { + theta=newtheta; + } + } + theta=osg::DegreesToRadians(theta); + + // We create an anglestep<=theta so that the line's points are evenly distributed around the circle + unsigned int numsteps=floor(osg::PI*2/theta); + if (numsteps<3) numsteps=3; // Sanity check: minimal representation of a circle is a tri + double anglestep=osg::PI*2/numsteps; + + double angle1 = 0.0; Vec3d a = _center; - Vec3d b,c; - for (int r = 0; r < numsteps; r++) - { - angle1 = angle2; - if (r == numsteps - 1) - angle2 = 0.0f; - else - angle2 += angle_step; + Vec3d b; + for(unsigned int r=0;r<=numsteps;r++) { b = a + Vec3d(_radius * (double) sin(angle1), _radius * (double) cos(angle1), 0); - c = a + Vec3d(_radius * (double) sin(angle2), _radius * (double) cos(angle2), 0); -// vlist.push_back(a); + angle1 += anglestep; vlist.push_back(b); - vlist.push_back(c); } - sc->addLineStrip(getLayer(), _color, vlist); -// sc->addTriangles(getLayer(), _color, vlist); + + sc->addLineStrip(getLayer(), _color, vlist); // Should really add LineLoop implementation and save a vertex sc->ocs_clear(); } @@ -253,25 +269,46 @@ dxfArc::drawScene(scene* sc) start = _startAngle; end = _endAngle; } + + double theta=5.0; // we generate polyline from "spokes" at theta degrees at arc's center + + if (_useAccuracy) { + // we generate points on a polyline where each point lies on the arc, thus the maximum error occurs at the midpoint of each line segment where it lies furthest inside the arc + // If we divide the segment in half and connect the bisection point to the arc's center, we have two rightangled triangles with + // one side=r-maxError, hypotenuse=r, and internal angle at center is half the angle we will step with: + double maxError=min(_maxError,_radius); // Avoid offending acos() in the edge case where allowable deviation is greater than radius. + double newtheta=acos( (_radius-maxError) / _radius); + newtheta=osg::RadiansToDegrees(newtheta)*2.0; + //cout<<"r="<<_radius<<" _me="<<_maxError<<" (_radius-_maxError)="<<(_radius-_maxError)<<" newtheta="<setAccuracy(_useAccuracy,_maxError,_improveAccuracyOnly); + //std::cout<<"dxfArc::create with _useAccuracy="<<_useAccuracy<<" maxError="<<_maxError<<" improveAccuracyOnly="<<_improveAccuracyOnly<create + static dxfBasicEntity* getRegistryEntity(std::string s) { + return _registry[s].get(); + } + protected: std::vector > _entityList; static std::map > _registry; dxfBasicEntity* _entity; bool _seqend; // bypass 0 codes. needs a 0 seqend to close. + + + }; /** Proxy class for automatic registration of dxf entities reader/writers.*/