开发者

a good-design approach for making a geometry library (regarding using union or not)?

i am making a geometry library and i am confused, what should be the return type of a function which calculates the intersection of a segment with another segment. The returned value would sometimes be a point and sometimes be a segment (overlap case) and sometimes be an empty set. As per my thinking there could be 3 ways to tackle this as per follows: 1. return a union (segment, null, point) 2. return a segment with first point == second point when intersection is a single point and both points as NAN when intersection is an empty set 3. return a vector (with 0 elements for empty set, 1 element for pnt and 2 elements for segment)

Please let me know if there are any alternates possible and what are the pros and cons of each of the des开发者_如何学运维ign. Moreover which design is supposed to be a good design and why. I am interested in making a robust architecture which lets single pipe-lining and thus almost no rewriting of code as well as scalable (in terms of adding functionality and handling all the edge cases)

Following is my code for reference (whose return type is vector)

vector<pnt> seg::inter(seg z){
vector<pnt> ans;
if(p1==p2){if(z.doesinter(p1)){ans.pb(p1);}}
else if(z.p1==z.p2){
if(doesinter(z.p1)) ans.pb(z.p1);}
else{
pnt p1p2=(p2-p1);
pnt q1=p1p2*pnt(0,1);
long double h1,h2;
if(abs((z.p2-z.p1).dot(q1))<=eps){
pnt r1((z.p1-p1)/(p2-p1)),r2((z.p2-p1)/(p2-p1));
if(abs(r1.y)<=eps){//colinear case
h1=r1.x;
h2=r2.x;
if(h1>h2)swap(h1,h2);
if(h2>=0&&h1<=1){//add eps
h1=max(0.0L,h1);h2=min(1.0L,h2);
ans.pb(p1+p1p2*h1);
if(doublecompare(h1,h2)==-1)ans.pb(p1+p1p2*h2);}}}
else{
h1 = ((p1-z.p1).dot(q1))/((z.p2-z.p1).dot(q1));
pnt q2 = (z.p2-z.p1)*pnt(0,1);
h2 = ((z.p1-p1).dot(q2))/((p2-p1).dot(q2));
if(h1+eps>=0&&h1-eps<=1&&h2+eps>=0&&h2-eps<=1) ans.pb(z.p1+(z.p2-z.p1)*h1);}}
return ans;}


My suggestion is to create a specialized Intersection class that can handle all the cases. You can return an instance of that class then. Internally the class could have for example the vector representation (with same endpoints if the intersection is one point, as you suggested) and could have methods for determining which case it is actually (bool isIntersecting(), isSegment(), etc,).

For a more sophisticated design, you can make this Intersection class abstract and provide specialized implementations for NoIntersection, PointIntersection and SegmentIntersection with different inner data representation.


The union "idea" is perfectly fine, it naturally express all cases. However I would recommend not using the union of the C language directly, because it's a low-level construct which will expose you to hard to find bugs.

Instead, you should use Boost.Variant.

Basically a variant is a combination of 2 elements: a tag and a union, the tag being used to tell which member of the union is in used at a given moment. Being a C++ class, it is C++ aware (not like union) and so you won't be facing restrictions on the type of objects you can put in and you won't be facing undefined behavior either.

typedef boost::Variant<NoneType, Point, Segment> IntersectionType;

Of course, you can also decide to wrap this in a class, to expose a richer interface.


In Modern C++ Design Alexandrescu's explain the multi methods using as example exactly your problem.

You should have a look.

http://loki-lib.sourceforge.net/index.php?n=Idioms.MultipleDispatcher

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜