1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.com>
5  *
6  *  RawTherapee is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  RawTherapee is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with RawTherapee.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 #pragma once
20 
21 #ifdef GUIVERSION
22 
23 #include <cairomm/cairomm.h>
24 #include <glibmm/ustring.h>
25 
26 #include "editcoordsys.h"
27 #include "../rtengine/coord.h"
28 
29 class ObjectMOBuffer;
30 class RTSurface;
31 
32 /** @file
33  *
34  * The Edit mechanism is designed to let tools (subscribers) communicate with the preview area (provider).
35  * Subscribers will be tools that need to create some graphics in the preview area, to let the user interact
36  * with it in a more user friendly way.
37  *
38  * Do not confuse with _local_ editing, which is another topic implemented in another class. The Edit feature
39  * is also not supported in batch editing from the File Browser.
40  *
41  * Edit tool can be of 2 types: pipette editing and object editing.
42  *
43  * ## Pipette edition
44  *
45  * By using this class, a pipette mechanism can be handled on the preview.
46  *
47  * Each pipette Edit tool must have a unique ID, that will identify them, and which let the ImProcCoordinator
48  * or other mechanism act as appropriated. They are all defined in rtgui/editid.h. A buffer type has to be given
49  * too, to know which kind of buffer to allocate (see EditSubscriber::BufferType).
50  *
51  * Only the first mouse button can be used to manipulate the pipette on the Preview, that's why the developer has
52  * to implement at least the following 4 methods:
53  *    - mouseOver
54  *    - button1Pressed
55  *    - drag1
56  *    - button1Released
57  *
58  * Actually, only curves does use this class, and everything is handled for curve implementer (as much as possible).
59  * See the curve's class documentation to see how to implement the curve's pipette feature.
60  *
61  * ### Event handling
62  *
63  * The mouseOver method is called on each mouse movement, excepted when dragging a point. This method can then access
64  * the pipetteVal array values, which contain the mean of the pixel read in the buffer, or -1 if the cursor is outside
65  * of the image. In this case, EditDataProvider::object is also set to 0 (and 1 if over the image).
66  *
67  * When the user will click on the left mouse button while pressing the CTRL key, button1Pressed will be called.
68  * Setting "dragging" to true (or false) is not required for the pipette type editing.
69  *
70  * The drag1 method will be called on all subsequent mouse move. The pipetteVal[3] array will already be filled with
71  * the mean of the read values under the cursor (actually a fixed square of 8px). If the BufferType is BT_SINGLEPLANE_FLOAT,
72  * only the first array value will be filled.
73  *
74  * Then the button1Released will be called to stop the dragging.
75  *
76  * ## Object edition
77  *
78  * By using this class, objects can be drawn and manipulated on the preview.
79  *
80  * The developer has to handle the buttonPress, buttonRelease, drag and mouseOver methods that he needs. There
81  * are buttonPress, buttonRelease and drag methods dedicated to each mouse button, for better flexibility
82  * (e.g.button2Press, button2Release, drag2 will handle event when mouse button 2 is used first). RT actually
83  * does not handle multiple mouse button event (e.g. button1 + button2), only one at a time. The first button pressed
84  * set the mechanism, all other combined button press are ignored.
85  *
86  * The developer also have to fill 2 display list with object of the Geometry subclass. Each geometric shape
87  * _can_ be used in one or the other, or both list at a time.
88  *
89  * The first list (visibleGeometry) is used to be displayed on the preview. The developer will have to set their state
90  * manually (see Geometry::State), but the display shape, line width and color can be handled automatically, or with
91  * specific values. To be displayed, the F_VISIBLE flag has to be set through the setActive or setVisible methods.
92  *
93  * The second list (mouseOverGeometry) is used in a backbuffer, the color used to draw the shape being the id of the
94  * mouseOverGeometry. As an example, you could create a line to be shown in the preview, but create 2 filled Circle object
95  * to be used as mouseOver detection, one on each end of the line. The association between both shape (visible and mouseOver)
96  * is handled by the developer. To be displayed on this backbuffer, the F_HOVERABLE flag has to be set through the
97  * setActive or setHoverable methods. For overlapping mouse over geometry, the priority is set by the order in the list :
98  * the last item is detected first (think of it like a stack piled up).
99  *
100  *
101  * ### Event handling
102  *
103  * RT will draw in the back buffer all mouseOverGeometry set by the developer once the Edit button is pressed, and handle
104  * the events automatically.
105  *
106  * RT will call the mouseOver method on each mouse movement where no mouse button is pressed.
107  *
108  * On mouse button press over a mouseOverGeometry (that has F_HOVERABLE set), it will call the button press method corresponding
109  * to the button (e.g. button1Pressed for mouse button 1), with the modifier key as parameter. Any other mouse button pressed at
110  * the same time will be ignored. It's up to the developer to decide whether this action is starting a 'drag' or 'pick' action,
111  * by setting the 'action' parameter to the appropriated value.
112  *
113  * If the user sets action to ES_ACTION_DRAGGING, RT will then send drag1 events (to stay with our button 1 pressed example) on each
114  * mouse movement. It's up to the developer of the tool to handle the dragging. The EditProvider class will help you in this by
115  * handling the actual position in various coordinate system and ways.
116  *
117  * When the user will release the mouse button, RT will call the button1Release event (in our example). The developer have
118  * then to set action to ES_ACTION_NONE.
119  *
120  * If the user sets action to ES_ACTION_PICKING, RT will keep in memory the mouseOver object that was selected when pressing the mouse
121  * (e.g. button 1), as well as the modifier keys.
122  *
123  * The element is said to be picked when the mouse button is released over the same mouse over object and with the same active
124  * modifier keys. In this case, the corresponding picked event (e.g. picked1 in our example) and the 'picked' flag will be true.
125  * If any of those condition is false, picked1 will still be be called to terminate the initiated picking action, but 'picked'
126  * will be false. This is necessary because the user may want to update the geometry if the picking is aborted. The developer have
127  * then to set action to ES_ACTION_NONE.
128  *
129  * Picking an on-screen element correspond to single-clicking on it. No double click is supported so far.
130  *
131  * Each of these methods have to returns a boolean value saying that the preview has to be refreshed or not (i.e. the displayed
132  * geometry).
133  *
134  * ## Other general internal implementation notes
135  *
136  * When a tool is being constructed, unique IDs are affected to the EditSubscribers of the Pipette type.
137  * Then the EditorPanel class will ask all ToolPanel to register the 'after' preview ImageArea object as data provider.
138  * The Subscribers have now to provide a toggle button to click on to start the Edit listening. When toggling on, the Subscriber
139  * register itself to the DataProvider, then an event is thrown through the standard ToolPanelListener::panelChanged
140  * method to update the preview with new graphics to be displayed. If a previous Edit button was active, it will be deactivated
141  * (the Edit buttons are mutually exclusive). For the Pipette type, a buffer will be created and has to be populated
142  * by the developer in rtengine's pipeline. The unique pipette ID will be used to know where to fill the buffer, as each pipette
143  * will need different data, corresponding to the state of the image right before the tool that needs pipette values. E.g for
144  * the HSV tool, the Hue and Saturation and Value curves are applied on the current state of the image. That's why the pipette
145  * of the H, S and V curve will share the same data of this "current state", otherwise the read value would be wrong.
146  *
147  * When the Edit process stops, the Subscriber is removed from the DataProvider, so buffers can be freed up.
148  * A new ToolPanelListener::panelChanged event is also thrown to update the preview again, without the tool's
149  * graphical objects. The Edit button is also toggled off (by the user or programmatically).
150  *
151  * It means that each Edit buttons toggled on will start an update of the preview which might or might not create
152  * a new History entry, depending on the ProcEvent used.
153  *
154  */
155 
156 class RGBColor
157 {
158     double r;
159     double g;
160     double b;
161 
162 public:
163     RGBColor ();
164     explicit RGBColor (double r, double g, double b);
165     explicit RGBColor (char r, char g, char b);
166 
167     void setColor (double r, double g, double b);
168     void setColor (char r, char g, char b);
169 
170     double getR ();
171     double getG ();
172     double getB ();
173 };
174 
175 class RGBAColor : public RGBColor
176 {
177     double a;
178 
179 public:
180     RGBAColor ();
181     explicit RGBAColor (double r, double g, double b, double a);
182     explicit RGBAColor (char r, char g, char b, char a);
183 
184     void setColor (double r, double g, double b, double a);
185     void setColor (char r, char g, char b, char a);
186 
187     double getA ();
188 };
189 
190 /// @brief Displayable and MouseOver geometry base class
191 class Geometry
192 {
193 public:
194     /// @brief Graphical state of the element
195     enum State {
196         NORMAL,     /// Default state
197         ACTIVE,     /// Focused state
198         PRELIGHT,   /// Hovered state
199         DRAGGED,    /// When being dragged
200         INSENSITIVE /// Displayed but insensitive
201     };
202 
203     /// @brief Coordinate space and origin of the point
204     enum Datum {
205         IMAGE,          /// Image coordinate system with image's top left corner as origin
206         CLICKED_POINT,  /// Screen coordinate system with clicked point as origin
207         CURSOR          /// Screen coordinate system with actual cursor position as origin
208     };
209     enum Flags {
210         F_VISIBLE     = 1 << 0, /// true if the geometry have to be drawn on the visible layer
211         F_HOVERABLE   = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer
212         F_AUTO_COLOR  = 1 << 2, /// true if the color depend on the state value, not the color field above
213     };
214 
215     /// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point:
216     enum DrivenPoint {
217         DP_CENTERCENTER,
218         DP_TOPLEFT,
219         DP_TOPCENTER,
220         DP_TOPRIGHT,
221         DP_CENTERRIGHT,
222         DP_BOTTOMRIGHT,
223         DP_BOTTOMCENTER,
224         DP_BOTTOMLEFT,
225         DP_CENTERLEFT
226     };
227 
228 protected:
229     RGBColor innerLineColor;
230     RGBColor outerLineColor;
231     short flags;
232 
233 public:
234     float innerLineWidth;  // ...outerLineWidth = innerLineWidth+2
235     Datum datum;
236     State state;  // set by the Subscriber
237 
238     Geometry ();
~Geometry()239     virtual ~Geometry() {}
240 
241     void setInnerLineColor (double r, double g, double b);
242     void setInnerLineColor (char r, char g, char b);
243     RGBColor getInnerLineColor ();
244     void setOuterLineColor (double r, double g, double b);
245     void setOuterLineColor (char r, char g, char b);
246     RGBColor getOuterLineColor ();
247     double getOuterLineWidth ();
248     double getMouseOverLineWidth ();
249     void setAutoColor (bool aColor);
250     bool isVisible ();
251     void setVisible (bool visible);
252     bool isHoverable ();
253     void setHoverable (bool visible);
254 
255 
256     // setActive will enable/disable the visible and hoverable flags in one shot!
257     void setActive (bool active);
258 
259     virtual void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
260     virtual void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
261     virtual void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
262 };
263 
264 class Circle : public Geometry
265 {
266 public:
267     rtengine::Coord center;
268     int radius;
269     bool filled;
270     bool radiusInImageSpace; /// If true, the radius depend on the image scale; if false, it is a fixed 'screen' size
271 
272     Circle ();
273     Circle (rtengine::Coord& center, int radius, bool filled = false, bool radiusInImageSpace = false);
274     Circle (int centerX, int centerY, int radius, bool filled = false, bool radiusInImageSpace = false);
275 
276     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
277     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
278     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
279 };
280 
281 class Line : public Geometry
282 {
283 public:
284     rtengine::Coord begin;
285     rtengine::Coord end;
286 
287     Line ();
288     Line (const rtengine::Coord& begin, const rtengine::Coord& end);
289     Line (int beginX, int beginY, int endX, int endY);
290 
291     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
292     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
293     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
294 };
295 
296 class Polyline : public Geometry
297 {
298 public:
299     std::vector<rtengine::Coord> points;
300     bool filled;
301 
302     Polyline ();
303 
304     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
305     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
306     void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
307 };
308 
309 class Rectangle : public Geometry
310 {
311 public:
312     rtengine::Coord topLeft;
313     rtengine::Coord bottomRight;
314     bool filled;
315 
316     Rectangle ();
317 
318     void setXYWH(int left, int top, int width, int height);
319     void setXYXY(int left, int top, int right, int bottom);
320     void setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight);
321     void setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight);
322     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
323     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
324     void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
325 };
326 
327 class OPIcon : public Geometry    // OP stands for "On Preview"
328 {
329 
330 private:
331     Cairo::RefPtr<RTSurface> normalImg;
332     Cairo::RefPtr<RTSurface> prelightImg;
333     Cairo::RefPtr<RTSurface> activeImg;
334     Cairo::RefPtr<RTSurface> draggedImg;
335     Cairo::RefPtr<RTSurface> insensitiveImg;
336 
337     static void updateImages();
338     void changeImage(Glib::ustring &newImage);
339     void drawImage (Cairo::RefPtr<RTSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
340     void drawMOImage (Cairo::RefPtr<RTSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
341     void drivenPointToRectangle(const rtengine::Coord &pos, rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H);
342 
343 public:
344     DrivenPoint drivenPoint;
345     rtengine::Coord position;
346 
347     OPIcon (const Cairo::RefPtr<RTSurface> &normal,
348             const Cairo::RefPtr<RTSurface> &active,
349             const Cairo::RefPtr<RTSurface> &prelight = {},
350             const Cairo::RefPtr<RTSurface> &dragged = {},
351             const Cairo::RefPtr<RTSurface> &insensitive = {},
352             DrivenPoint drivenPoint = DP_CENTERCENTER);
353     OPIcon (Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustring  prelightImage = "", Glib::ustring  draggedImage = "", Glib::ustring insensitiveImage = "", DrivenPoint drivenPoint = DP_CENTERCENTER);
354     const Cairo::RefPtr<RTSurface> getNormalImg();
355     const Cairo::RefPtr<RTSurface> getPrelightImg();
356     const Cairo::RefPtr<RTSurface> getActiveImg();
357     const Cairo::RefPtr<RTSurface> getDraggedImg();
358     const Cairo::RefPtr<RTSurface> getInsensitiveImg();
359     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
360     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
361     void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
362 };
363 
364 class OPAdjuster : public Geometry    // OP stands for "On Preview"
365 {
366 
367 };
368 
setColor(double r,double g,double b)369 inline void RGBColor::setColor (double r, double g, double b) {
370     this->r = r;
371     this->g = g;
372     this->b = b;
373 }
374 
setColor(char r,char g,char b)375 inline void RGBColor::setColor (char r, char g, char b) {
376     this->r = double (r) / 255.;
377     this->g = double (g) / 255.;
378     this->b = double (b) / 255.;
379 }
380 
getR()381 inline double RGBColor::getR () {
382     return r;
383 }
384 
getG()385 inline double RGBColor::getG () {
386     return g;
387 }
388 
getB()389 inline double RGBColor::getB () {
390     return b;
391 }
392 
setColor(double r,double g,double b,double a)393 inline void RGBAColor::setColor (double r, double g, double b, double a) {
394     RGBColor::setColor (r, g, b);
395     this->a = a;
396 }
397 
setColor(char r,char g,char b,char a)398 inline void RGBAColor::setColor (char r, char g, char b, char a) {
399     RGBColor::setColor (r, g, b);
400     this->a = double (a) / 255.;
401 }
402 
getA()403 inline double RGBAColor::getA () {
404     return a;
405 }
406 
setInnerLineColor(double r,double g,double b)407 inline void Geometry::setInnerLineColor (double r, double g, double b) {
408     innerLineColor.setColor (r, g, b);
409     flags &= ~F_AUTO_COLOR;
410 }
411 
setInnerLineColor(char r,char g,char b)412 inline void Geometry::setInnerLineColor (char r, char g, char b) {
413     innerLineColor.setColor (r, g, b);
414     flags &= ~F_AUTO_COLOR;
415 }
416 
setOuterLineColor(double r,double g,double b)417 inline void Geometry::setOuterLineColor (double r, double g, double b) {
418     outerLineColor.setColor (r, g, b);
419     flags &= ~F_AUTO_COLOR;
420 }
421 
getOuterLineWidth()422 inline double Geometry::getOuterLineWidth () {
423     return double (innerLineWidth) + 2.;
424 }
425 
setOuterLineColor(char r,char g,char b)426 inline void Geometry::setOuterLineColor (char r, char g, char b) {
427     outerLineColor.setColor (r, g, b);
428     flags &= ~F_AUTO_COLOR;
429 }
430 
getMouseOverLineWidth()431 inline double Geometry::getMouseOverLineWidth () {
432     return getOuterLineWidth () + 2.;
433 }
434 
setAutoColor(bool aColor)435 inline void Geometry::setAutoColor (bool aColor) {
436     if (aColor) {
437         flags |= F_AUTO_COLOR;
438     } else {
439         flags &= ~F_AUTO_COLOR;
440     }
441 }
442 
isVisible()443 inline bool Geometry::isVisible () {
444     return flags & F_VISIBLE;
445 }
446 
setVisible(bool visible)447 inline void Geometry::setVisible (bool visible) {
448     if (visible) {
449         flags |= F_VISIBLE;
450     } else {
451         flags &= ~F_VISIBLE;
452     }
453 }
454 
isHoverable()455 inline bool Geometry::isHoverable () {
456     return flags & F_HOVERABLE;
457 }
458 
setHoverable(bool hoverable)459 inline void Geometry::setHoverable (bool hoverable) {
460     if (hoverable) {
461         flags |= F_HOVERABLE;
462     } else {
463         flags &= ~F_HOVERABLE;
464     }
465 }
466 
setActive(bool active)467 inline void Geometry::setActive (bool active) {
468     if (active) {
469         flags |= (F_VISIBLE | F_HOVERABLE);
470     } else {
471         flags &= ~(F_VISIBLE | F_HOVERABLE);
472     }
473 }
474 
Geometry()475 inline Geometry::Geometry () :
476         innerLineColor (char (255), char (255), char (255)), outerLineColor (
477                 char (0), char (0), char (0)), flags (
478                 F_VISIBLE | F_HOVERABLE | F_AUTO_COLOR), innerLineWidth (1.5f), datum (
479                 IMAGE), state (NORMAL) {
480 }
481 
RGBAColor()482 inline RGBAColor::RGBAColor () :
483         RGBColor (0., 0., 0.), a (0.) {
484 }
485 
RGBColor()486 inline RGBColor::RGBColor () :
487         r (0.), g (0.), b (0.) {
488 }
489 
RGBColor(double r,double g,double b)490 inline RGBColor::RGBColor (double r, double g, double b) :
491         r (r), g (g), b (b) {
492 }
493 
RGBColor(char r,char g,char b)494 inline RGBColor::RGBColor (char r, char g, char b) :
495         r (double (r) / 255.), g (double (g) / 255.), b (double (b) / 255.) {
496 }
497 
RGBAColor(double r,double g,double b,double a)498 inline RGBAColor::RGBAColor (double r, double g, double b, double a) :
499         RGBColor (r, g, b), a (a) {
500 }
501 
RGBAColor(char r,char g,char b,char a)502 inline RGBAColor::RGBAColor (char r, char g, char b, char a) :
503         RGBColor (r, g, b), a (double (a) / 255.) {
504 }
505 
Circle()506 inline Circle::Circle () :
507         center (100, 100), radius (10), filled (false), radiusInImageSpace (
508                 false) {
509 }
510 
Rectangle()511 inline Rectangle::Rectangle () :
512         topLeft (0, 0), bottomRight (10, 10), filled (false) {
513 }
514 
Polyline()515 inline Polyline::Polyline () :
516         filled (false) {
517 }
518 
Line()519 inline Line::Line () :
520         begin (10, 10), end (100, 100) {
521 }
522 
Circle(rtengine::Coord & center,int radius,bool filled,bool radiusInImageSpace)523 inline Circle::Circle (rtengine::Coord& center, int radius, bool filled,
524         bool radiusInImageSpace) :
525         center (center), radius (radius), filled (filled), radiusInImageSpace (
526                 radiusInImageSpace) {
527 }
528 
Circle(int centerX,int centerY,int radius,bool filled,bool radiusInImageSpace)529 inline Circle::Circle (int centerX, int centerY, int radius, bool filled,
530         bool radiusInImageSpace) :
531         center (centerX, centerY), radius (radius), filled (filled), radiusInImageSpace (
532                 radiusInImageSpace) {
533 }
534 
Line(const rtengine::Coord & begin,const rtengine::Coord & end)535 inline Line::Line (const rtengine::Coord& begin, const rtengine::Coord& end) :
536         begin (begin), end (end) {
537 }
538 
Line(int beginX,int beginY,int endX,int endY)539 inline Line::Line (int beginX, int beginY, int endX, int endY) :
540         begin (beginX, beginY), end (endX, endY) {
541 }
542 
543 #endif
544 
545