// -*- C++ -*- /* * GChemPaint library * bond.cc * * Copyright (C) 2001-2007 Jean Bréfort * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "gchempaint-config.h" #include "bond.h" #include "atom.h" #include "fragment.h" #include "cycle.h" #include "settings.h" #include "document.h" #include "theme.h" #include "view.h" #include "widgetdata.h" #include "libgcpcanvas/gcp-canvas-group.h" #include "libgcpcanvas/gcp-canvas-line.h" #include "libgcpcanvas/gcp-canvas-bpath.h" #include "libgcpcanvas/gcp-canvas-polygon.h" #include #include #include gcpBond::gcpBond(): Bond() { m_CoordsCalc = false; m_type = NormalBondType; m_level = 0; } gcpBond::gcpBond(gcpAtom* first, gcpAtom* last, unsigned char order): Bond(first, last, order) { m_CoordsCalc = false; m_type = NormalBondType; m_level = 0; } gcpBond::~gcpBond() { } void gcpBond::SetType(gcpBondType type) { m_type = type; m_CoordsCalc = false; if (m_type != NormalBondType) m_order = 1; } double gcpBond::GetAngle2D(gcpAtom* pAtom) { double x1, y1, x2, y2; m_Begin->GetCoords(&x1, &y1); m_End->GetCoords(&x2, &y2); x2 -= x1; y2 -= y1; double length = square(x2) + square(y2); if (length == 0.0) return HUGE_VAL; if (pAtom == m_Begin) return atan2(- y2, x2) * 90 / 1.570796326794897; else if (pAtom == m_End) return atan2(y2, - x2) * 90 / 1.570796326794897; return HUGE_VAL; } double gcpBond::GetAngle2DRad(gcpAtom* pAtom) { double x1, y1, x2, y2; m_Begin->GetCoords(&x1, &y1); m_End->GetCoords(&x2, &y2); x2 -= x1; y2 -= y1; double length = square(x2) + square(y2); if (length == 0.0) return HUGE_VAL; if (pAtom == m_Begin) return atan2(- y2, x2); else if (pAtom == m_End) return atan2(y2, - x2); return HUGE_VAL; } gcpCycle* gcpBond::GetFirstCycle(std::list::iterator& i, gcpCycle * pCycle) { i = m_Cycles.begin(); return GetNextCycle(i, pCycle); } gcpCycle* gcpBond::GetNextCycle(std::list::iterator& i, gcpCycle * pCycle) { if (*i == pCycle) i++; if (i == m_Cycles.end()) return NULL; pCycle = *i; i++; return pCycle; } bool gcpBond::IsInCycle(gcpCycle* pCycle) { std::list::iterator i; for (i = m_Cycles.begin(); i != m_Cycles.end(); i++) if ((*i) == pCycle) return true; return false; } bool gcpBond::GetLine2DCoords(unsigned Num, double* x1, double* y1, double* x2, double* y2) { if ((Num == 0) || (Num > m_order)) return false; if (!m_CoordsCalc) { gcpTheme *Theme = dynamic_cast (GetDocument ())->GetTheme (); m_Begin->GetCoords(x1, y1); m_End->GetCoords(x2, y2); double dx = *x2 - *x1, dy = *y2 - *y1; double l = sqrt(square(dx) + square(dy)); double BondDist = Theme->GetBondDist () / Theme->GetZoomFactor (); dx *= (BondDist / l); dy *= (BondDist / l); if (m_order & 1) { m_coords[0] = *x1; m_coords[1] = *y1; m_coords[2] = *x2; m_coords[3] = *y2; if (m_order == 3) { m_coords[4] = *x1 - dy; m_coords[5] = *y1 + dx; m_coords[6] = *x2 - dy; m_coords[7] = *y2 + dx; m_coords[8] = *x1 + dy; m_coords[9] = *y1 - dx; m_coords[10] = *x2 + dy; m_coords[11] = *y2 - dx; } } else if ((m_order == 2) && IsCyclic()) { m_coords[0] = *x1; m_coords[1] = *y1; m_coords[2] = *x2; m_coords[3] = *y2; gcpCycle* pCycle; if (IsCyclic() > 1) { //Search prefered cycle std::list::iterator i = m_Cycles.begin(); pCycle = *i; for (; i != m_Cycles.end(); i++) if (pCycle->IsBetterForBonds(*i)) pCycle = *i; } else pCycle = m_Cycles.front(); double a0 = atan2(*y1 - *y2, *x2 - *x1), a1, a2; pCycle->GetAngles2D(this, &a1, &a2); if (sin(a0 - a1) * sin (a0 - a2) > 0) { double sign = sin(a0 - a1) > 0.0 ? 1.0 : -1.0; double tanb = fabs(tan((3.141592653589793238462643 - a0 + a1) / 2)), cosa = cos(a0), sina = sin(a0); m_coords[4] = *x1 + BondDist * cosa * tanb - dy * sign; m_coords[5] = *y1 + dx * sign - BondDist * sina * tanb; tanb = fabs(tan((a2 - a0) / 2)); m_coords[6] = *x2 - BondDist * cosa * tanb - dy * sign; m_coords[7] = *y2 + dx * sign + BondDist * sina * tanb; } else { m_coords[0] = *x1 - dy / 2; m_coords[1] = *y1 + dx / 2; m_coords[2] = *x2 - dy / 2; m_coords[3] = *y2 + dx / 2; m_coords[4] = *x1 + dy / 2; m_coords[5] = *y1 - dx / 2; m_coords[6] = *x2 + dy / 2; m_coords[7] = *y2 - dx / 2; } } else { m_coords[0] = *x1 - dy / 2; m_coords[1] = *y1 + dx / 2; m_coords[2] = *x2 - dy / 2; m_coords[3] = *y2 + dx / 2; m_coords[4] = *x1 + dy / 2; m_coords[5] = *y1 - dx / 2; m_coords[6] = *x2 + dy / 2; m_coords[7] = *y2 - dx / 2; if (m_order == 4) { m_coords[8] = *x1 - dy * 1.5; m_coords[9] = *y1 + dx * 1.5; m_coords[10] = *x2 - dy * 1.5; m_coords[11] = *y2 + dx * 1.5; m_coords[12] = *x1 + dy * 1.5; m_coords[13] = *y1 - dx * 1.5; m_coords[14] = *x2 + dy * 1.5; m_coords[15] = *y2 - dx * 1.5; } } m_CoordsCalc = true; } Num--; Num *= 4; *x1 = m_coords[Num++]; *y1 = m_coords[Num++]; *x2 = m_coords[Num++]; *y2 = m_coords[Num++]; return true; } Object* gcpBond::GetAtomAt(double x, double y, double z) { double x1, y1; m_Begin->GetCoords(&x1, &y1); if ((fabs(x - x1) < 10) && (fabs(y - y1) < 10)) return m_Begin; m_End->GetCoords(&x1, &y1); if ((fabs(x - x1) < 10) && (fabs(y - y1) < 10)) return m_End; return NULL; } bool gcpBond::SaveNode(xmlDocPtr xml, xmlNodePtr node) { switch(m_type) { case UpBondType: xmlNewProp(node, (xmlChar*)"type", (xmlChar*)"up"); break; case DownBondType: xmlNewProp(node, (xmlChar*)"type", (xmlChar*)"down"); break; case ForeBondType: xmlNewProp(node, (xmlChar*)"type", (xmlChar*)"fore"); break; case UndeterminedBondType: xmlNewProp(node, (xmlChar*)"type", (xmlChar*)"undetermined"); break; default: break; } if (m_level != 0) { char *buf = g_strdup_printf ("%d", m_level); xmlNewProp(node, (xmlChar*)"level", (xmlChar*) buf); g_free (buf); } return true; } bool gcpBond::LoadNode (xmlNodePtr node) { char* buf; buf = (char*) xmlGetProp (node, (xmlChar*) "type"); if (!buf) SetType (NormalBondType); else if (!strcmp (buf, "up")) SetType (UpBondType); else if (!strcmp (buf, "down")) SetType (DownBondType); else if (!strcmp (buf, "fore")) SetType (ForeBondType); else if (!strcmp (buf, "undetermined")) SetType (UndeterminedBondType); else SetType (NormalBondType); if (buf) xmlFree (buf); buf = (char*) xmlGetProp (node, (xmlChar*) "level"); if (buf) { m_level = atoi (buf); xmlFree (buf); } return true; } void gcpBond::IncOrder(int n) { if (!((gcpAtom*)GetAtom(0))->AcceptNewBonds() || !((gcpAtom*)GetAtom(1))->AcceptNewBonds()) m_order = 1; else { Bond::IncOrder(n); if (m_order == 4) m_order = 1;//avoid quadruple bonds for now } m_CoordsCalc = false; ((gcpAtom*)m_Begin)->Update(); ((gcpAtom*)m_End)->Update(); } double gcpBond::GetDist(double x, double y) { double x1, y1, x2, y2, l, d1, d2; gcpTheme *Theme = dynamic_cast (GetDocument ())->GetTheme (); double BondDist = Theme->GetBondDist () / Theme->GetZoomFactor (); m_Begin->GetCoords(&x1, &y1); m_End->GetCoords(&x2, &y2); d1 = (x2 - x1) * (x1 - x) + (y2 - y1) * (y1 - y); d2 = (x2 - x1) * (x2 - x) + (y2 - y1) * (y2 - y); if ((d1 < 0.0) && (d2 < 0.0)) return sqrt(square(x2 - x) + square(y2 - y)); if ((d1 > 0.0) && (d2 > 0.0)) return sqrt(square(x1 - x) + square(y1 - y)); x2 -= x1; y2 -= y1; x -= x1; y -= y1; l = fabs(x2 * y - y2 * x) / sqrt(square(x2) + square(y2)); return (l < BondDist * (m_order - 1)) ? 0 : l - BondDist * (m_order - 1); } void gcpBond::AddCycle(gcpCycle* pCycle) { m_Cycles.push_back(pCycle); if ((m_order == 2) && m_CoordsCalc) SetDirty(); } void gcpBond::RemoveCycle(gcpCycle* pCycle) { m_Cycles.remove(pCycle); if ((m_order == 2) && m_CoordsCalc) SetDirty(); } void gcpBond::SetDirty() { gcpDocument *pDoc = (gcpDocument*)GetDocument(); if (pDoc) pDoc->NotifyDirty(this); m_CoordsCalc = false; } void gcpBond::RemoveAllCycles() { m_Cycles.clear(); if (m_order == 2) { gcpDocument *pDoc = (gcpDocument*)GetDocument(); if (pDoc) pDoc->NotifyDirty(this); m_CoordsCalc = false; } } void gcpBond::Move(double x, double y, double z) { m_CoordsCalc = false; } void gcpBond::Transform2D(Matrix2D& m, double x, double y) { m_CoordsCalc = false; } void gcpBond::Revert() { m_CoordsCalc = false; Atom* pAtom = m_Begin; m_Begin = m_End; m_End = pAtom; } double gcpBond::Get2DLength() { double x1, y1, x2, y2; m_Begin->GetCoords(&x1, &y1); m_End->GetCoords(&x2, &y2); return sqrt(square(x1 - x2) + square(y1 - y2)); } void gcpBond::SetSelected(GtkWidget* w, int state) { if (!m_order) return; gcpWidgetData* pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data"); GnomeCanvasGroup* group = pData->Items[this]; gchar* color; switch (state) { case SelStateUnselected: color = Color; break; case SelStateSelected: color = SelectColor; break; case SelStateUpdating: color = AddColor; break; case SelStateErasing: color = DeleteColor; break; default: color = Color; break; } void *obj = g_object_get_data(G_OBJECT(group), "path"); switch (GetType()) { case NormalBondType: case UndeterminedBondType: g_object_set(obj, "outline_color", color, NULL); break; case ForeBondType: case UpBondType: case DownBondType: g_object_set(obj, "fill_color", color, NULL); break; } } void gcpBond::Add(GtkWidget* w) { if (!w) return; gcpWidgetData* pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data"); gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); Atom *pAtom0, *pAtom1; if (!(pAtom0 = GetAtom(0))) return; if (!(pAtom1 = GetAtom(1))) return; unsigned char order = GetOrder(); if (order == 0) return; GnomeCanvasGroup* group; GnomeCanvasItem* item = NULL; pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data"); group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(pData->Group, gnome_canvas_group_ext_get_type(), NULL)); g_signal_connect(G_OBJECT(group), "event", G_CALLBACK(on_event), w); g_object_set_data(G_OBJECT(group), "object", this); bool result = false; if (m_Crossing.size () > 0) { map::iterator i, iend = m_Crossing.end (); for (i = m_Crossing.begin (); i != iend; i++) if ((result |= (*i).second.is_before)) break; } GnomeCanvasPathDef *path = NULL; if (result) { path = BuildCrossingPathDef (pData); if (path) { GnomeCanvasItem *item = (m_type == NormalBondType || m_type == UndeterminedBondType)? gnome_canvas_item_new ( group, gnome_canvas_bpath_ext_get_type (), "bpath", path, "outline_color", "white", "width_units", pTheme->GetBondWidth () * 3., NULL): gnome_canvas_item_new ( group, gnome_canvas_bpath_ext_get_type(), "bpath", path, "fill_color", "white", "width_units", 0., NULL); g_object_set_data (G_OBJECT (group), "back", item); g_object_set_data (G_OBJECT (item), "object", this); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); /* now bring to front, FIXME: not secure ! */ gnome_canvas_item_lower_to_bottom (item); gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (group)); Atom *pAtom = GetAtom (0); if (pAtom->GetZ () != 6 || static_cast (pAtom)->GetShowSymbol ()) gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (pData->Items[pAtom])); pAtom = GetAtom (1); if (pAtom->GetZ () != 6 || static_cast (pAtom)->GetShowSymbol ()) gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (pData->Items[pAtom])); gnome_canvas_path_def_unref (path); } } path = BuildPathDef (pData); switch (GetType()) { case NormalBondType: case UndeterminedBondType: item = gnome_canvas_item_new( group, gnome_canvas_bpath_ext_get_type(), "bpath", path, "outline_color", (pData->IsSelected(this))? SelectColor: Color, "width_units", pTheme->GetBondWidth (), NULL); break; case ForeBondType: case UpBondType: case DownBondType: item = gnome_canvas_item_new( group, gnome_canvas_bpath_ext_get_type(), "bpath", path, "fill_color", (pData->IsSelected(this))? SelectColor: Color, "width_units", 0., NULL); break; } gnome_canvas_path_def_unref (path); g_object_set_data(G_OBJECT(group), "path", item); g_object_set_data(G_OBJECT(item), "object", this); g_signal_connect(G_OBJECT(item), "event", G_CALLBACK(on_event), w); pData->Items[this] = group; if (pAtom0->GetParent()->GetType() == FragmentType) gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[pAtom0->GetParent()])); else if (pAtom0->GetZ() != 6 || static_cast (pAtom0)->GetShowSymbol ()) gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[pAtom0])); else gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM(pData->Items[pAtom0])); map::iterator i; Object* electron = pAtom0->GetFirstChild (i); while (electron){ gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[electron])); electron = pAtom0->GetNextChild (i); } if (pAtom1->GetParent()->GetType() == FragmentType) gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[pAtom1->GetParent()])); else if (pAtom1->GetZ() != 6 || static_cast (pAtom1)->GetShowSymbol ()) gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[pAtom1])); else gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM (pData->Items[pAtom1])); electron = pAtom1->GetFirstChild (i); while (electron){ gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(pData->Items[electron])); electron = pAtom1->GetNextChild (i); } } bool gcpBond::ReplaceAtom(gcpAtom* oldAtom, gcpAtom* newAtom) { if (oldAtom == m_Begin) { if (m_End) m_End->RemoveBond(this); m_Begin = newAtom; if (m_Begin && m_End) m_End->AddBond(this); } else if (oldAtom == m_End) { if (m_Begin) m_Begin->RemoveBond(this); m_End = newAtom; if (m_Begin && m_End) m_Begin->AddBond(this); } return true; } GnomeCanvasPathDef* gcpBond::BuildPathDef (gcpWidgetData* pData) { double x1, y1, x2, y2, x, y, dx, dy, dx1, dy1; int i, n; GnomeCanvasPathDef* path = gnome_canvas_path_def_new (); gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); switch (GetType()) { case NormalBondType: i = 1; while (GetLine2DCoords(i++, &x1, &y1, &x2, &y2)) { gnome_canvas_path_def_moveto (path, x1 * pTheme->GetZoomFactor (), y1 * pTheme->GetZoomFactor ()); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor (), y2 * pTheme->GetZoomFactor ()); } break; case UpBondType: GetLine2DCoords(1, &x1, &y1, &x2, &y2); gnome_canvas_path_def_moveto (path, x1 * pTheme->GetZoomFactor (), y1 * pTheme->GetZoomFactor ()); x = sqrt(square(x2 - x1) + square(y2 - y1)); dx = (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2; dy = (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2; gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () + dx, y2 * pTheme->GetZoomFactor () + dy); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () -dx, y2 * pTheme->GetZoomFactor () -dy); gnome_canvas_path_def_closepath (path); break; case DownBondType: { double xi[8]; GetLine2DCoords(1, &x1, &y1, &x2, &y2); x1 *= pTheme->GetZoomFactor (); y1 *= pTheme->GetZoomFactor (); x2 *= pTheme->GetZoomFactor (); y2 *= pTheme->GetZoomFactor (); x = sqrt(square(x2 - x1) + square(y2 - y1)); n = int(floor(x / (pTheme->GetHashDist () + pTheme->GetHashWidth ()))); dx1 = (x2 - x1) / x * pTheme->GetHashWidth (); dy1 = (y2 - y1) / x * pTheme->GetHashWidth (); dx = (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2; dy = (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2; gnome_canvas_path_def_moveto (path, xi[0] = x1 + dx, xi[1] = y1 + dy); gnome_canvas_path_def_lineto (path, xi[2] = x1 - dx, xi[3] = y1 - dy); dx *= (1 - pTheme->GetHashWidth () / x); dy *= (1 - pTheme->GetHashWidth () / x); gnome_canvas_path_def_lineto (path, xi[4] = x1 + dx1 - dx, xi[5] = y1 + dy1 - dy); gnome_canvas_path_def_lineto (path, xi[6] = x1 + dx1 + dx, xi[7] = y1 + dy1 + dy); gnome_canvas_path_def_lineto (path, xi[0], xi[1]); gnome_canvas_path_def_closepath_current (path); dx = (x2 - x1) / x * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) - (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2 * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) / x; dy = (y2 - y1) / x * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) - (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2 * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) / x; dx1 = (x2 - x1) / x * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) + (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2 * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) / x; dy1 = (y2 - y1) / x * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) + (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2 * (pTheme->GetHashDist () + pTheme->GetHashWidth ()) / x; for (int i = 1; i < n; i++) { gnome_canvas_path_def_moveto (path, xi[0] += dx, xi[1] += dy); gnome_canvas_path_def_lineto (path, xi[2] += dx1, xi[3] += dy1); gnome_canvas_path_def_lineto (path, xi[4] += dx1, xi[5] += dy1); gnome_canvas_path_def_lineto (path, xi[6] += dx, xi[7] += dy); gnome_canvas_path_def_lineto (path, xi[0], xi[1]); gnome_canvas_path_def_closepath_current (path); } break; } case ForeBondType: GetLine2DCoords(1, &x1, &y1, &x2, &y2); x = sqrt(square(x2 - x1) + square(y2 - y1)); dx = (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2; dy = (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2; gnome_canvas_path_def_moveto (path, x1 * pTheme->GetZoomFactor () + dx, y1 * pTheme->GetZoomFactor () + dy); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () + dx, y2 * pTheme->GetZoomFactor () + dy); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () -dx, y2 * pTheme->GetZoomFactor () -dy); gnome_canvas_path_def_lineto (path, x1 * pTheme->GetZoomFactor () -dx, y1 * pTheme->GetZoomFactor () -dy); gnome_canvas_path_def_closepath (path); break; case UndeterminedBondType: { GetLine2DCoords(1, &x1, &y1, &x2, &y2); x1 *= pTheme->GetZoomFactor (); y1 *= pTheme->GetZoomFactor (); x2 *= pTheme->GetZoomFactor (); y2 *= pTheme->GetZoomFactor (); gnome_canvas_path_def_moveto(path, x1, y1); double length; length = sqrt(square(x2 - x1) + square(y2 - y1)); n = (int)length / 3; int s = 1; dx = (x2 - x1) / n; dy = (y2 - y1) / n; x = x1; y = y1; for (int i = 1; i < n; i++) { x1 = x + dx / 3 + dy /1.5 * s; y1 = y + dy / 3 - dx /1.5 * s; dx1 = x + dx / 1.5 + dy /1.5 * s; dy1 = y + dy / 1.5 - dx /1.5 * s; x += dx; y += dy; s *= -1; gnome_canvas_path_def_curveto(path, x1, y1, dx1, dy1, x, y); } x1 = x + dx / 3 + dy /1.5 * s; y1 = y + dy / 3 - dx /1.5 * s; dx1 = x + dx / 1.5 + dy /1.5 * s; dy1 = y + dy / 1.5 - dx /1.5 * s; gnome_canvas_path_def_curveto(path, x1, y1, dx1, dy1, x2, y2); break; } } return path; } GnomeCanvasPathDef *gcpBond::BuildCrossingPathDef (gcpWidgetData* pData) { gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); GnomeCanvasPathDef* path = NULL; double x1, y1, x2, y2, x, dx, dy; int i; switch (GetType ()) { case NormalBondType: i = 1; path = gnome_canvas_path_def_new (); while (GetLine2DCoords (i++, &x1, &y1, &x2, &y2)) { dx = (x2 - x1) / 10.; dy = (y2 - y1) / 10.; x1 += dx; x2 -= dx; y1 += dy; y2 -= dy; gnome_canvas_path_def_moveto (path, x1 * pTheme->GetZoomFactor (), y1 * pTheme->GetZoomFactor ()); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor (), y2 * pTheme->GetZoomFactor ()); } break; case UpBondType: break; case DownBondType: break; case ForeBondType: path = gnome_canvas_path_def_new (); GetLine2DCoords(1, &x1, &y1, &x2, &y2); dx = (x2 - x1) / 10.; dy = (y2 - y1) / 10.; x1 += dx; x2 -= dx; y1 += dy; y2 -= dy; x = sqrt(square(x2 - x1) + square(y2 - y1)); dx = (y1 - y2) / x * pTheme->GetStereoBondWidth () / 2; dy = (x2 - x1) / x * pTheme->GetStereoBondWidth () / 2; dx += (dx > 0.)? 1.: -1.; dy += (dy > 0.)? 1.: -1.; gnome_canvas_path_def_moveto (path, x1 * pTheme->GetZoomFactor () + dx, y1 * pTheme->GetZoomFactor () + dy); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () + dx, y2 * pTheme->GetZoomFactor () + dy); gnome_canvas_path_def_lineto (path, x2 * pTheme->GetZoomFactor () -dx, y2 * pTheme->GetZoomFactor () -dy); gnome_canvas_path_def_lineto (path, x1 * pTheme->GetZoomFactor () -dx, y1 * pTheme->GetZoomFactor () -dy); gnome_canvas_path_def_closepath (path); break; case UndeterminedBondType: break; } return path; } void gcpBond::Update(GtkWidget* w) { if (!w || !m_order) return; gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data(G_OBJECT(w), "data"); if (pData->Items[this] != NULL) return; gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); bool result = false; if (m_Crossing.size () > 0) { map::iterator i, iend = m_Crossing.end (); for (i = m_Crossing.begin (); i != iend; i++) if ((result |= (*i).second.is_before)) break; } GnomeCanvasPathDef* path; void * obj; GnomeCanvasGroup *group = pData->Items[this]; obj = g_object_get_data(G_OBJECT(group), "back"); if (result) { path = BuildCrossingPathDef (pData); if (path) { if (obj) g_object_set (obj, "bpath", path, NULL); else { GnomeCanvasItem *item = (m_type == NormalBondType || m_type == UndeterminedBondType)? gnome_canvas_item_new ( group, gnome_canvas_bpath_ext_get_type (), "bpath", path, "outline_color", "white", "width_units", pTheme->GetBondWidth () * 3., NULL): gnome_canvas_item_new ( group, gnome_canvas_bpath_ext_get_type(), "bpath", path, "fill_color", "white", "width_units", 0., NULL); g_object_set_data (G_OBJECT (group), "back", item); g_object_set_data (G_OBJECT (item), "object", this); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); /* now bring to front, FIXME: not secure ! */ gnome_canvas_item_lower_to_bottom (item); gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (group)); Atom *pAtom = GetAtom (0); if (pAtom->GetZ () != 6 || static_cast (pAtom)->GetShowSymbol ()) gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (pData->Items[pAtom])); pAtom = GetAtom (1); if (pAtom->GetZ () != 6 || static_cast (pAtom)->GetShowSymbol ()) gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (pData->Items[pAtom])); } gnome_canvas_path_def_unref (path); } } else if (obj) { g_object_set_data (G_OBJECT(group), "back", NULL); } path = BuildPathDef (pData); obj = g_object_get_data(G_OBJECT(group), "path"); g_object_set (obj, "bpath", path, NULL); if (m_type == NormalBondType || m_type == UndeterminedBondType) g_object_set (obj, "width_units", pTheme->GetBondWidth (), NULL); gnome_canvas_path_def_unref (path); } double gcpBond::GetYAlign () { double x1, y1, x2, y2; m_Begin->GetCoords(&x1, &y1); m_End->GetCoords(&x2, &y2); return (y1 + y2) / 2; } bool gcpBond::IsCrossing (gcpBond *pBond) { double a1, a2, b1, b2, c1, c2, d, d1, d2; double x0, x1, x2, x3, y0, y1, y2, y3, z0, z1, z2, z3; if (m_Begin == pBond->m_Begin || m_Begin == pBond->m_End || m_End == pBond->m_Begin || m_End == pBond->m_End) return false; m_Begin->GetCoords(&x0, &y0, &z0); m_End->GetCoords(&x1, &y1, &z1); pBond->m_Begin->GetCoords(&x2, &y2, &z2); pBond->m_End->GetCoords(&x3, &y3, &z3); a1 = x1 - x0; a2 = y1 - y0; b1 = x2 - x3; b2 = y2 - y3; d = a1 * b2 - a2 * b1; if (d == 0.) return false; c1 = x2 - x0; c2 = y2 - y0; d1 = c1 * b2 - c2 * b1; d2 = c2 * a1 - c1 * a2; a1 = d1 / d; a2 = d2 / d; if ((a1 <= 0.) || (a1 >= 1.)) return false; if ((a2 > 0.) && (a2 < 1.)) { double z = z0 + a1 * (z1 - z0), z_ = z2 + a2 * (z3 - z2); bool is_before = z > z_ || m_level > pBond->m_level; if (z == z_ && m_level == pBond->m_level) { if (m_type == ForeBondType && pBond->m_type != ForeBondType) { is_before = true; pBond->m_level -= 1; } else { is_before = false; pBond->m_level += 1; } } m_Crossing[pBond].a = a1; m_Crossing[pBond].is_before = is_before; pBond->m_Crossing[this].a = a2; pBond->m_Crossing[this].is_before = !is_before; return true; } else return false; } static void on_bring_to_front (gcpBond *pBond) { pBond->BringToFront (); } static void on_move_to_back (gcpBond *pBond) { pBond->MoveToBack (); } bool gcpBond::BuildContextualMenu (GtkUIManager *UIManager, Object *object, double x, double y) { bool result = false; Object *pAtom = GetAtomAt (x, y); if (pAtom) result = pAtom->BuildContextualMenu (UIManager, object, x, y); if (m_Crossing.size () == 0) return (pAtom)? result: GetParent ()->BuildContextualMenu (UIManager, object, x, y); bool is_before = false, is_after = false; if (m_Crossing.size () > 0) { map::iterator i, iend = m_Crossing.end (); for (i = m_Crossing.begin (); i != iend; i++) { if (m_level == (*i).first->m_level || m_type != (*i).first->m_type) continue; if ((*i).second.is_before) is_before = true; else is_after = true; } } if (!(is_before || is_after)) return (pAtom)? result: GetParent ()->BuildContextualMenu (UIManager, object, x, y); GtkActionGroup *group = gtk_action_group_new ("bond"); GtkAction *action; action = gtk_action_new ("Bond", _("Bond"), NULL, NULL); gtk_action_group_add_action (group, action); g_object_unref (action); if (is_before) { action = gtk_action_new ("MoveBack", _("Move to back"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (on_move_to_back), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); } if (is_after) { action = gtk_action_new ("BringFront", _("Bring to front"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (on_bring_to_front), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); } gtk_ui_manager_insert_action_group (UIManager, group, 0); g_object_unref (group); if (!pAtom) GetParent ()->BuildContextualMenu (UIManager, object, x, y); return true; } void gcpBond::MoveToBack () { gcpView *pView = reinterpret_cast (GetDocument ())->GetView (); map::iterator i, iend = m_Crossing.end (); for (i = m_Crossing.begin (); i != iend; i++) { if (m_level > (*i).first->m_level && m_type == (*i).first->m_type) { m_level = (*i).first->m_level - 1; (*i).second.is_before = false; (*i).first->m_Crossing[this].is_before = true; pView->Update ((*i).first); } } pView->Update (this); } void gcpBond::BringToFront () { gcpView *pView = reinterpret_cast (GetDocument ())->GetView (); map::iterator i, iend = m_Crossing.end (); for (i = m_Crossing.begin (); i != iend; i++) { if (m_level < (*i).first->m_level && m_type == (*i).first->m_type) { m_level = (*i).first->m_level + 1; (*i).second.is_before = true; (*i).first->m_Crossing[this].is_before = false; pView->Update ((*i).first); } } pView->Update (this); }