1 // -*- C++ -*-
2
3 /*
4 * Gnome Chemistry Utils
5 * gccv/squiggle.h
6 *
7 * Copyright (C) 2008-2014 Jean Bréfort <jean.brefort@normalesup.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 */
24
25 #include "config.h"
26 #include "canvas.h"
27 #include "group.h"
28 #include "squiggle.h"
29 #include <cmath>
30
31 namespace gccv {
32
Squiggle(Canvas * canvas,double xstart,double ystart,double xend,double yend)33 Squiggle::Squiggle (Canvas *canvas, double xstart, double ystart, double xend, double yend):
34 LineItem (canvas), m_xstart (0.), m_ystart (0.), m_xend (0.), m_yend (0.),
35 m_Width (0.), m_Step (0.)
36 {
37 SetPosition (xstart, ystart, xend, yend);
38 }
39
Squiggle(Group * parent,double xstart,double ystart,double xend,double yend,ItemClient * client)40 Squiggle::Squiggle (Group *parent, double xstart, double ystart, double xend, double yend, ItemClient *client):
41 LineItem (parent, client), m_xstart (0.), m_ystart (0.), m_xend (0.), m_yend (0.),
42 m_Width (0.), m_Step (0.)
43 {
44 SetPosition (xstart, ystart, xend, yend);
45 }
46
~Squiggle()47 Squiggle::~Squiggle ()
48 {
49 }
50
SetPosition(double xstart,double ystart,double xend,double yend)51 void Squiggle::SetPosition (double xstart, double ystart, double xend, double yend)
52 {
53 Invalidate ();
54 m_xstart = xstart;
55 m_ystart = ystart;
56 m_xend = xend;
57 m_yend = yend;
58 BoundsChanged ();
59 Invalidate ();
60 }
61
Distance(double x,double y,Item ** item) const62 double Squiggle::Distance (double x, double y, Item **item) const
63 {
64 double d1, d2;
65 d1 = (m_xend - m_xstart) * (x - m_xstart) + (m_yend - m_ystart) * (y - m_ystart);
66 d2 = (m_xend - m_xstart) * (x - m_xend) + (m_yend - m_ystart) * (y - m_yend);
67 if (item)
68 *item = const_cast <Squiggle *> (this);
69 if (d1 >= 0. && d2 >= 0.) {
70 x -= m_xend;
71 y -= m_yend;
72 return sqrt (x * x + y * y);
73 }
74 if (d1 <= 0. && d2 <= 0.) {
75 x -= m_xstart;
76 y -= m_ystart;
77 return sqrt (x * x + y * y);
78 }
79 x -= m_xstart;
80 y -= m_ystart;
81 d1 = m_xend - m_xstart;
82 d2 = m_yend - m_ystart;
83 return fabs (d1 * y - d2 * x) / sqrt (d1 * d1 + d2 * d2) - m_Width / 2.;
84 }
85
Draw(cairo_t * cr,G_GNUC_UNUSED bool is_vector) const86 void Squiggle::Draw (cairo_t *cr, G_GNUC_UNUSED bool is_vector) const
87 {
88 GOColor color = GetEffectiveLineColor ();
89 if (color != 0) {
90 double dx = m_xend - m_xstart, dy = m_yend - m_ystart, length = sqrt (dx * dx + dy * dy);
91 dx /= length;
92 dy /= length; // we now have a unit vector.
93 // evaluate the number of waves
94 int n = floor (length / m_Step);
95 // now the steps between consecutive control points on the same size
96 length /= n;
97 double xstep = length * dx, ystep = length * dy, x = xstep / 1.5, y = ystep / 1.5;
98 // evaluate the first left and right control points.
99 double width = m_Width / 2. - GetLineWidth () / 2.;
100 double x0 = m_xstart + dy * width + xstep / 2.,
101 y0 = m_ystart - dx * width + ystep / 2.,
102 x1 = m_xstart - dy * width + 1.5 * xstep,
103 y1 = m_ystart + dx * width + 1.5 * ystep, t;
104 cairo_set_line_width (cr, GetLineWidth ());
105 cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
106 cairo_move_to (cr, m_xstart, m_ystart);
107 cairo_curve_to (cr, m_xstart + x, m_ystart + y, x0 - x, y0 - y, x0, y0);
108 for (int i = 1; i < n; i++) {
109 cairo_curve_to (cr, x0 + x, y0 + y, x1 - x, y1 - y, x1, y1);
110 t = x0;
111 x0 = x1;
112 x1 = t + 2 * xstep;
113 t = y0;
114 y0 = y1;
115 y1 = t + 2 * ystep;
116 }
117 cairo_curve_to (cr, x0 + x, y0 + y, m_xend - x, m_yend - y, m_xend, m_yend);
118 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (color));
119 cairo_stroke (cr);
120 }
121 }
122
UpdateBounds()123 void Squiggle::UpdateBounds ()
124 {
125 double lw = m_Width / 2., lh;
126 double angle = atan2 (m_yend - m_ystart, m_xend - m_xstart);
127 // TODO: take line cap into account
128 lh = fabs (lw * cos (angle));
129 lw = fabs (lw * sin (angle));
130 if (m_xstart < m_xend) {
131 m_x0 = m_xstart - lw;
132 m_x1 = m_xend + lw;
133 } else {
134 m_x0 = m_xend - lw;
135 m_x1 = m_xstart + lw;
136 }
137 if (m_ystart < m_yend) {
138 m_y0 = m_ystart - lh;
139 m_y1 = m_yend + lh;
140 } else {
141 m_y0 = m_yend - lh;
142 m_y1 = m_ystart + lh;
143 }
144 Item::UpdateBounds ();
145 }
146
Move(double x,double y)147 void Squiggle::Move (double x, double y)
148 {
149 Invalidate ();
150 m_xstart += x;
151 m_ystart += y;
152 m_xend += x;
153 m_yend += y;
154 BoundsChanged ();
155 Invalidate ();
156 }
157
158 }
159