1 // XDrawChem
2 // Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
3 // Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>
4 
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 
18 // gobject.cpp - GraphicObject's implementation of functions
19 
20 #include <QPoint>
21 #include <QPolygon>
22 #include <QRect>
23 #include <QString>
24 
25 #include "bondedit.h"
26 #include "defs.h"
27 #include "drawable.h"
28 #include "gobject.h"
29 #include "render2d.h"
30 
GraphicObject(Render2D * r1,QObject * parent)31 GraphicObject::GraphicObject(Render2D *r1, QObject *parent) : Drawable(parent) {
32     r = r1;
33     highlighted = false;
34     style = 0;
35     ot = 0;
36 }
37 
CloneTo(Drawable * target) const38 GraphicObject *GraphicObject::CloneTo(Drawable *target) const {
39     if (!target)
40         target = new GraphicObject(r, parent());
41 
42     GraphicObject *t = static_cast<GraphicObject *>(this->Drawable::CloneTo(target));
43     t->objectPoints = objectPoints;
44     t->ot = ot;
45     return t;
46 }
47 
ToXML(QString xml_id)48 QString GraphicObject::ToXML(QString xml_id) {
49     QString s, n1;
50 
51     // begin graphic object
52     s.append("<graphicobject id=\"");
53     s.append(xml_id);
54     s.append("\">\n");
55 
56     // write object type (ot)
57     s.append("<objecttype>");
58     n1.setNum(ot);
59     s.append(n1);
60     s.append("</objecttype>\n");
61 
62     // write Style
63     s.append("<style>");
64     n1.setNum(style);
65     s.append(n1);
66     s.append("</style>\n");
67 
68     // write points
69     if (ot == TYPE_BEZIER) {
70         int ci, xi, yi;
71 
72         for (ci = 0; ci < 4; ci++) {
73             s.append("<point>");
74             objectPoints.point(ci, &xi, &yi);
75             n1.setNum(xi);
76             s.append(n1);
77             s.append(" ");
78             n1.setNum(yi);
79             s.append(n1);
80             s.append("</point>\n");
81         }
82     }
83     // write color
84     s.append("<color>");
85     n1.setNum(color.red());
86     s.append(n1);
87     s.append(" ");
88     n1.setNum(color.green());
89     s.append(n1);
90     s.append(" ");
91     n1.setNum(color.blue());
92     s.append(n1);
93     s.append("</color>\n");
94 
95     // end bracket
96     s.append("</graphicobject>\n");
97 
98     return s;
99 }
100 
ToCDXML(QString xml_id)101 QString GraphicObject::ToCDXML(QString xml_id) {
102     QString s, n1;
103 
104     return s; // until I learn how to do these objects in CDXML
105 
106     // begin arrow
107     s.append("<graphic id=\"");
108     s.append(xml_id);
109     s.append("\" BoundingBox=\"");
110     n1.setNum(end->x);
111     s.append(n1);
112     s.append(" ");
113     n1.setNum(end->y);
114     s.append(n1);
115     s.append(" ");
116     n1.setNum(start->x);
117     s.append(n1);
118     s.append(" ");
119     n1.setNum(start->y);
120     s.append(n1);
121     s.append("\" ");
122     if (style == BRACKET_SQUARE)
123         s.append("GraphicType=\"Bracket\" BracketType=\"SquarePair\"");
124     if (style == BRACKET_CURVE)
125         s.append("GraphicType=\"Bracket\" BracketType=\"RoundPair\"");
126     if (style == BRACKET_BRACE)
127         s.append("GraphicType=\"Bracket\" BracketType=\"CurlyPair\"");
128     s.append("/>\n");
129 
130     return s;
131 }
132 
133 // set GraphicObject from XDrawChem-format XML
FromXML(QString xml_tag)134 void GraphicObject::FromXML(QString xml_tag) {
135     int i1, i2;
136 
137     i1 = xml_tag.indexOf("<Start>");
138     if (i1 >= 0) {
139         i2 = xml_tag.indexOf("</Start>") + 8;
140         SetStartFromXML(xml_tag.mid(i1, i2 - i1));
141     }
142     i1 = xml_tag.indexOf("<End>");
143     if (i1 >= 0) {
144         i2 = xml_tag.indexOf("</End>") + 6;
145         SetEndFromXML(xml_tag.mid(i1, i2 - i1));
146     }
147     i1 = xml_tag.indexOf("<objecttype>");
148     if (i1 >= 0) {
149         i2 = xml_tag.indexOf("</objecttype>") + 13;
150         ot = xml_tag.mid(i1 + 12, 2).toInt();
151     }
152     i1 = xml_tag.indexOf("<style>");
153     if (i1 >= 0) {
154         i2 = xml_tag.indexOf("</style>") + 8;
155         style = xml_tag.mid(i1 + 7, i2 - i1 - 15).toInt();
156     }
157     i1 = xml_tag.indexOf("<color>");
158     if (i1 >= 0) {
159         i2 = xml_tag.indexOf("</color>") + 8;
160         SetColorFromXML(xml_tag.mid(i1, i2 - i1));
161     }
162     if (ot == TYPE_BEZIER) {
163         objectPoints.resize(4);
164         int ci, xi, yi, i3 = 0;
165         QString xml_subtag;
166 
167         for (ci = 0; ci < 4; ci++) {
168             i1 = xml_tag.indexOf("<point>", i3);
169             i2 = xml_tag.indexOf("</point>", i3) + 8;
170             i3 = i2 + 1;
171             xml_subtag = xml_tag.mid(i1, i2 - i1);
172             i1 = xml_subtag.indexOf("<point>");
173             i2 = xml_subtag.indexOf("</point>");
174             xml_subtag.remove(i2, 999);
175             xml_subtag.remove(i1, 7);
176 
177             QTextStream ts(&xml_subtag, QIODevice::ReadOnly);
178 
179             ts >> xi >> yi;
180             qDebug() << ci << " " << xi << " " << yi;
181             objectPoints.setPoint(ci, xi, yi);
182         }
183         start = new DPoint(objectPoints.point(0));
184         end = new DPoint(objectPoints.point(1));
185     }
186 }
187 
Render()188 void GraphicObject::Render() {
189     QColor c1;
190 
191     if (highlighted)
192         c1 = QColor(255, 0, 0);
193     else
194         c1 = color;
195     if (ot == TYPE_BEZIER)
196         r->drawBezier(objectPoints, c1, false, style);
197     if (ot == TYPE_GRAPHIC_LINE)
198         r->drawLine(start->toQPoint(), end->toQPoint(), 1, GetColor(), 1);
199     if (ot == TYPE_GRAPHIC_BONDMARK)
200         r->drawLine(start->toQPoint(), end->toQPoint(), 1, GetColor(), 1);
201 }
202 
Edit()203 void GraphicObject::Edit() {
204     return; // it's not practical to edit any graphic objects this way
205     /*
206        BondEditDialog be(r, "bracket editor", start, end, TYPE_BRACKET, 0, 0, 0,
207        style, color);
208        if ( !be.exec() ) return;
209        qDebug() << "change" ;
210        style = be.Style();
211        color = be.Color();
212      */
213 }
214 
Type()215 int GraphicObject::Type() {
216     // real type is stored in style
217     return TYPE_GRAPHIC_OBJECT;
218 }
219 
Find(DPoint * target)220 bool GraphicObject::Find(DPoint *target) {
221     if (start == target)
222         return true;
223     if (end == target)
224         return true;
225     return false;
226 }
227 
228 // Do not allow connections to this object.
229 // Simplest way to do this, I think, is to disallow this function
FindNearestPoint(DPoint * target,double & dist)230 DPoint *GraphicObject::FindNearestPoint(DPoint *target, double &dist) {
231     dist = 99999.0;
232     return 0;
233 }
234 
FindNearestObject(DPoint * target,double & dist)235 Drawable *GraphicObject::FindNearestObject(DPoint *target, double &dist) {
236     DPoint *tl = new DPoint;
237     DPoint *tr = new DPoint;
238     DPoint *bl = new DPoint;
239     DPoint *br = new DPoint;
240     double tl_x, tl_y, br_x, br_y, swp, dist1, dist2;
241 
242     tl_x = start->x;
243     tl_y = start->y;
244     br_x = end->x;
245     br_y = end->y;
246     if (tl_x < br_x) {
247         swp = tl_x;
248         tl_x = br_x;
249         br_x = swp;
250     }
251     if (tl_y < br_y) {
252         swp = tl_y;
253         tl_y = br_y;
254         br_y = swp;
255     }
256     tl->x = tl_x;
257     tl->y = tl_y;
258     bl->x = tl_x;
259     bl->y = br_y;
260     tr->x = br_x;
261     tr->y = tl_y;
262     br->x = br_x;
263     br->y = br_y;
264     dist1 = DistanceToLine(tl, bl, target);
265     dist2 = DistanceToLine(tr, br, target);
266     if (dist1 < dist2)
267         dist = dist1;
268     else
269         dist = dist2;
270     delete tl;
271     delete tr;
272     delete bl;
273     delete br;
274 
275     return this;
276 }
277 
setPoints(DPoint * s,DPoint * e)278 void GraphicObject::setPoints(DPoint *s, DPoint *e) {
279     start = s;
280     end = e;
281 }
282 
isWithinRect(QRect n,bool)283 bool GraphicObject::isWithinRect(QRect n, bool /*shiftdown*/) {
284     if (DPointInRect(start, n) && DPointInRect(end, n))
285         highlighted = true;
286     else
287         highlighted = false;
288     return highlighted;
289 }
290 
BoundingBox()291 QRect GraphicObject::BoundingBox() {
292     if (highlighted == false)
293         return QRect(QPoint(999, 999), QPoint(0, 0));
294     int top, bottom, left, right, swp;
295 
296     top = (int)start->y;
297     left = (int)start->x;
298     bottom = (int)end->y;
299     right = (int)end->x;
300     if (bottom < top) {
301         swp = top;
302         top = bottom;
303         bottom = swp;
304     }
305     if (right < left) {
306         swp = left;
307         left = right;
308         right = swp;
309     }
310     return QRect(QPoint(left, top), QPoint(right, bottom));
311 }
312 
setPointArray(QPolygon inp1)313 void GraphicObject::setPointArray(QPolygon inp1) {
314     objectPoints = inp1;
315     // kludge around lack opf Start and End in this object type/style
316     start = new DPoint(objectPoints.point(0));
317     end = new DPoint(objectPoints.point(1));
318 }
319