Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
pencil.cpp
Go to the documentation of this file.
1/*
2 * sb-to-bez Toy - Tests conversions from sbasis to cubic bezier.
3 *
4 * Copyright 2007 jf barraud.
5 * 2008 njh
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
14 *
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
20 *
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
25 *
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
29 *
30 */
31
32// mainly experimental atm...
33// do not expect to find anything understandable here atm.
34
35#include <2geom/d2.h>
36#include <2geom/sbasis.h>
39
40#include <toys/path-cairo.h>
42
43#define ZERO 1e-7
44
45using std::vector;
46using namespace Geom;
47using namespace std;
48
49#include <stdio.h>
50#include <gsl/gsl_poly.h>
51
52void cairo_pw(cairo_t *cr, Piecewise<SBasis> p, double hscale=1., double vscale=1.) {
53 for(unsigned i = 0; i < p.size(); i++) {
54 D2<SBasis> B;
55 B[0] = Linear(150+p.cuts[i]*hscale, 150+p.cuts[i+1]*hscale);
56 B[1] = Linear(450) - p[i]*vscale;
57 cairo_d2_sb(cr, B);
58 }
59}
60
61//===================================================================================
62
64naive_sb_seg_to_bez(Piecewise<D2<SBasis> > const &M,double t0,double t1){
65
67 Point M0 = M(t0);
68 Point dM0 = dM(t0)*(t1-t0);
69 Point M1 = M(t1);
70 Point dM1 = dM(t1)*(t1-t0);
72 for (unsigned dim=0; dim<2; dim++){
73 SBasis r(2, Linear());
74 r[0] = Linear(M0[dim],M1[dim]);
75 r[1] = Linear(M0[dim]-M1[dim]+dM0[dim],-(M0[dim]-M1[dim]+dM1[dim]));
76 result[dim] = r;
77 }
78 return result;
79}
80
82sb_seg_to_bez(Piecewise<D2<SBasis> > const &M,double t0,double t1){
83 Point M0,dM0,d2M0,M1,dM1,d2M1,A0,A1;
84 Piecewise<D2<SBasis> > dM,d2M;
85 dM=derivative(M);
86 d2M=derivative(dM);
87 M0 =M(t0);
88 M1 =M(t1);
89 dM0 =dM(t0);
90 dM1 =dM(t1);
91 d2M0=d2M(t0);
92 d2M1=d2M(t1);
93 A0=M(t0);
94 A1=M(t1);
95
96 std::vector<D2<SBasis> > candidates = cubics_fitting_curvature(M0,M1,dM0,dM1,d2M0,d2M1);
97 if (candidates.size()==0){
98 return D2<SBasis>(SBasis(M0[X],M1[X]),SBasis(M0[Y],M1[Y])) ;
99 }
100 double maxlength = -1;
101 unsigned best = 0;
102 for (unsigned i=0; i<candidates.size(); i++){
103 double l = length(candidates[i]);
104 if ( l < maxlength || maxlength < 0 ){
105 maxlength = l;
106 best = i;
107 }
108 }
109 return candidates[best];
110}
112
114
116 Piecewise<D2<SBasis> >const&B) {
119 //double h_dist = bnds.dimensions().length();
120//0 is in the rect!, TODO:gain factor ~2 for free.
121// njh: not really, the benefit is actually rather small.
122 double h_dist = 0;
123 if(bnds)
124 h_dist = bnds->extent();
125 return h_dist ;
126 } else {
127 Rect bnds = *bounds_fast(A - B);
128 return max(bnds.min().length(), bnds.max().length());
129 }
130}
131
132int recursive_curvature_fitter(cairo_t* cr, Piecewise<D2<SBasis> > const &f, double t0, double t1, double precision) {
133 if (t0>=t1) return 0;//TODO: fix me...
134 if (t0+0.001>=t1) return 0;//TODO: fix me...
135
136 //TODO: don't re-compute derivative(f) at each try!!
137 D2<SBasis> k_bez = sb_seg_to_bez(f,t0,t1);
138
139 if(k_bez[0].size() > 1 and k_bez[1].size() > 1) {
141 s *= (t1-t0)/arcLengthSb(k_bez).segs.back().at1();
142 s += t0;
143 double h_dist = goal_function(compose(f,s), Piecewise<D2<SBasis> >(k_bez));
144 if(h_dist < precision) {
145 cairo_save(cr);
146 cairo_set_line_width (cr, 0.93);
147 cairo_set_source_rgba (cr, 0.7, 0.0, 0.0, 1);
148 draw_handle(cr, k_bez.at0());
149 cairo_d2_sb(cr, k_bez);
150 cairo_stroke(cr);
151 cairo_restore(cr);
152 return 1;
153 }
154 }
155 //TODO: find a better place where to cut (at the worst fit?).
156 return recursive_curvature_fitter(cr, f, t0, (t0+t1)/2, precision) +
157 recursive_curvature_fitter(cr, f, (t0+t1)/2, t1, precision);
158}
159
160double single_curvature_fitter(Piecewise<D2<SBasis> > const &f, double t0, double t1) {
161 if (t0>=t1) return 0;//TODO: fix me...
162 if (t0+0.001>=t1) return 0;//TODO: fix me...
163
164 D2<SBasis> k_bez = sb_seg_to_bez(f,t0,t1);
165
166 if(k_bez[0].size() > 1 and k_bez[1].size() > 1) {
168 s *= (t1-t0)/arcLengthSb(k_bez).segs.back().at1();
169 s += t0;
170 return goal_function(compose(f,s), Piecewise<D2<SBasis> >(k_bez));
171 }
172 return 1e100;
173}
174
175struct quadratic_params
176{
177 Piecewise<D2<SBasis> > const *f;
178 double t0, precision;
179};
180
181
182double quadratic (double x, void *params) {
183 struct quadratic_params *p
184 = (struct quadratic_params *) params;
185
186 return single_curvature_fitter(*p->f, p->t0, x) - p->precision;
187}
188
189#include <stdio.h>
190#include <gsl/gsl_errno.h>
191#include <gsl/gsl_math.h>
192#include <gsl/gsl_roots.h>
193
194
195int sequential_curvature_fitter(cairo_t* cr, Piecewise<D2<SBasis> > const &f, double t0, double t1, double precision) {
196 if(t0 >= t1) return 0;
197
198 double r = t1;
199 if(single_curvature_fitter(f, t0, t1) > precision) {
200 int status;
201 int iter = 0, max_iter = 100;
202 const gsl_root_fsolver_type *T;
203 gsl_root_fsolver *s;
204 gsl_function F;
205 struct quadratic_params params = {&f, t0, precision};
206
207 F.function = &quadratic;
208 F.params = &params;
209
210 T = gsl_root_fsolver_brent;
211 s = gsl_root_fsolver_alloc (T);
212 gsl_root_fsolver_set (s, &F, t0, t1);
213
214 do
215 {
216 iter++;
217 status = gsl_root_fsolver_iterate (s);
218 r = gsl_root_fsolver_root (s);
219 double x_lo = gsl_root_fsolver_x_lower (s);
220 double x_hi = gsl_root_fsolver_x_upper (s);
221 status = gsl_root_test_interval (x_lo, x_hi,
222 0, 0.001);
223
224
225 }
226 while (status == GSL_CONTINUE && iter < max_iter);
227
228 double x_lo = gsl_root_fsolver_x_lower (s);
229 double x_hi = gsl_root_fsolver_x_upper (s);
230 printf ("%5d [%.7f, %.7f] %.7f %.7f\n",
231 iter, x_lo, x_hi,
232 r,
233 x_hi - x_lo);
234 gsl_root_fsolver_free (s);
235 }
236 D2<SBasis> k_bez = sb_seg_to_bez(f,t0,r);
237
238 cairo_save(cr);
239 cairo_set_line_width (cr, 0.93);
240 cairo_set_source_rgba (cr, 0.7, 0.0, 0.0, 1);
241 draw_handle(cr, k_bez.at0());
242 cairo_d2_sb(cr, k_bez);
243 cairo_stroke(cr);
244 cairo_restore(cr);
245
246 if(r < t1)
247 return sequential_curvature_fitter(cr, f, r, t1, precision) + 1;
248 return 1;
249}
250
251
252class SbToBezierTester: public Toy {
253 //std::vector<Slider> sliders;
254 PointHandle adjuster, adjuster2, adjuster3;
255 std::vector<Toggle> toggles;
257
258 void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override {
259 cairo_save(cr);
260
261 cairo_set_source_rgba (cr, 0., 0., 0., 1);
262 cairo_set_line_width (cr, 0.5);
263
264 if(!mouses.empty()) {
265 cairo_move_to(cr, mouses[0]);
266 for(auto & mouse : mouses) {
267 cairo_line_to(cr, mouse);
268 }
269 cairo_stroke(cr);
270 }
271 adjuster2.pos[0]=150;
272 adjuster2.pos[1]=std::min(std::max(adjuster2.pos[1],150.),450.);
273 cairo_move_to(cr, 150, 150);
274 cairo_line_to(cr, 150, 450);
275 cairo_stroke(cr);
276 ostringstream val_s;
277 double scale0=(450-adjuster2.pos[1])/300;
278 double curve_precision = pow(10, scale0*5-2);
279 val_s << curve_precision;
280 draw_text(cr, adjuster2.pos, val_s.str().c_str());
281
282
283 Piecewise<D2<SBasis> > f_as_pw = stroke;
284 cairo_pw_d2_sb(cr, f_as_pw);
285 cairo_stroke(cr);
286 if(!stroke.empty()) {
287 f_as_pw = arc_length_parametrization(f_as_pw);
288 int segs = 0;
289 goal_function_type = toggles[1].on;
290 if(toggles[0].on)
291 segs = sequential_curvature_fitter(cr, f_as_pw, 0, f_as_pw.cuts.back(), curve_precision);
292 else {
293 segs = recursive_curvature_fitter(cr, f_as_pw, 0, f_as_pw.cuts.back(),curve_precision);
294 }
295 *notify << " total segments: "<< segs <<"\n";
296 }
297 cairo_restore(cr);
298 Point p(25, height - 50), d(50,25);
299 toggles[0].bounds = Rect(p, p + d);
300 p+= Point(75, 0);
301 toggles[1].bounds = Rect(p, p + d);
302 draw_toggles(cr, toggles);
303 Toy::draw(cr, notify, width, height, save,timer_stream);
304 }
305
306public:
307 void key_hit(unsigned keyval, unsigned modifiers) override {
308 if(keyval == 's') toggles[0].toggle();
309 redraw();
310 }
311 vector<Point> mouses;
312 int mouse_drag;
313
314 void mouse_pressed(Geom::Point const &pos, unsigned button, unsigned modifiers) override {
315 toggle_events(toggles, pos, button);
316 Toy::mouse_pressed(pos, button, modifiers);
317 if(!selected) {
318 mouse_drag = 1;
319 mouses.clear();
320 }
321 }
322
323
324 void mouse_moved(Geom::Point const &pos, unsigned modifiers) override
325 {
326 if (mouse_drag) {
327 mouses.emplace_back(pos);
328 redraw();
329 } else {
330 Toy::mouse_moved(pos, modifiers);
331 }
332 }
333
334 void mouse_released(Geom::Point const &pos, unsigned button, unsigned modifiers) override
335 {
336 mouse_drag = 0;
337 stroke.clear();
338 stroke.push_cut(0);
339 Path pth;
340 for (unsigned i = 2; i < mouses.size(); i += 2) {
341 pth.append(QuadraticBezier(mouses[i-2], mouses[i-1], mouses[i]));
342 }
343 stroke = pth.toPwSb();
344 Toy::mouse_released(pos, button, modifiers);
345 }
346
347 SbToBezierTester() {
348 adjuster.pos = Geom::Point(150+300*uniform(),150+300*uniform());
349 handles.push_back(&adjuster);
350 adjuster2.pos = Geom::Point(150,300);
351 handles.push_back(&adjuster2);
352 adjuster3.pos = Geom::Point(450,300);
353 handles.push_back(&adjuster3);
354 toggles.emplace_back("Seq", false);
355 toggles.emplace_back("Linfty", true);
356 }
357};
358
359int main(int argc, char **argv) {
360 init(argc, argv, new SbToBezierTester);
361 return 0;
362}
363
364/*
365 Local Variables:
366 mode:c++
367 c-file-style:"stroustrup"
368 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
369 indent-tabs-mode:nil
370 fill-column:99
371 End:
372*/
373// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
Basic intersection routines.
int main()
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Point at0() const
Definition d2.h:121
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Function that interpolates linearly between two values.
Definition linear.h:55
Range of real numbers that can be empty.
Definition interval.h:199
Sequence of contiguous curves, aka spline.
Definition path.h:353
Piecewise< D2< SBasis > > toPwSb() const
Definition path.cpp:388
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
Function defined as discrete pieces.
Definition piecewise.h:71
unsigned size() const
Definition piecewise.h:131
std::vector< double > cuts
Definition piecewise.h:75
std::vector< T > segs
Definition piecewise.h:76
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Polynomial in symmetric power basis.
Definition sbasis.h:70
Geom::Point pos
virtual void mouse_pressed(Geom::Point const &pos, unsigned button, unsigned modifiers)
virtual void mouse_moved(Geom::Point const &pos, unsigned modifiers)
vector< Handle * > handles
Handle * selected
virtual void mouse_released(Geom::Point const &pos, unsigned button, unsigned modifiers)
virtual void save(FILE *f)
virtual void key_hit(unsigned keyval, unsigned modifiers)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
Css & result
Geom::IntPoint size
Colors::Color stroke
Lifts one dimensional objects into 2D.
BezierCurveN< 2 > QuadraticBezier
Quadratic (order 2) Bezier curve.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
T pow(T const &t, int n)
Integer exponentiation for transforms.
Definition transforms.h:98
Various utility functions.
Definition affine.h:22
Coord length(LineSegment const &seg)
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Definition sbasisN.h:158
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< D2< SBasis > > arc_length_parametrization(D2< SBasis > const &M, unsigned order=3, double tol=.01)
std::vector< D2< SBasis > > cubics_fitting_curvature(Point const &M0, Point const &M1, Point const &dM0, Point const &dM1, double d2M0xdM0, double d2M1xdM1, int insist_on_speed_signs=1, double epsilon=1e-5)
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
OptInterval bounds_fast(Bezier const &b)
Definition bezier.cpp:305
Piecewise< SBasis > arcLengthSb(D2< SBasis > const &M, double tol=.01)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
STL namespace.
void cairo_line_to(cairo_t *cr, Geom::Point p1)
struct _cairo cairo_t
Definition path-cairo.h:16
void draw_handle(cairo_t *cr, Geom::Point h)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
void cairo_d2_sb(cairo_t *cr, Geom::D2< Geom::SBasis > const &p)
void cairo_pw_d2_sb(cairo_t *cr, Geom::Piecewise< Geom::D2< Geom::SBasis > > const &p)
double goal_function(Piecewise< D2< SBasis > >const &A, Piecewise< D2< SBasis > >const &B)
Definition pencil.cpp:115
double quadratic(double x, void *params)
Definition pencil.cpp:182
void cairo_pw(cairo_t *cr, Piecewise< SBasis > p, double hscale=1., double vscale=1.)
Definition pencil.cpp:52
double single_curvature_fitter(Piecewise< D2< SBasis > > const &f, double t0, double t1)
Definition pencil.cpp:160
int sequential_curvature_fitter(cairo_t *cr, Piecewise< D2< SBasis > > const &f, double t0, double t1, double precision)
Definition pencil.cpp:195
D2< SBasis > naive_sb_seg_to_bez(Piecewise< D2< SBasis > > const &M, double t0, double t1)
Definition pencil.cpp:64
int recursive_curvature_fitter(cairo_t *cr, Piecewise< D2< SBasis > > const &f, double t0, double t1, double precision)
Definition pencil.cpp:132
int goal_function_type
Definition pencil.cpp:113
D2< SBasis > sb_seg_to_bez(Piecewise< D2< SBasis > > const &M, double t0, double t1)
Definition pencil.cpp:82
int goal_function_type
two-dimensional geometric operators.
Conversion between SBasis and Bezier basis polynomials.
Polynomial in symmetric power basis (S-basis)
double height
double width
void draw_text(cairo_t *cr, Geom::Point pos, const char *txt, bool bottom=false, const char *fontdesc="Sans")
double uniform()
void toggle_events(std::vector< Toggle > &ts, Geom::Point const &pos, unsigned button)
void cairo_set_source_rgba(cairo_t *cr, colour c)
void draw_toggles(cairo_t *cr, std::vector< Toggle > &ts)
void redraw()
void init(int argc, char **argv, Toy *t, int width=600, int height=600)