From fa27223fcd5f8d6bf1e794ca22c85ba777ba2e76 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 8 Apr 2009 13:21:59 +0000 Subject: [PATCH] From Mike Connell, "Here are some small fixes that allow you to specify the allowable deviation when creating polylines from arcs and circles in DXF files. Changes are against 2.8.0 It adds two options: Accuracy(x) - ensures the polyline will be within x units from the ideal arc/curve ImproveAccuracyOnly - do not use the given accuracy 'x', if it would result in a worse curve than with the previous (2.8.0) implementation for a particular arc/curve. As an added bonus there was a small bug in the existing implementation whereby the primitives were line strips but the vertices generated were actually suitable for GL_LINES, so the improved accuracy doesn't even have to come at a performance cost :-)" --- src/osgPlugins/dxf/ReaderWriterDXF.cpp | 32 ++++++++- src/osgPlugins/dxf/dxfEntity.cpp | 91 ++++++++++++++++++-------- src/osgPlugins/dxf/dxfEntity.h | 38 +++++++++-- 3 files changed, 129 insertions(+), 32 deletions(-) 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.*/