|
1 | | -[See also the file TODO_static_filters] |
2 | | - |
3 | 1 | Concerning the main code: |
4 | 2 | ------------------------- |
5 | 3 | - Policy for overlapping comparisons : another good (faster) solution |
@@ -70,3 +68,143 @@ Concerning the test-suite: |
70 | 68 | The script could output information to test them generically somehow. |
71 | 69 | - Test NaN propagation. Comparisons with these should throw the exception... |
72 | 70 | Check that they are correctly propagated (by min(), max(), even operator*...) |
| 71 | + |
| 72 | +Special TODO list for the static filters. |
| 73 | +----------------------------------------- |
| 74 | +It's a lot of work here for a "minor" optimization, so it's "low" priority, |
| 75 | +except we could merge stuff with Olivier's Fixed ! |
| 76 | + |
| 77 | +- Known problems with the current approach: |
| 78 | + - Match operator<(a,b) and co... |
| 79 | + - What to do with branches (e.g. collinearC3() and power_test()): |
| 80 | + - The epsilon computation type should return ZERO/EQUAL as default. |
| 81 | + This way, collinearC3() works. |
| 82 | + - The user can provide the epsilon variant inside the source code, |
| 83 | + delimited by special symbols /*CGAL_FILTER_BODY ... */. That's the |
| 84 | + solution for CGAL. |
| 85 | + - Checks that the epsilons have been updated (which will not prove that |
| 86 | + it's correct, but is better than nothing). |
| 87 | +- Or use G++'s interface as a parser ? See gcc mail archives, 15 august 2000, |
| 88 | + "XML output for GCC". An XML description for predicates ? |
| 89 | +- /*DEGREE=2*/ attribute to the arguments ? |
| 90 | +- # of bounds : one per predicate, or one per argument ? give choice. |
| 91 | +- # of epsilons: one per predicate, or one set per sub-predicate ? choice. |
| 92 | +- Check that the compiler optimizes the epsilon computation (use |
| 93 | + __attribute__((const)) for Static_filter_error operators) ? |
| 94 | +- As Fred pointed out: the scheme is not thread safe. |
| 95 | +- Remove the assertions in the original code. |
| 96 | +- In case there are loops, we must take the max() of the epsilons. This should |
| 97 | + not happen often, imho... Wait and see. |
| 98 | +- Move static_infos in src/. |
| 99 | +- Replace: NEW_bound = max(NEW_bound, fabs(px.to_double())); by: if (NEW_bound |
| 100 | + < fabs(px.to_double())) NEW_bound = fabs(px.to_double()); or even, using a |
| 101 | + .bound() member function: if (NEW_bound < px.bound()) NEW_bound = px.bound(); |
| 102 | + Moreover, to_double() is not exact, we should use abs(to_interval(x)).sup() ! |
| 103 | +- Member function access for generic type should be (?): .dbl_approx() |
| 104 | + .bound() (basically a bound on: fabs(.dbl_approx())) .error() |
| 105 | +- Add a "number of bits" field in Static_filter_error ? (so that we get the |
| 106 | + same thing as Fixed for 24 bits) |
| 107 | + |
| 108 | +- Another approach to consider : Implement predicates taking one or several |
| 109 | + epsilons as additional parameters, and have the functionality found in Open |
| 110 | + CasCade, using sign(a, epsilon). Then with a special traits or something, |
| 111 | + we can define sign(a,epsilon) = sign(a), and get the traditionnal template |
| 112 | + predicates from that... So that the epsilons are removed at compile time ? |
| 113 | + It would be nice to know exactly the desired functionality for epsilons... |
| 114 | + |
| 115 | +- Where to put the context of the predicates ? different possibilities : |
| 116 | + 1- static data member of the predicate object (~as it is now) |
| 117 | + 2- data member of the predicate object |
| 118 | + 3- static data member of the kernel |
| 119 | + 4- data member of the kernel |
| 120 | + 5- global data |
| 121 | + |
| 122 | + Things to take into account : |
| 123 | + - I want to be able to initialize the bounds externally. |
| 124 | + This can't be done if we choose 2-. |
| 125 | + - I want to be able to have different contexts depending where I use the |
| 126 | + predicate, this can't be done with 5- nor 3- nor 1-. |
| 127 | + - If I add a failure_counter, it should be at the same place as the context, |
| 128 | + and I should be able to access it from the outside. If we do that like |
| 129 | + triangulation, treating geom_traits as a data member of triangulatino, then |
| 130 | + it's ok. |
| 131 | + - So it remains 2 possibilities : |
| 132 | + a- data member of the predicate object. |
| 133 | + b- data member of the kernel. |
| 134 | + |
| 135 | + So 1-, 3-, 5- are out since we can't have different contexts. |
| 136 | + 2- and 4- are basically equivalent if we can access the context of an object |
| 137 | + via the kernel object (_gt in triangulation). So, Orientation_2_object(), |
| 138 | + for this particular kernel, would return a const ref to a data member of the |
| 139 | + kernel... |
| 140 | + So the good choice seems to be to have data stored in each predicate object, |
| 141 | + and having the kernel store a predicate object for each predicate. |
| 142 | + Then the orientation_2_object() simply returns a reference to it. |
| 143 | + |
| 144 | + Then it means algorithms should use one "global" object per predicate (e.g. |
| 145 | + one orientation object for a whole Triangulation). Except for cases where |
| 146 | + they actually want different contexts. |
| 147 | + |
| 148 | +// Additional kernel for storage type ? |
| 149 | +template <class SK, class EK, class IK = Cartesian<Interval_nt> > |
| 150 | +class Filtered_Point_2 |
| 151 | +{ |
| 152 | + const CK::Point_2 storage; |
| 153 | + |
| 154 | + // IK cached ? It doesn't make sense to do it lazily because it's going to |
| 155 | + // be used. BUT, what is worth is the case when the storage number type is |
| 156 | + // like a double : in this case, no approximation needs to be stored. |
| 157 | + |
| 158 | + IK::Point_2 app; |
| 159 | + const IK::Point_2 & approx() { return app; } |
| 160 | + |
| 161 | +#if No_cache_EK // via a traits parameter ? Have a generic caching mechanism ? |
| 162 | + EK::Point_2 exact() { return EK::Point_2(storage); } |
| 163 | +#else |
| 164 | + EK::Point_2 ex; |
| 165 | + const EK::Point_2 & exact { return ex; } |
| 166 | +#endif |
| 167 | +}; |
| 168 | + |
| 169 | +// For filtered constructions, we should be able to re-use the same predicates, |
| 170 | +// but have different constructions and objects Point_2... |
| 171 | +template <class EK, class IK = Cartesian<Interval> > |
| 172 | +class Filtered_kernel |
| 173 | +{ |
| 174 | +public: |
| 175 | + |
| 176 | + Filtered_kernel() |
| 177 | + : ik(), ek(), |
| 178 | + orientation_2_obj(ik.orientation_2_object(), ek.orientation_2_object()) |
| 179 | + // ... |
| 180 | + {} |
| 181 | + |
| 182 | + typedef CGAL::Filtered_Point_2<...> Point_2; |
| 183 | + // ... |
| 184 | + |
| 185 | + typedef CGAL::Filtered_p_Orientation<IK, EK> Orientation_2; |
| 186 | + Orientation_2 orientation_2_obj; |
| 187 | + const Orientation_2 & orientation_2_object() const |
| 188 | + { return orientation_2_obj; } |
| 189 | + |
| 190 | + const IK & get_ik() const { return ik; } |
| 191 | + const EK & get_ek() const { return ek; } |
| 192 | + |
| 193 | +private: |
| 194 | + |
| 195 | + EK ek; |
| 196 | + IK ik; |
| 197 | +}; |
| 198 | + |
| 199 | + Then you use this thing as a kernel : |
| 200 | + Filtered_kernel<Cartesian<leda_real> > |
| 201 | + eventually adding profiling template parameters... |
| 202 | + Just like we have at the NT level : Lazy_exact_nt<leda_real>. |
| 203 | + |
| 204 | + Maybe have a unique (Cartesian) kernel that includes filtering and caching |
| 205 | + capabilities which are toggleable by a simple traits ? The predicate objects |
| 206 | + would include the (currently so-called) update_epsilon() member functions... |
| 207 | + Or probably better, a filtering wrapper that can be used by homogeneous as |
| 208 | + well... |
| 209 | + |
| 210 | + |
0 commit comments