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