1 // -*- C++ -*-
2
3 /*
4 * GChemPaint arrows plugin
5 * curvedarrowtool.cc
6 *
7 * Copyright (C) 2004-2011 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 "curvedarrowtool.h"
27 #include <gccv/bezier-arrow.h>
28 #include <gccv/canvas.h>
29 #include <gcp/application.h>
30 #include <gcp/atom.h>
31 #include <gcp/bond.h>
32 #include <gcp/document.h>
33 #include <gcp/electron.h>
34 #include <gcp/mechanism-arrow.h>
35 #include <gcp/mechanism-step.h>
36 #include <gcp/mesomer.h>
37 #include <gcp/molecule.h>
38 #include <gcp/reaction-step.h>
39 #include <gcp/settings.h>
40 #include <gcp/theme.h>
41 #include <gcp/view.h>
42 #include <gcp/widgetdata.h>
43 #include <gcugtk/ui-builder.h>
44 #include <gtk/gtk.h>
45
46 using namespace std;
47
gcpCurvedArrowTool(gcp::Application * App,string Id)48 gcpCurvedArrowTool::gcpCurvedArrowTool (gcp::Application *App, string Id): gcp::Tool (App, Id)
49 {
50 m_Full = Id == "CurvedArrow";
51 if (m_Full) {
52 GOConfNode *node = go_conf_get_node (gcu::Application::GetConfDir (), "paint/plugins/arrows");
53 m_EndAtBondCenter = go_conf_get_bool (node, "end-at-new-bond-center");
54 go_conf_free_node (node);
55 } else
56 m_EndAtBondCenter = true;
57 }
58
~gcpCurvedArrowTool()59 gcpCurvedArrowTool::~gcpCurvedArrowTool ()
60 {
61 }
62
OnClicked()63 bool gcpCurvedArrowTool::OnClicked ()
64 {
65 gcp::Document *pDoc = m_pView->GetDoc ();
66 gcp::Theme *pTheme = pDoc->GetTheme ();
67 gccv::BezierArrow *arrow = NULL; // make g++ happy
68 m_SourceAux = NULL;
69 m_Target = NULL;
70 m_pData->UnselectAll ();
71 if (m_pObject)
72 switch (m_pObject->GetType ()) {
73 case gcu::FragmentType:
74 m_pObject = static_cast <gcp::Fragment *> (m_pObject)->GetAtom ();
75 case gcu::AtomType: {
76 gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
77 if (!AllowAsSource (atom))
78 return false;
79 // find if there is an explicit electron pair or single electron and use it as source
80 std::map <std::string, gcu::Object *>::iterator it;
81 gcu::Object *obj;
82 gcp::Electron *elec = NULL, *cur;
83 double x, y, angle, dist, a0;
84 atom->GetCoords (&x, &y);
85 x *= pTheme->GetZoomFactor ();
86 y *= pTheme->GetZoomFactor ();
87 a0 = (x == m_x0 && y == m_y0)? go_nan: atan2 (y - m_y0, m_x0 - x);
88 for (obj = atom->GetFirstChild (it); obj; obj = atom->GetNextChild (it)) {
89 cur = dynamic_cast <gcp::Electron *> (obj);
90 if (!cur || (m_Full && !cur->IsPair ()))
91 continue;
92 cur->GetPosition (&angle, &dist);
93 if (elec) {
94 } else {
95 elec = cur;
96 if (isnan (a0))
97 break;
98 }
99 }
100 if (elec) {
101 elec->GetPosition (&a0, &dist);
102 a0 *= M_PI / 180.;
103 m_pObject = elec;
104 }
105 // find the most probable bond or the nearest atom
106 if (atom->GetBondsNumber () > 0) {
107 m_Target = atom->GetBondAtAngle (a0);
108 m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
109 if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
110 m_Target = NULL;
111 if (m_Target == NULL)
112 break;
113 if (m_pObject == atom)
114 AtomToAdjBond ();
115 else
116 ElectronToAdjBond ();
117 } else {
118 // try to find a possible atom target
119 // TODO: implement and return true
120 m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
121 break;
122 }
123 break;
124 }
125 case gcu::BondType: {
126 gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
127 if (!AllowAsSource (bond))
128 return false;
129 m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
130 BondToAdjAtom ();
131 break;
132 }
133 default:
134 if (m_pObject->GetType () == gcp::ElectronType) {
135 gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
136 if (!AllowAsSource (elec))
137 return false;
138 gcu::Object *obj = m_pObject->GetParent ();
139 gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
140 double x, a0;
141 elec->GetPosition (&a0, &x);
142 a0 *= M_PI / 180.;
143 // find the most probable bond or the nearest atom
144 if (atom->GetBondsNumber () > 0) {
145 m_Target = atom->GetBondAtAngle (a0);
146 m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
147 if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
148 m_Target = NULL;
149 if (m_Target == NULL)
150 break;
151 ElectronToAdjBond ();
152 } else {
153 // try to find a possible atom target
154 // TODO: implement and return true
155 m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
156 }
157 break;
158 } else if (m_pObject->GetType () == gcp::MechanismArrowType) {
159 // select the arrow and show the control points
160 m_Item = arrow = static_cast <gccv::BezierArrow *> (dynamic_cast <gccv::ItemClient *> (m_pObject)->GetItem ());
161 m_x0 = -1; // to make clear we have not hit a control point yet
162 arrow->GetControlPoints (m_CPx0, m_CPy0, m_CPx1, m_CPy1, m_CPx2, m_CPy2, m_CPx3, m_CPy3);
163 // store control point half width in m_y0
164 m_y0 = arrow->GetLineWidth () * 2.5;
165 }
166 break;
167 }
168 if (arrow) {
169 arrow->SetShowControls (true);
170 arrow->SetLineWidth (pTheme->GetArrowWidth ());
171 arrow->SetLineColor (gcp::AddColor);
172 }
173 return true;
174 }
175
OnDrag()176 void gcpCurvedArrowTool::OnDrag ()
177 {
178 if (!m_Item || !m_pObject)
179 return; // this should not occur
180 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
181 gccv::BezierArrow *arrow = static_cast <gccv::BezierArrow *> (m_Item);
182 if (m_pObject->GetType () == gcp::MechanismArrowType) {
183 if (m_x0 < 0.) {
184 if (m_x >= m_CPx1 - m_y0 && m_x <= m_CPx1 + m_y0 && m_y >= m_CPy1 - m_y0 && m_y <= m_CPy1 + m_y0) {
185 // we are inside the second control point
186 m_x0 = m_x;
187 m_y0 = m_y;
188 // store original values
189 m_x1 = m_CPx1;
190 m_y1 = m_CPy1;
191 } else if (m_x >=m_CPx2 - m_y0 && m_x <= m_CPx2 + m_y0 && m_y >= m_CPy2 - m_y0 && m_y <= m_CPy2 + m_y0) {
192 // we are inside the third control point
193 m_x0 = m_x;
194 m_y0 = m_y;
195 m_Target = m_pObject; // to differentiate between the two control points
196 // store original values
197 m_x1 = m_CPx2;
198 m_y1 = m_CPy2;
199 }
200 } else {
201 // TODO: implement moving the control points
202 if (m_Target) {
203 m_CPx2 = m_x1 + m_x - m_x0;
204 m_CPy2 = m_y1 + m_y - m_y0;
205 } else {
206 m_CPx1 = m_x1 + m_x - m_x0;
207 m_CPy1 = m_y1 + m_y - m_y0;
208 }
209 arrow->SetControlPoints (m_CPx0, m_CPy0, m_CPx1, m_CPy1, m_CPx2, m_CPy2, m_CPx3, m_CPy3);
210 }
211 return;
212 }
213 gccv::Item *item = m_pView->GetCanvas ()->GetItemAt (m_x, m_y);
214 if (item) {
215 gcu::Object *cur = (item == m_Item)? m_Target: dynamic_cast <gcu::Object *> (item->GetClient ());
216 if (!cur) { // looks that this can happen, why?
217 arrow->SetControlPoints (0., 0., 0., 0., 0., 0., 0., 0.);
218 return;
219 }
220 if (cur->GetType () == gcu::FragmentType)
221 cur = static_cast <gcp::Fragment *> (cur)->GetAtom ();
222 if (cur == m_pObject) {
223 switch (m_pObject->GetType ()) {
224 case gcu::BondType: {
225 gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
226 if (!m_Target || m_Target == bond->GetAtom (0) || m_Target == bond->GetAtom (1)) {
227 m_Target = NULL;
228 BondToAdjAtom ();
229 return;
230 } else {
231 m_Target = NULL;
232 break; // TODO: implement
233 }
234 break;
235 }
236 case gcu::AtomType: {
237 gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
238 if (!m_Target) {
239 std::map< gcu::Atom *, gcu::Bond * >::iterator it;
240 double x, y;
241 atom->GetCoords (&x, &y);
242 x *= pTheme->GetZoomFactor ();
243 y *= pTheme->GetZoomFactor ();
244 double a0 = (x == m_x && y == m_y)? go_nan: atan2 (y - m_y, m_x - x);
245 if (isnan (a0))
246 m_Target = atom->GetFirstBond (it);
247 else
248 m_Target = atom->GetBondAtAngle (a0);
249 if (m_Target && AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
250 AtomToAdjBond ();
251 else
252 m_Target = NULL;
253 break;
254 } else {
255 gcp::Bond *bond = dynamic_cast <gcp::Bond *> (m_Target);
256 if (bond && AllowAsTarget (bond))
257 AtomToAdjBond ();
258 else
259 m_Target = NULL;
260 }
261 break;
262 }
263 default:
264 if (m_pObject->GetType () == gcp::ElectronType) {
265 gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
266 gcu::Object *obj = m_pObject->GetParent ();
267 gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
268 double x, a0;
269 elec->GetPosition (&a0, &x);
270 a0 *= M_PI / 180.;
271 // find the most probable bond or the nearest atom
272 if (atom->GetBondsNumber () > 0) {
273 m_Target = atom->GetBondAtAngle (a0);
274 if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
275 m_Target = NULL;
276 if (m_Target == NULL)
277 break;
278 ElectronToAdjBond ();
279 }
280 break;
281 } else if (m_pObject->GetType () == gcp::MechanismArrowType) {
282 break; // TODO: implement
283 } else {
284 m_Target = NULL;
285 break; // TODO: add more types
286 }
287 }
288 } else if (cur == m_Target) {
289 gcu::TypeId sid, tid;
290 sid = m_pObject->GetType ();
291 tid = m_Target->GetType ();
292 switch (sid) {
293 case gcu::BondType:
294 switch (tid) {
295 case gcu::BondType:
296 return; // nothing should change in that case
297 case gcu::AtomType: {
298 gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
299 if (m_Target == bond->GetAtom (0) || m_Target == bond->GetAtom (1))
300 BondToAdjAtom ();
301 break;
302 }
303 default:
304 break;
305 }
306 break;
307 case gcu::AtomType:
308 break;
309 default:
310 break;
311 }
312 return;
313 } else {
314 switch (cur->GetType ()) {
315 case gcu::BondType: {
316 gcp::Bond *bond = static_cast <gcp::Bond *> (cur);
317 if (!AllowAsTarget (bond)) {
318 m_Target = NULL;
319 break;
320 }
321 m_Target = bond;
322 switch (m_pObject->GetType ()) {
323 case gcu::BondType: {
324 BondToAdjBond ();
325 return;
326 }
327 case gcu::AtomType: {
328 AtomToAdjBond ();
329 return;
330 }
331 default:
332 if (m_pObject->GetType () == gcp::ElectronType) {
333 ElectronToAdjBond ();
334 return;
335 }
336 m_Target = NULL;
337 break;
338 }
339 break;
340 }
341 case gcu::AtomType: {
342 gcp::Atom *atom = static_cast <gcp::Atom *> (cur);
343 if (!AllowAsTarget (atom)) {
344 m_Target = NULL;
345 break;
346 }
347 m_Target = cur;
348 switch (m_pObject->GetType ()) {
349 case gcu::BondType: {
350 gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
351 if (cur == bond->GetAtom (0) || cur == bond->GetAtom (1))
352 BondToAdjAtom ();
353 else
354 BondToAtom ();
355 break;
356 }
357 case gcu::AtomType:
358 AtomToAtom ();
359 break;
360 default:
361 if (m_pObject->GetType () == gcp::ElectronType) {
362 ElectronToAtom ();
363 return;
364 }
365 m_Target = NULL;
366 }
367 break;
368 }
369 default:
370 m_Target = NULL;
371 break; // TODO: add more types
372 }
373 }
374 } else
375 m_Target = NULL;
376 if (m_Target == NULL)
377 arrow->SetControlPoints (0., 0., 0., 0., 0., 0., 0., 0.);
378 }
379
OnMotion()380 void gcpCurvedArrowTool::OnMotion ()
381 {
382 m_pData->UnselectAll ();
383 bool allowed = false;
384 if (m_pObject)
385 switch (m_pObject->GetType ()) {
386 case gcu::FragmentType:
387 m_pObject = static_cast <gcp::Fragment *> (m_pObject)->GetAtom ();
388 case gcu::AtomType:
389 allowed = AllowAsSource (reinterpret_cast <gcp::Atom *> (m_pObject));
390 break;
391 case gcu::BondType:
392 allowed = AllowAsSource (reinterpret_cast <gcp::Bond *> (m_pObject));
393 break;
394 default:
395 if (m_pObject->GetType () == gcp::ElectronType) {
396 if (m_Full)
397 allowed = static_cast <gcp::Electron *> (m_pObject)->IsPair ();
398 else
399 allowed = true;
400 } else if (m_pObject->GetType () == gcp::MechanismArrowType) {
401 if (m_Full)
402 allowed = static_cast <gcp::MechanismArrow *> (m_pObject)->GetPair ();
403 else
404 allowed = !static_cast <gcp::MechanismArrow *> (m_pObject)->GetPair ();
405 if (allowed)
406 static_cast <gccv::BezierArrow *> (dynamic_cast <gccv::ItemClient *> (m_pObject)->GetItem ())->SetShowControls (true);
407 }
408 break;
409 }
410 if (allowed)
411 m_pData->SetSelected (m_pObject);
412 gdk_window_set_cursor (gtk_widget_get_parent_window (m_pWidget), allowed? m_pApp->GetCursor (gcp::CursorPencil): m_pApp->GetCursor (gcp::CursorUnallowed));
413 }
414
OnLeaveNotify()415 void gcpCurvedArrowTool::OnLeaveNotify ()
416 {
417 m_pData->UnselectAll ();
418 }
419
OnRelease()420 void gcpCurvedArrowTool::OnRelease ()
421 {
422 m_pApp->ClearStatus ();
423 gcp::Document* pDoc = m_pView->GetDoc ();
424 if (m_Item) {
425 if (m_pObject->GetType () == gcp::MechanismArrowType) {
426 m_Item = NULL;
427 gcp::Operation *op = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
428 gcu::Object *obj = m_pObject->GetGroup ();
429 op->AddObject (obj, 0);
430 gcp::MechanismArrow *a = static_cast <gcp::MechanismArrow *> (m_pObject);
431 if (m_Target)
432 a->SetControlPoint (2, (m_CPx2 - m_CPx3) / m_dZoomFactor, (m_CPy2 - m_CPy3) / m_dZoomFactor);
433 else
434 a->SetControlPoint (1, (m_CPx1 - m_CPx0) / m_dZoomFactor, (m_CPy1 - m_CPy0) / m_dZoomFactor);
435 m_pView->Update (m_pObject);
436 op->AddObject (obj, 1);
437 pDoc->FinishOperation ();
438 return;
439 } else {
440 delete m_Item;
441 m_Item = NULL;
442 }
443 }
444 else
445 return;
446 if (!m_pObject || !m_Target || (m_CPx2 == 0. && m_CPy2 == 0.))
447 return;
448 gcp::Operation *op = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
449 gcu::Object *obj = m_pObject->GetGroup ();
450 op->AddObject (obj, 0);
451 if (obj != m_Target->GetGroup ())
452 op->AddObject (m_Target->GetGroup (), 0);
453 gcp::MechanismArrow *a = new gcp::MechanismArrow ();
454 gcp::Molecule *mol = static_cast <gcp::Molecule *> (m_Target->GetMolecule ());
455 // we suppose that the molecule is owned either by a mechanism step, a reaction step or the document
456 // anyway, the tool must refuse all other situations
457 obj = mol->GetParent ();
458 if (obj->GetType () == gcu::ReactantType)
459 obj = obj->GetParent ();
460 if (obj->GetType () == gcu::DocumentType) {
461 gcp::Molecule *mol_ = static_cast <gcp::Molecule *> (m_pObject->GetMolecule ());
462 if (mol_->GetParent () != obj) {// the source is already inside a mechanism step, NOT in a reaction step
463 obj = mol_->GetParent ();
464 obj->AddChild (mol);
465 } else {
466 gcp::MechanismStep *step = new gcp::MechanismStep ();
467 pDoc->AddChild (step);
468 obj = step;
469 obj->AddChild (mol);
470 if (mol !=mol_)
471 obj->AddChild (mol_);
472 }
473 }
474 pDoc->AddObject (a);
475 obj->AddChild (a);
476 a->SetSource (m_pObject);
477 a->SetSourceAux (m_SourceAux);
478 a->SetTarget (m_Target);
479 a->SetPair (m_Full);
480 a->SetControlPoint (1, m_CPx1 / m_dZoomFactor, m_CPy1 / m_dZoomFactor);
481 a->SetControlPoint (2, m_CPx2 / m_dZoomFactor, m_CPy2 / m_dZoomFactor);
482 if (m_SetEnd)
483 a->SetEndAtNewBondCenter (m_EndAtBondCenter);
484 a->EmitSignal (gcp::OnChangedSignal);
485 m_pView->Update (a);
486
487 gcu::Object *group = obj->GetGroup ();
488 op->AddObject ((group)? group: obj, 1);
489 pDoc->FinishOperation ();
490 }
491
on_default(GtkToggleButton * button)492 static void on_default (GtkToggleButton *button)
493 {
494 GOConfNode *node = go_conf_get_node (gcu::Application::GetConfDir (), "paint/plugins/arrows");
495 go_conf_set_bool (node, "end-at-new-bond-center", gtk_toggle_button_get_active (button));
496 go_conf_free_node (node);
497 }
498
on_end_toggled(GtkToggleButton * button,gcpCurvedArrowTool * tool)499 static void on_end_toggled (GtkToggleButton *button, gcpCurvedArrowTool *tool)
500 {
501 tool->SetEndAtBondCenter (gtk_toggle_button_get_active (button));
502 }
503
GetPropertyPage()504 GtkWidget *gcpCurvedArrowTool::GetPropertyPage ()
505 {
506 if (!m_Full)
507 return NULL;
508 gcugtk::UIBuilder *builder = new gcugtk::UIBuilder (UIDIR"/curvedarrowtool.ui", GETTEXT_PACKAGE);
509 GtkWidget *b = builder->GetWidget ("target-btn");
510 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), m_EndAtBondCenter);
511 g_signal_connect (G_OBJECT (b), "toggled", G_CALLBACK (on_end_toggled), this);
512 GtkWidget *w = builder->GetWidget ("default");
513 g_signal_connect_swapped (w, "clicked", G_CALLBACK (on_default), b);
514 GtkWidget *res = builder->GetRefdWidget ("curved-arrow-grid");
515 delete builder;
516 return res;
517 }
518
AllowAsSource(gcp::Atom * atom)519 bool gcpCurvedArrowTool::AllowAsSource (gcp::Atom *atom)
520 {
521 if (!atom->HasAvailableElectrons (m_Full))
522 return false;
523 std::set <gcu::Object *>::iterator i;
524 gcu::Object* obj = atom->GetFirstLink (i);
525 while (obj && obj->GetType () != gcp::MechanismArrowType)
526 obj = atom->GetNextLink (i);
527 if (obj) {
528 if (m_Full)
529 return false;
530 // for homolytic cleavage, the bond must be the source for both arrows
531 gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
532 if (arrow->GetPair ())
533 return false;
534 // only one arrow should be there
535 obj = atom->GetNextLink (i);
536 if (obj && obj->GetType () == gcp::MechanismArrowType)
537 return false;
538 }
539 return true;
540 }
541
AllowAsTarget(gcp::Atom * atom)542 bool gcpCurvedArrowTool::AllowAsTarget (gcp::Atom *atom)
543 {
544 if (atom == m_pObject || atom == m_pObject->GetParent ())
545 return false;
546 if (m_pObject->GetType () == gcu::AtomType && static_cast <gcp::Atom *> (m_pObject)->GetBond (atom))
547 return false;
548 if (m_pObject->GetType () == gcp::ElectronType) {
549 gcu::Object *obj = m_pObject->GetParent ();
550 gcp::Atom *atom0 = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
551 if (atom0->GetBond (atom))
552 return false;
553 }
554 if (m_pObject->GetType () == gcu::BondType && !m_Full) {
555 // check if there is any existant arrow with the same source and target
556 std::set <gcu::Object *>::iterator i;
557 gcu::Object* obj = atom->GetFirstLink (i);
558 while (obj && obj->GetType () != gcp::MechanismArrowType)
559 obj = atom->GetNextLink (i);
560 if (obj && static_cast <gcp::MechanismArrow *> (obj)->GetSource () == m_pObject
561 && static_cast <gcp::MechanismArrow *> (obj)->GetTarget () == atom)
562 return false;
563 }
564 // now check that molecules are not in incompatible groups
565 gcu::Object *obj1 = m_pObject->GetMolecule (), *obj2 = atom->GetMolecule ();
566 if (obj1 != obj2) {
567 // otherwise, no problem
568 // get molecules parents
569 obj1 = obj1->GetParent ();
570 obj2 = obj2->GetParent ();
571 // if they belong to two different reaction step, return false
572 if ((obj1->GetType () == gcp::ReactionStepType || obj2->GetType () == gcp::ReactionStepType) && obj1 != obj2)
573 return false;
574 // no arrows between a mesomer and anything else
575 if (obj1->GetType () == gcp::MesomerType || obj2->GetType () == gcp::MesomerType)
576 return false;
577 // otherwise we request that the two molecules have the same parent or parents inside one another (might be a bug)
578 if (obj1 != obj2 && obj1->GetParent () != obj2->GetParent () && obj1->GetParent () != obj2 && obj1 != obj2->GetParent ())
579 return false;
580 }
581 return atom->AcceptNewBonds () || atom->GetBondsNumber ();
582 }
583
AllowAsSource(gcp::Bond * bond)584 bool gcpCurvedArrowTool::AllowAsSource (gcp::Bond *bond)
585 {
586 std::set <gcu::Object *>::iterator i;
587 gcu::Object* obj = bond->GetFirstLink (i);
588 while (obj && obj->GetType () != gcp::MechanismArrowType)
589 obj = bond->GetNextLink (i);
590 if (obj) {
591 if (m_Full)
592 return false;
593 // for homolytic cleavage, the bond must be the source for both arrows
594 gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
595 if (arrow->GetPair () || arrow->GetSource () != bond)
596 return false;
597 // only one arrow should be there
598 obj = bond->GetNextLink (i);
599 if (obj && obj->GetType () == gcp::MechanismArrowType)
600 return false;
601 }
602 return true;
603 }
604
AllowAsTarget(gcp::Bond * bond)605 bool gcpCurvedArrowTool::AllowAsTarget (gcp::Bond *bond)
606 {
607 std::set <gcu::Object *>::iterator i;
608 gcu::Object* obj = bond->GetFirstLink (i);
609 while (obj && obj->GetType () != gcp::MechanismArrowType)
610 obj = bond->GetNextLink (i);
611 if (obj) {
612 if (m_Full)
613 return false;
614 // for homolytic cleavage, the bond must be the source for both arrows
615 gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
616 if (arrow->GetPair () || arrow->GetTarget () != bond || arrow->GetSource () == m_pObject)
617 return false;
618 // only one arrow should be there
619 obj = bond->GetNextLink (i);
620 if (obj && obj->GetType () == gcp::MechanismArrowType)
621 return false;
622 }
623 // now check if the source is an adjacent bond or atom
624 switch (m_pObject->GetType ()) {
625 case gcu::BondType: {
626 gcp::Bond *bond0 = static_cast <gcp::Bond *> (m_pObject);
627 if (bond->GetAtom (bond0->GetAtom (0)) == NULL && bond->GetAtom (bond0->GetAtom (1)) == NULL)
628 return false;
629 break;
630 }
631 case gcu::AtomType: {
632 gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
633 if (bond->GetAtom (0) != atom && bond->GetAtom (1) != atom)
634 return false;
635 break;
636 }
637 default:
638 if (m_pObject->GetType () == gcp::ElectronType) {
639 gcu::Object *obj = m_pObject->GetParent ();
640 gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
641 if (bond->GetAtom (0) != atom && bond->GetAtom (1) != atom)
642 return false;
643 break;
644 }
645 return false;
646 }
647 return true;
648 }
649
AllowAsSource(gcp::Electron * elec)650 bool gcpCurvedArrowTool::AllowAsSource (gcp::Electron *elec)
651 {
652 if (m_Full && !elec->IsPair ())
653 return false;
654 std::set <gcu::Object *>::iterator i;
655 gcu::Object* obj = elec->GetFirstLink (i);
656 while (obj && obj->GetType () != gcp::MechanismArrowType)
657 obj = elec->GetNextLink (i);
658 if (obj) {
659 if (m_Full)
660 return false;
661 // for homolytic cleavage, the electron must be the source for both arrows
662 gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
663 if (arrow->GetPair ())
664 return false;
665 // only one arrow should be there
666 obj = elec->GetNextLink (i);
667 if (obj && obj->GetType () == gcp::MechanismArrowType)
668 return false;
669 }
670 return true;
671 }
672
AtomToAdjBond()673 void gcpCurvedArrowTool::AtomToAdjBond ()
674 {
675 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy, s = 1.;
676 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
677 gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
678 gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
679 *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
680 if (end == m_pObject) {
681 end = start;
682 start = static_cast <gcp::Atom *> (m_pObject);
683 }
684 start->GetCoords (&x0, &y0);
685 end->GetCoords (&x1, &y1);
686 // convert to canvas coordinates
687 x0 *= m_dZoomFactor;
688 y0 *= m_dZoomFactor;
689 x1 *= m_dZoomFactor;
690 y1 *= m_dZoomFactor;
691 dx = y1 - y0;
692 dy = x0 - x1;
693 l = hypot (dx, dy);
694 dx /= l;
695 dy /= l;
696 // try to find on which side we are
697 if ((m_x - x0) * dx + (m_y - y0) * dy < 0) {
698 dx = -dx;
699 dy = -dy;
700 s = -1;
701 }
702 x3 = ((x0 + x1) / 2 + dx * pTheme->GetPadding ());
703 y3 = ((y0 + y1) / 2 + dy * pTheme->GetPadding ());
704 x3 /= m_dZoomFactor;
705 y3 /= m_dZoomFactor;
706 bond->AdjustPosition (x3, y3);
707 x3 *= m_dZoomFactor;
708 y3 *= m_dZoomFactor;
709 l /= 2.;
710 m_CPx1 = m_CPx2 = l * dx;
711 m_CPy1 = m_CPy2 = l * dy;
712 double a = atan2 (-m_CPy1, m_CPx1) * 180. / M_PI;
713 if (start->GetPosition (a, x0, y0)) {
714 // convert to canvas coordinates
715 x0 *= m_dZoomFactor;
716 y0 *= m_dZoomFactor;
717 if (!m_Full) {
718 x0 -= 2. * dy * s;
719 y0 += 2. * dx * s;
720 x3 += 2. * dy * s;
721 y3 -= 2. * dx * s;
722 }
723 x1 = x0 + m_CPx1;
724 y1 = y0 + m_CPy1;
725 x2 = x3 + m_CPx1;
726 y2 = y3 + m_CPy1;
727 m_CPx0 = x0;
728 m_CPy0 = y0;
729 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
730 } else
731 x0 = y0 = m_CPx1 = m_CPx2 = m_CPy1 = m_CPy2 = x3 = y3 = 0;
732 m_SetEnd = false;
733 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
734 }
735
AtomToAtom()736 void gcpCurvedArrowTool::AtomToAtom ()
737 {
738 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy;
739 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
740 gcp::Atom *start = static_cast <gcp::Atom *> (m_pObject),
741 *end = static_cast <gcp::Atom *> (m_Target);
742 start->GetCoords (&x0, &y0);
743 end->GetCoords (&x3, &y3);
744 x0 *= m_dZoomFactor;
745 y0 *= m_dZoomFactor;
746 x3 *= m_dZoomFactor;
747 y3 *= m_dZoomFactor;
748 dx = x3 - x0;
749 dy = y3 - y0;
750 l = hypot (dx, dy);
751 dx /= l;
752 dy /= l;
753 l = pTheme->GetBondLength () * m_dZoomFactor;
754 if (start->GetBondsNumber () == 0) {
755 // Set values to m_CPx1 and m_CPy1 according to the cursor position
756 if ((m_x - x0) * (y3 - y0) - (m_y - y0) * (x3 - x0) < 0)
757 {
758 m_CPx1 = -l * dy;
759 m_CPy1 = l * dx;
760 } else {
761 m_CPx1 = l * dy;
762 m_CPy1 = -l * dx;
763 }
764 }
765 double angle = -atan2 (m_CPy1, m_CPx1) * 180. / M_PI;
766 if (start->GetPosition (angle, x0, y0)) {
767 // convert to canvas coordinates
768 m_CPx0 = x0 *= m_dZoomFactor;
769 m_CPy0 = y0 *= m_dZoomFactor;
770 x1 = x0 + m_CPx1;
771 y1 = y0 + m_CPy1;
772 if (!m_Full || m_EndAtBondCenter) {
773 x3 = (x3 + x0) / 2.;
774 y3 = (y3 + y0) / 2.;
775 if (!m_Full) {
776 x3 -= 2. * dx;
777 y3 -= 2. * dy;
778 }
779 if (m_CPx1 * dy - m_CPy1 * dx < 0.) {
780 m_CPx2 = -dy * l;
781 m_CPy2 = dx * l;
782 } else {
783 m_CPx2 = dy * l;
784 m_CPy2 = -dx * l;
785 }
786 } else {
787 angle = -atan2 (m_CPy2, m_CPx2) * 180. / M_PI;
788 if (end->GetPosition (angle, x3, y3)) {
789 x3 *= m_dZoomFactor;
790 y3 *= m_dZoomFactor;
791 m_CPx2 = -dx * l;
792 m_CPy2 = -dy * l;
793 } else
794 goto ata_err;
795 }
796 x2 = x3 + m_CPx2;
797 y2 = y3 + m_CPy2;
798 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
799 } else
800 ata_err:
801 x0 = y0 = m_CPx1 = m_CPx2= m_CPy0 = m_CPy1 = x3 = y3 = 0;
802 m_SetEnd = m_Full;
803 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
804 }
805
BondToAdjAtom()806 void gcpCurvedArrowTool::BondToAdjAtom ()
807 {
808 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy, s = 1.;
809 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
810 gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
811 gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
812 *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
813 if (start == m_Target) {
814 start = end;
815 end = static_cast <gcp::Atom *> (m_Target);
816 } else if (m_Target && end != m_Target)
817 return; // should not occur
818 start->GetCoords (&x0, &y0);
819 end->GetCoords (&x1, &y1);
820 // convert to canvas coordinates
821 x0 *= m_dZoomFactor;
822 y0 *= m_dZoomFactor;
823 x1 *= m_dZoomFactor;
824 y1 *= m_dZoomFactor;
825 if (!m_Target) {
826 // use the atom nearest to the mouse pointer
827 x2 = hypot (x0 - m_x, y0 - m_y);
828 y2 = hypot (x1 - m_x, y1 - m_y);
829 if (x2 < y2) {
830 m_Target = start;
831 x2 = x0;
832 x0 = x1;
833 x1 = x2;
834 y2 = y0;
835 y0 = y1;
836 y1 = y2;
837 } else
838 m_Target = end;
839 }
840 if (AllowAsTarget (static_cast <gcp::Atom *> (m_Target))) {
841 x2 = m_x - x0;
842 y2 = m_y - y0;
843 x1 -= x0;
844 y1 -= y0; // use x3 as bond length for now
845 l = hypot (x1, y1);
846 dx = x1 / l;
847 dy = y1 / l;
848 // everything being normalized, vector product sign will say on which side we are
849 // and scalar product where we are. Let's use x3 for scalar and y3 for vector products.
850 x2 /= l;
851 y2 /= l;
852 x3 = dx * x2 + dy * y2;
853 y3 = dx * y2 - dy * x2;
854 x0 += (x1 /= 2.);
855 y0 += (y1 /= 2.);
856 if (!m_Full) {
857 x0 += 2. * dx;
858 y0 += 2. * dy;
859 }
860 x2 = dx;
861 if (y3 < 0) {
862 dx = dy;
863 dy = -x2;
864 } else {
865 dx = -dy;
866 dy = x2;
867 s = -1.;
868 }
869 // add some padding
870 x0 += dx * pTheme->GetPadding ();
871 y0 += dy * pTheme->GetPadding ();
872 x0 /= m_dZoomFactor;
873 y0 /= m_dZoomFactor;
874 bond->AdjustPosition (x0, y0);
875 m_CPx0 = x0 *= m_dZoomFactor;
876 m_CPy0 = y0 *= m_dZoomFactor;
877 l /= 2.;
878 m_CPx1 = dx * l;
879 m_CPy1 = dy * l;
880 x1 = x0 + m_CPx1;
881 y1 = y0 + m_CPy1;
882 // adjust end position
883 double angle = -atan2 (dy, dx) * 180. / M_PI;
884 if (static_cast <gcp::Atom *> (m_Target)->GetPosition (angle, x3, y3)) {
885 // convert to canvas coordinates
886 x3 *= m_dZoomFactor;
887 y3 *= m_dZoomFactor;
888 // set second control point
889 l += pTheme->GetArrowHeadA ();
890 m_CPx2 = dx * l;
891 m_CPy2 = dy * l;
892 if (!m_Full) {
893 x3 += 2. * dy * s;
894 y3 -= 2. * dx * s;
895 }
896 x2 = x3 + m_CPx2;
897 y2 = y3 + m_CPy2;
898 m_LastTarget = m_Target;
899 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
900 } else
901 x0 = y0 = x1 = y1 = x2 = y2 = x3 = y3 = m_CPx2 = m_CPy2 = 0;
902 } else
903 m_Target = NULL;
904 m_SetEnd = false;
905 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
906 }
907
BondToAdjBond()908 void gcpCurvedArrowTool::BondToAdjBond ()
909 {
910 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy;
911 double x, y, x_, y_;
912 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
913 gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
914 gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
915 *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
916 start->GetCoords (&x, &y);
917 end->GetCoords (&x_, &y_);
918 // convert to canvas coordinates
919 x *= m_dZoomFactor;
920 y *= m_dZoomFactor;
921 x_ *= m_dZoomFactor;
922 y_ *= m_dZoomFactor;
923 if (static_cast <gcp::Bond *> (m_pObject)->GetAtom (start) == NULL) {
924 gcp::Atom *buf = start;
925 start = end;
926 end = buf;
927 // also exchange coordinates
928 x0 = x;
929 x = x_;
930 x_ = x0;
931 x0 = y;
932 y = y_;
933 y_ = x0;
934 }
935 // start is the common atom, x and y its coordinates
936 x0 = m_CPx0;
937 y0 = m_CPy0;
938 x1 = x0 + m_CPx1;
939 y1 = y0 + m_CPy1;
940 x3 = (x + x_) / 2.;
941 y3 = (y + y_) / 2.;
942 dx = y_ - y;
943 dy = x - x_;
944 l = hypot (dx, dy);
945 dx /= l;
946 dy /= l;
947 if (!m_Full) {
948 x3 += 2. * dy;
949 y3 -= 2. * dx;
950 }
951 if ((m_CPx1 * (y0 - y) - m_CPy1 * (x0 - x)) * (dx * (y3 - y) - dy * (x3 - x)) > 0.) {
952 dx = -dx;
953 dy = -dy;
954 }
955 x3 += dx * pTheme->GetPadding ();
956 y3 += dy * pTheme->GetPadding ();
957 x3 /= m_dZoomFactor;
958 y3 /= m_dZoomFactor;
959 bond->AdjustPosition (x3, y3);
960 x3 *= m_dZoomFactor;
961 y3 *= m_dZoomFactor;
962 l /= 2.;
963 l += pTheme->GetArrowHeadA ();
964 m_CPx2 = dx * l;
965 m_CPy2 = dy * l;
966 x2 = x3 + m_CPx2;
967 y2 = y3 + m_CPy2;
968 m_SourceAux = NULL;
969 m_SetEnd = false;
970 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
971 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
972 }
973
BondToAtom()974 void gcpCurvedArrowTool::BondToAtom ()
975 {
976 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy;
977 gcp::Atom *start = static_cast <gcp::Atom *> (m_LastTarget),
978 *end = static_cast <gcp::Atom *> (m_Target);
979 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
980 start->GetCoords (&x0, &y0);
981 end->GetCoords (&x, &y);
982 x0 *= m_dZoomFactor;
983 y0 *= m_dZoomFactor;
984 x *= m_dZoomFactor;
985 y *= m_dZoomFactor;
986 dx = x - x0;
987 dy = y - y0;
988 if (!m_Full || m_EndAtBondCenter) {
989 double l = hypot (dx, dy);
990 dx /= l;
991 dy /= l;
992 x3 = (x + x0) / 2.;
993 y3 = (y + y0) / 2.;
994 if (!m_Full) {
995 x3 -= 2. * dx;
996 y3 -= 2. * dy;
997 }
998 if (m_CPx1 * dy - m_CPy1 * dx < 0.) {
999 dx = -dx;
1000 dy = -dy;
1001 }
1002 x2 = x3 + (m_CPx2 = dy * pTheme->GetBondLength () * m_dZoomFactor);
1003 y2 = y3 + (m_CPy2 = -dx * pTheme->GetBondLength () * m_dZoomFactor);
1004 x0 = m_CPx0;
1005 y0 = m_CPy0;
1006 x1 = x0 + m_CPx1;
1007 y1 = y0 + m_CPy1;
1008 } else {
1009 a = atan2 (dy, -dx) * 180. / M_PI;
1010 if (end->GetPosition (a, x3, y3)) {
1011 x3 *= m_dZoomFactor;
1012 y3 *= m_dZoomFactor;
1013 x2 = (x0 + x) / 2.;
1014 y2 = (y0 + y) / 2.;
1015 m_CPx2 = x2 - x3;
1016 m_CPy2 = y2 - y3;
1017 x0 = m_CPx0;
1018 y0 = m_CPy0;
1019 x1 = x0 + m_CPx1;
1020 y1 = y0 + m_CPy1;
1021 } else
1022 x0 = y0 = x1 = y1 = m_CPx2 = m_CPy2 = 0.;
1023 }
1024 m_SourceAux = m_LastTarget;
1025 m_SetEnd = m_Full;
1026 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1027 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1028 }
1029
ElectronToAdjBond()1030 void gcpCurvedArrowTool::ElectronToAdjBond ()
1031 {
1032 gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
1033 gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
1034 gcp::Atom *atom = static_cast <gcp::Atom *> (elec->GetParent ()),
1035 *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
1036 *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
1037 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
1038 if (end == atom) {
1039 end = start;
1040 start = atom;
1041 }
1042 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy, l;
1043 elec->GetPosition (&a, &dx);
1044 a *= M_PI / 180.;
1045 if (dx != 0.) {
1046 x = dx * cos (a);
1047 y = -dx * sin (a);
1048 x *= m_dZoomFactor;
1049 y *= m_dZoomFactor;
1050 } else {
1051 start->GetRelativePosition (a * 180. / M_PI, x, y);
1052 x *= m_dZoomFactor;
1053 y *= m_dZoomFactor;
1054 x += 2. * cos (a);
1055 y -= 2. * sin (a);
1056 }
1057 start->GetCoords (&x0, &y0);
1058 end->GetCoords (&x3, &y3);
1059 x0 *= m_dZoomFactor;
1060 y0 *= m_dZoomFactor;
1061 x3 *= m_dZoomFactor;
1062 y3 *= m_dZoomFactor;
1063 dx = x3 - x0;
1064 dy = y3 - y0;
1065 l = hypot (x, y);
1066 // store x and y in x1 and y1
1067 x1 = x;
1068 y1 = y;
1069 x = x / l;
1070 y = y / l;
1071 l = pTheme->GetBondLength () * m_dZoomFactor / 2.;
1072 m_CPx1 = x * l;
1073 m_CPy1 = y * l;
1074 l = hypot (dx, dy);
1075 dx /= l;
1076 dy /= l;
1077 // try to find on which side we are
1078 x3 = (x0 + x3) / 2.;
1079 y3 = (y0 + y3) / 2.;
1080 if (!m_Full) {
1081 if (elec->IsPair ()) {
1082 if ((y3 - y0) * x - (x3 - x0) * y < 0) {
1083 x0 += y * 2.;
1084 y0 -= x * 2.;
1085 } else {
1086 x0 -= y * 2.;
1087 y0 += x * 2.;
1088
1089 }
1090 }
1091 x3 -= 2. * dx;
1092 y3 -= 2. * dy;
1093 }
1094 x0 += x1 + pTheme->GetPadding () * cos (a);
1095 y0 += y1 - pTheme->GetPadding () * sin (a);
1096 x1 = x0 + m_CPx1;
1097 y1 = y0 + m_CPy1;
1098 if ((m_CPy1) * dx - (m_CPx1) * dy > 0) {
1099 dx = -dx;
1100 dy = -dy;
1101 }
1102 x3 += dy * pTheme->GetPadding ();
1103 y3 += -dx * pTheme->GetPadding ();
1104 x3 /= m_dZoomFactor;
1105 y3 /= m_dZoomFactor;
1106 bond->AdjustPosition (x3, y3);
1107 x3 *= m_dZoomFactor;
1108 y3 *= m_dZoomFactor;
1109 m_CPx2 = l * dy;
1110 m_CPy2 = -l * dx;
1111 x2 = x3 + m_CPx2;
1112 y2 = y3 + m_CPy2;
1113 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1114 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1115 }
1116
ElectronToAtom()1117 void gcpCurvedArrowTool::ElectronToAtom ()
1118 {
1119 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy, l;
1120 gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
1121 gcp::Atom *start = static_cast <gcp::Atom *> (elec->GetParent ()),
1122 *end = static_cast <gcp::Atom *> (m_Target);
1123 gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
1124 elec->GetPosition (&a, &dx);
1125 a *= M_PI / 180.;
1126 if (dx != 0.) {
1127 x = dx * cos (a);
1128 y = -dx * sin (a);
1129 x *= m_dZoomFactor;
1130 y *= m_dZoomFactor;
1131 } else {
1132 start->GetRelativePosition (a * 180. / M_PI, x, y);
1133 x *= m_dZoomFactor;
1134 y *= m_dZoomFactor;
1135 x += 2. * cos (a);
1136 y -= 2. * sin (a);
1137 }
1138 start->GetCoords (&x0, &y0);
1139 end->GetCoords (&x3, &y3);
1140 x0 *= m_dZoomFactor;
1141 y0 *= m_dZoomFactor;
1142 x3 *= m_dZoomFactor;
1143 y3 *= m_dZoomFactor;
1144 dx = x3 - x0;
1145 dy = y3 - y0;
1146 x0 += x + pTheme->GetPadding () * cos (a);
1147 y0 += y - pTheme->GetPadding () * sin (a);
1148 l = hypot (x, y) / pTheme->GetBondLength () / m_dZoomFactor * 2.;
1149 x1 = x0 + (m_CPx1 = x / l);
1150 y1 = y0 + (m_CPy1 = y / l);
1151 l = hypot (dx, dy);
1152 dx /= l;
1153 dy /= l;
1154 // try to find on which side we are
1155 if ((m_CPy1) * dx - (m_CPx1) * dy > 0) {
1156 dx = -dx;
1157 dy = -dy;
1158 }
1159 if (!m_Full || m_EndAtBondCenter) {
1160 x3 = (x3 + x0) / 2.;
1161 y3 = (y3 + y0) / 2.;
1162 if (!m_Full) {
1163 x3 -= 2. * dx;
1164 y3 -= 2. * dy;
1165 }
1166 x2 = x3 + (m_CPx2 = dy * pTheme->GetBondLength () * m_dZoomFactor);
1167 y2 = y3 + (m_CPy2 = -dx * pTheme->GetBondLength () * m_dZoomFactor);
1168 } else {
1169 a = atan2 (dy, -dx) * 180. / M_PI;
1170 x2 = (x0 + x3) / 2.;
1171 y2 = (y0 + y3) / 2.;
1172 if (end->GetPosition (a, x3, y3)) {
1173 x3 *= m_dZoomFactor;
1174 y3 *= m_dZoomFactor;
1175 m_CPx2 = x2 - x3;
1176 m_CPy2 = y2 - y3;
1177 } else
1178 x0 = y0 = x1 = y1 = m_CPx2 = m_CPy2 = 0.;
1179 }
1180 static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1181 static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1182 }
1183