wxruby3-shapes 0.9.0.pre.beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +12 -0
  3. data/CREDITS.md +18 -0
  4. data/INSTALL.md +39 -0
  5. data/LICENSE +21 -0
  6. data/README.md +118 -0
  7. data/assets/screenshot.png +0 -0
  8. data/bin/wx-shapes +9 -0
  9. data/lib/wx/shapes/arrow_base.rb +86 -0
  10. data/lib/wx/shapes/arrows/circle_arrow.rb +39 -0
  11. data/lib/wx/shapes/arrows/diamond_arrow.rb +33 -0
  12. data/lib/wx/shapes/arrows/open_arrow.rb +56 -0
  13. data/lib/wx/shapes/arrows/solid_arrow.rb +69 -0
  14. data/lib/wx/shapes/art/shape_canvas/page.xpm +73 -0
  15. data/lib/wx/shapes/auto_layout.rb +358 -0
  16. data/lib/wx/shapes/base.rb +33 -0
  17. data/lib/wx/shapes/canvas_history.rb +84 -0
  18. data/lib/wx/shapes/connection_point.rb +238 -0
  19. data/lib/wx/shapes/core.rb +19 -0
  20. data/lib/wx/shapes/diagram.rb +659 -0
  21. data/lib/wx/shapes/events.rb +389 -0
  22. data/lib/wx/shapes/printout.rb +136 -0
  23. data/lib/wx/shapes/serializable.rb +440 -0
  24. data/lib/wx/shapes/serialize/core.rb +40 -0
  25. data/lib/wx/shapes/serialize/id.rb +82 -0
  26. data/lib/wx/shapes/serialize/wx.rb +104 -0
  27. data/lib/wx/shapes/serializer/json.rb +258 -0
  28. data/lib/wx/shapes/serializer/yaml.rb +125 -0
  29. data/lib/wx/shapes/shape.rb +2129 -0
  30. data/lib/wx/shapes/shape_canvas.rb +3285 -0
  31. data/lib/wx/shapes/shape_data_object.rb +43 -0
  32. data/lib/wx/shapes/shape_handle.rb +287 -0
  33. data/lib/wx/shapes/shape_list.rb +161 -0
  34. data/lib/wx/shapes/shapes/bitmap_shape.rb +257 -0
  35. data/lib/wx/shapes/shapes/circle_shape.rb +136 -0
  36. data/lib/wx/shapes/shapes/control_shape.rb +483 -0
  37. data/lib/wx/shapes/shapes/curve_shape.rb +231 -0
  38. data/lib/wx/shapes/shapes/diamond_shape.rb +62 -0
  39. data/lib/wx/shapes/shapes/edit_text_shape.rb +317 -0
  40. data/lib/wx/shapes/shapes/ellipse_shape.rb +106 -0
  41. data/lib/wx/shapes/shapes/flex_grid_shape.rb +78 -0
  42. data/lib/wx/shapes/shapes/grid_shape.rb +404 -0
  43. data/lib/wx/shapes/shapes/line_shape.rb +907 -0
  44. data/lib/wx/shapes/shapes/multi_sel_rect.rb +214 -0
  45. data/lib/wx/shapes/shapes/ortho_shape.rb +357 -0
  46. data/lib/wx/shapes/shapes/polygon_shape.rb +294 -0
  47. data/lib/wx/shapes/shapes/rect_shape.rb +378 -0
  48. data/lib/wx/shapes/shapes/round_ortho_shape.rb +131 -0
  49. data/lib/wx/shapes/shapes/round_rect_shape.rb +142 -0
  50. data/lib/wx/shapes/shapes/square_shape.rb +119 -0
  51. data/lib/wx/shapes/shapes/text_shape.rb +324 -0
  52. data/lib/wx/shapes/thumbnail.rb +234 -0
  53. data/lib/wx/shapes/version.rb +12 -0
  54. data/lib/wx/shapes/wx.rb +29 -0
  55. data/lib/wx/shapes.rb +18 -0
  56. data/lib/wx/wx-shapes/base.rb +87 -0
  57. data/lib/wx/wx-shapes/cmd/sampler.rb +58 -0
  58. data/lib/wx/wx-shapes/cmd/test.rb +27 -0
  59. data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +7 -0
  60. data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
  61. data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
  62. data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
  63. data/rakelib/yard/yard-custom-templates.rb +2 -0
  64. data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
  65. data/samples/demo/art/AlignBottom.xpm +35 -0
  66. data/samples/demo/art/AlignCenter.xpm +35 -0
  67. data/samples/demo/art/AlignLeft.xpm +35 -0
  68. data/samples/demo/art/AlignMiddle.xpm +35 -0
  69. data/samples/demo/art/AlignRight.xpm +35 -0
  70. data/samples/demo/art/AlignTop.xpm +35 -0
  71. data/samples/demo/art/Bitmap.xpm +25 -0
  72. data/samples/demo/art/Circle.xpm +22 -0
  73. data/samples/demo/art/Curve.xpm +21 -0
  74. data/samples/demo/art/Diamond.xpm +22 -0
  75. data/samples/demo/art/EditText.xpm +21 -0
  76. data/samples/demo/art/Ellipse.xpm +22 -0
  77. data/samples/demo/art/FixedRect.xpm +22 -0
  78. data/samples/demo/art/FlexGrid.xpm +22 -0
  79. data/samples/demo/art/GC.xpm +23 -0
  80. data/samples/demo/art/Grid.xpm +22 -0
  81. data/samples/demo/art/Line.xpm +21 -0
  82. data/samples/demo/art/NoSource.xpm +69 -0
  83. data/samples/demo/art/OrthoLine.xpm +21 -0
  84. data/samples/demo/art/Rect.xpm +22 -0
  85. data/samples/demo/art/RoundOrthoLine.xpm +21 -0
  86. data/samples/demo/art/RoundRect.xpm +22 -0
  87. data/samples/demo/art/Shadow.xpm +23 -0
  88. data/samples/demo/art/StandAloneLine.xpm +22 -0
  89. data/samples/demo/art/Text.xpm +21 -0
  90. data/samples/demo/art/Tool.xpm +23 -0
  91. data/samples/demo/art/sample.xpm +251 -0
  92. data/samples/demo/demo.rb +658 -0
  93. data/samples/demo/frame_canvas.rb +422 -0
  94. data/samples/demo/images/motyl.bmp +0 -0
  95. data/samples/demo/images/motyl2.bmp +0 -0
  96. data/samples/sample1/art/sample.xpm +251 -0
  97. data/samples/sample1/sample.rb +263 -0
  98. data/samples/sample2/art/sample.xpm +251 -0
  99. data/samples/sample2/sample.rb +133 -0
  100. data/samples/sample2/sample_canvas.rb +35 -0
  101. data/samples/sample2/sample_shape.rb +108 -0
  102. data/samples/sample3/art/sample.xpm +251 -0
  103. data/samples/sample3/sample.rb +281 -0
  104. data/samples/sample4/art/sample.xpm +251 -0
  105. data/samples/sample4/sample.rb +180 -0
  106. data/tests/art/motyl.bmp +0 -0
  107. data/tests/lib/wxapp_runner.rb +64 -0
  108. data/tests/serializer_tests.rb +521 -0
  109. data/tests/test_grid_shapes.rb +42 -0
  110. data/tests/test_serialize.rb +7 -0
  111. data/tests/test_serialize_yaml.rb +17 -0
  112. metadata +242 -0
@@ -0,0 +1,907 @@
1
+ # Wx::SF::LineShape - line shape class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/arrow_base'
5
+
6
+ module Wx::SF
7
+
8
+ class LineShape < Shape
9
+
10
+ # Default values
11
+ module DEFAULT
12
+ # Default value of undefined ID.
13
+ UNKNOWNID = nil
14
+ # Default value of LineShape @pen data member.
15
+ PEN = Wx::Pen.new(Wx::BLACK) if Wx::App.is_main_loop_running
16
+ Wx.add_delayed_constant(self, :PEN) { Wx::Pen.new(Wx::BLACK) }
17
+ # Default value of LineShape @dock_point data member.
18
+ DOCKPOINT = 0
19
+ # Default value of LineShape @dock_point data member (start line point).
20
+ DOCKPOINT_START = -1
21
+ # Default value of LineShape @dock_point data member (end line point).
22
+ DOCKPOINT_END = -2
23
+ # Default value of LineShape @dock_point data member (middle dock point).
24
+ DOCKPOINT_CENTER = 2**64
25
+ # Default value of LineShape @src_offset and LineShape @trg_offset data members.
26
+ OFFSET = Wx::RealPoint.new(-1, -1)
27
+ # Default value of LineShape @src_point and LineShape @trg_point data members.
28
+ POINT = Wx::RealPoint.new(0, 0)
29
+ # Default value of LineShape @stand_alone data member.
30
+ STANDALONE = false
31
+ end
32
+
33
+ # The modes in which the line shape can stay.
34
+ class LINEMODE < Wx::Enum
35
+ READY = self.new(0)
36
+ UNDERCONSTRUCTION = self.new(1)
37
+ SRCCHANGE = self.new(2)
38
+ TRGCHANGE = self.new(3)
39
+ end
40
+
41
+ property :src_shape_id, :trg_shape_id
42
+ property src_point: :serialize_src_point, trg_point: :serialize_trg_point
43
+ property :stand_alone, :src_arrow, :trg_arrow, :src_offset, :trg_offset,
44
+ :dock_point, :line_pen, :control_points
45
+
46
+ # @overload initialize()
47
+ # default constructor
48
+ # @overload initialize(src, trg, path, manager)
49
+ # @param [Wx::SF::Serializable::ID] src ID of the source shape
50
+ # @param [Wx::SF::Serializable::ID] trg ID of the target shape
51
+ # @param [Array<Wx::RealPoint>] path List of the line control points (can be empty)
52
+ # @param [Diagram] diagram containing diagram
53
+ # @overload initialize(src, trg, path, manager)
54
+ # @param [Wx::RealPoint] src starting line point
55
+ # @param [Wx::RealPoint] trg end line point
56
+ # @param [Array<Wx::RealPoint>,nil] path List of the line control points (can be empty or nil)
57
+ # @param [Diagram] diagram containing diagram
58
+ def initialize(*args)
59
+ if args.empty?
60
+ super()
61
+ @src_shape_id = @trg_shape_id = DEFAULT::UNKNOWNID
62
+ @src_point = DEFAULT::POINT.dup
63
+ @trg_point = DEFAULT::POINT.dup
64
+ @stand_alone = DEFAULT::STANDALONE
65
+ @lst_points = []
66
+ else
67
+ src, trg, path, diagram = args
68
+ super(Shape::DEFAULT::POSITION.dup, diagram)
69
+ if src.respond_to?(:to_real_point) && trg.respond_to?(:to_real_point)
70
+ @src_point = src.to_real_point
71
+ @trg_point = trg.to_real_point
72
+ @src_shape_id = @trg_shape_id = DEFAULT::UNKNOWNID
73
+ @stand_alone = true
74
+ elsif src.is_a?(Wx::SF::Serializable::ID) && trg.is_a?(Wx::SF::Serializable::ID)
75
+ @src_point = DEFAULT::POINT.dup
76
+ @trg_point = DEFAULT::POINT.dup
77
+ @src_shape_id = src
78
+ @trg_shape_id = trg
79
+ @stand_alone = false
80
+ else
81
+ ::Kernel.raise ArgumentError, "Invalid arguments #{args}"
82
+ end
83
+ path ||= []
84
+ @lst_points = path.select { |pt| pt.respond_to?(:to_real_point) }.collect { |pt| pt.to_real_point }
85
+ ::Kernel.raise ArgumentError, "Invalid arguments #{args}" unless path.size == @lst_points.size
86
+ end
87
+
88
+ @src_arrow = nil
89
+ @trg_arrow = nil
90
+
91
+ @dock_point = DEFAULT::DOCKPOINT
92
+ @pen = DEFAULT::PEN
93
+
94
+ @src_offset = DEFAULT::OFFSET.dup
95
+ @trg_offset = DEFAULT::OFFSET.dup
96
+
97
+ @mode = LINEMODE::READY
98
+ @prev_position = Wx::RealPoint.new
99
+ @unfinished_point = Wx::Point.new
100
+ end
101
+
102
+ # Get source shape id.
103
+ # @return [Wx::SF::Serializable::ID]
104
+ def get_src_shape_id
105
+ @src_shape_id
106
+ end
107
+ alias :src_shape_id :get_src_shape_id
108
+
109
+ # Set source shape id.
110
+ # @param [Wx::SF::Serializable::ID] id
111
+ def set_src_shape_id(id)
112
+ @src_shape_id = id
113
+ end
114
+ alias :src_shape_id= :set_src_shape_id
115
+
116
+ # Get target shape id.
117
+ # @return [Wx::SF::Serializable::ID]
118
+ def get_trg_shape_id
119
+ @trg_shape_id
120
+ end
121
+ alias :trg_shape_id :get_trg_shape_id
122
+
123
+ # Set target shape id.
124
+ # @param [Wx::SF::Serializable::ID] id
125
+ def set_trg_shape_id(id)
126
+ @trg_shape_id = id
127
+ end
128
+ alias :trg_shape_id= :set_trg_shape_id
129
+
130
+ # Get source point.
131
+ # @return [Wx::RealPoint]
132
+ def get_src_point
133
+ unless @stand_alone
134
+ src_shape = @diagram.find_shape(@src_shape_id)
135
+
136
+ if src_shape && !@lst_points.empty?
137
+ if src_shape.get_connection_points.empty?
138
+ return src_shape.get_border_point(get_mod_src_point, @lst_points.first)
139
+ else
140
+ return get_mod_src_point
141
+ end
142
+ else
143
+ if @mode != LINEMODE::UNDERCONSTRUCTION
144
+ pt1, _ = get_direct_line
145
+ else
146
+ pt1 = get_mod_src_point
147
+ end
148
+ return pt1
149
+ end
150
+
151
+ return Wx::RealPoint.new
152
+ end
153
+ @src_point
154
+ end
155
+ alias :src_point :get_src_point
156
+
157
+ # Set source point.
158
+ # @param [Wx::RealPoint] pt
159
+ def set_src_point(pt)
160
+ @src_point = pt.to_real_point
161
+ end
162
+
163
+ # Get target point.
164
+ # @return [Wx::RealPoint]
165
+ def get_trg_point
166
+ unless @stand_alone
167
+ trg_shape = @diagram.find_shape(@trg_shape_id)
168
+
169
+ if trg_shape && !@lst_points.empty?
170
+ if trg_shape.get_connection_points.empty?
171
+ return trg_shape.get_border_point(get_mod_trg_point, @lst_points.last)
172
+ else
173
+ return get_mod_trg_point
174
+ end
175
+ else
176
+ if @mode != LINEMODE::UNDERCONSTRUCTION
177
+ _, pt2 = get_direct_line
178
+ else
179
+ pt2 = @unfinished_point.to_real
180
+ end
181
+ return pt2
182
+ end
183
+
184
+ return Wx::RealPoint.new
185
+ end
186
+ @trg_point
187
+ end
188
+ alias :trg_point :get_trg_point
189
+
190
+ # Set target point.
191
+ # @param [Wx::RealPoint] pt
192
+ def set_trg_point(pt)
193
+ @trg_point = pt.to_real_point
194
+ end
195
+
196
+ # Get source arrow.
197
+ # @return [Wx::SF::ArrowBase]
198
+ def get_src_arrow
199
+ @src_arrow
200
+ end
201
+ alias :src_arrow :get_src_arrow
202
+
203
+ # Set source arrow
204
+ # @overload set_src_arrow(arrow)
205
+ # @param [Wx::SF::ArrowBase] arrow
206
+ # @return [Wx::SF::ArrowBase,nil] the new source arrow object if invalid
207
+ # @overload set_src_arrow(arrow_klass)
208
+ # @param [Class] arrow_klass
209
+ # @return [Wx::SF::ArrowBase,nil] the new source arrow object if invalid
210
+ def set_src_arrow(arg)
211
+ if (arg.is_a?(::Class) && arg < ArrowBase) || arg.is_a?(ArrowBase)
212
+ @src_arrow = arg.is_a?(ArrowBase) ? arg : arg.new
213
+ @src_arrow.set_parent_shape(self)
214
+ end
215
+ nil
216
+ end
217
+ alias :src_arrow= :set_src_arrow
218
+
219
+ # Get target arrow.
220
+ # @return [Wx::SF::ArrowBase]
221
+ def get_trg_arrow
222
+ @trg_arrow
223
+ end
224
+ alias :trg_arrow :get_trg_arrow
225
+
226
+ # Set target arrow
227
+ # @overload set_trg_arrow(arrow)
228
+ # @param [Wx::SF::ArrowBase] arrow
229
+ # @return [Wx::SF::ArrowBase,nil] the new source arrow object if invalid
230
+ # @overload set_trg_arrow(arrow_klass)
231
+ # @param [Class] arrow_klass
232
+ # @return [Wx::SF::ArrowBase,nil] the new source arrow object if invalid
233
+ def set_trg_arrow(arg)
234
+ if (arg.is_a?(::Class) && arg < ArrowBase) || arg.is_a?(ArrowBase)
235
+ @trg_arrow = arg.is_a?(ArrowBase) ? arg : arg.new
236
+ @trg_arrow.set_parent_shape(self)
237
+ end
238
+ nil
239
+ end
240
+ alias :trg_arrow= :set_trg_arrow
241
+
242
+ # Get line type
243
+ # @return [Wx::Pen]
244
+ def get_line_pen
245
+ @pen
246
+ end
247
+ alias :line_pen :get_line_pen
248
+
249
+ # Set line type
250
+ # @param [Wx::Pen] pen line type
251
+ def set_line_pen(pen)
252
+ @pen = pen
253
+ end
254
+ alias :line_pen= :set_line_pen
255
+
256
+ # Set the line dock point. It is a zero based index of the line
257
+ # control point which will act as the shape position (value returned by Shape#get_relative_position function).
258
+ # @param [Integer] index Zero based index of the line control point
259
+ def set_dock_point(index)
260
+ @dock_point = index
261
+ end
262
+ alias :dock_point= :set_dock_point
263
+
264
+ # Get the line dock point. It is a zero based index of the line
265
+ # control point which will act as the shape position (value returned by Shape#get_relative_position function).
266
+ # @return [Integer] Zero based index of the line control point (-1 means UNDEFINED)
267
+ def get_dock_point
268
+ @dock_point
269
+ end
270
+
271
+ # Returns true if stand alone line
272
+ # @return [Boolean]
273
+ def get_stand_alone
274
+ @stand_alone
275
+ end
276
+ alias :stand_alone? :get_stand_alone
277
+
278
+ # Set stand alone line mode
279
+ # @param [Boolean] f flag
280
+ def set_stand_alone(f = true)
281
+ @stand_alone = !!f
282
+ end
283
+ alias :stand_alone= :set_stand_alone
284
+
285
+ # Get starting and ending line points.
286
+ # @return [Array(Wx::RealPoint, Wx::RealPoint)] starting line point and ending line point
287
+ def get_direct_line
288
+ if @stand_alone
289
+ return [@src_point, @trg_point]
290
+ else
291
+ src_shape = get_diagram.find_shape(@src_shape_id)
292
+ trg_shape = get_diagram.find_shape(@trg_shape_id)
293
+
294
+ if src_shape && trg_shape
295
+ trg_center = get_mod_trg_point
296
+ src_center = get_mod_src_point
297
+
298
+ if src_shape.get_parent_shape == trg_shape || trg_shape.get_parent_shape == src_shape
299
+ trg_bb = trg_shape.get_bounding_box
300
+ src_bb = src_shape.get_bounding_box
301
+
302
+ if trg_bb.contains?(src_center.x.to_i, src_center.y.to_i)
303
+ if src_center.y > trg_center.y
304
+ src = Wx::RealPoint.new(src_center.x, src_bb.bottom.to_f)
305
+ trg = Wx::RealPoint.new(src_center.x, trg_bb.bottom.to_f)
306
+ else
307
+ src = Wx::RealPoint.new(src_center.x, src_bb.top.to_f)
308
+ trg = Wx::RealPoint.new(src_center.x, trg_bb.top.to_f)
309
+ end
310
+ return [src, trg]
311
+ elsif src_bb.contains?(trg_center.x.to_i, trg_center.y.to_i)
312
+ if trg_center.y > src_center.y
313
+ src = Wx::RealPoint.new(trg_center.x, src_bb.bottom.to_f)
314
+ trg = Wx::RealPoint.new(trg_center.x, trg_bb.bottom.to_f)
315
+ else
316
+ src = Wx::RealPoint.new(trg_center.x, src_bb.top.to_f)
317
+ trg = Wx::RealPoint.new(trg_center.x, trg_bb.top.to_f)
318
+ end
319
+ return [src, trg]
320
+ end
321
+ end
322
+
323
+ if src_shape.get_connection_points.empty?
324
+ src = src_shape.get_border_point(src_center, trg_center)
325
+ else
326
+ src = src_center
327
+ end
328
+
329
+ if trg_shape.get_connection_points.empty?
330
+ trg = trg_shape.get_border_point(trg_center, src_center)
331
+ else
332
+ trg = trg_center
333
+ end
334
+ return [src, trg]
335
+ end
336
+ end
337
+ nil # should not happen
338
+ end
339
+
340
+ # Get a list of the line's control points (their positions).
341
+ # @return [Array<Wx::RealPoint>] List of control points' positions
342
+ def get_control_points
343
+ @lst_points
344
+ end
345
+
346
+ # Get a position of given line dock point.
347
+ # @param [Integer] dp Dock point
348
+ # @return [Wx::RealPoint] The dock point's position if exists, otherwise the line center
349
+ def get_dock_point_position(dp)
350
+ pts_cnt = @lst_points.size
351
+
352
+ if dp >= 0
353
+ if pts_cnt > dp
354
+ return @lst_points[dp]
355
+ elsif pts_cnt > 0
356
+ return @lst_points[pts_cnt/2]
357
+ end
358
+ elsif dp == -1 # start line point
359
+ return get_src_point
360
+ elsif dp == -2 # end line point
361
+ return get_trg_point
362
+ end
363
+
364
+ get_center
365
+ end
366
+
367
+ # Initialize line's starting point with existing fixed connection point.
368
+ # @param [Wx::SF::ConnectionPoint] cp Pointer to connection point
369
+ def set_starting_connection_point(cp)
370
+ if cp && cp.get_parent_shape
371
+ pos_cp = cp.get_connection_point
372
+ rct_bb = cp.get_parent_shape.get_bounding_box
373
+
374
+ @src_offset.x = (pos_cp.x - rct_bb.left).to_f / rct_bb.width
375
+ @src_offset.y = (pos_cp.y - rct_bb.top).to_f / rct_bb.height
376
+ end
377
+ end
378
+
379
+ # Initialize line's ending point with existing fixed connection point.
380
+ # @param [Wx::SF::ConnectionPoint] cp Pointer to connection point
381
+ def set_ending_connection_point(cp)
382
+ if cp && cp.get_parent_shape
383
+ pos_cp = cp.get_connection_point
384
+ rct_bb = cp.get_parent_shape.get_bounding_box
385
+
386
+ @trg_offset.x = (pos_cp.x - rct_bb.left).to_f / rct_bb.width
387
+ @trg_offset.y = (pos_cp.y - rct_bb.top).to_f / rct_bb.height
388
+ end
389
+ end
390
+
391
+ # Get number of line segments for this shape.
392
+ # @return [Integer] number of line segments
393
+ def get_line_segment_count
394
+ @lst_points.size+1
395
+ end
396
+ alias :line_segment_count :get_line_segment_count
397
+ alias :segment_count :get_line_segment_count
398
+
399
+ # Get starting and ending point of line segment defined by its index.
400
+ # @param [Integer] index Index of desired line segment
401
+ # @return [Array(Wx::RealPoint,Wx::RealPoint)] starting and ending point of line segment
402
+ def get_line_segment(index)
403
+ if @lst_points.empty?
404
+ return get_direct_line if index == 0
405
+ else
406
+ if index == 0
407
+ return [get_src_point, @lst_points.first.dup]
408
+ elsif index == @lst_points.size
409
+ return [@lst_points.last.dup, get_trg_point]
410
+ elsif index > 0 && index < @lst_points.size
411
+ return @lst_points[index-1, 2].collect {|p| p.dup}
412
+ end
413
+ end
414
+ [Wx::RealPoint.new, Wx::RealPoint.new]
415
+ end
416
+
417
+ # Get line's bounding box. The function can be overridden if necessary.
418
+ # @return [Wx::Rect] Bounding rectangle
419
+ def get_bounding_box
420
+ line_rct = nil
421
+
422
+ # calculate control points area if they exist
423
+ if !@lst_points.empty?
424
+ prev_pt = get_src_point
425
+
426
+ @lst_points.each do |pt|
427
+ if line_rct.nil?
428
+ line_rct = Wx::Rect.new(prev_pt.to_point, pt.to_point)
429
+ else
430
+ line_rct.union!(Wx::Rect.new(prev_pt.to_point, pt.to_point))
431
+ end
432
+ prev_pt = pt
433
+ end
434
+
435
+ line_rct.union!(Wx::Rect.new(prev_pt.to_point, get_trg_point.to_point))
436
+ else
437
+ # include starting point
438
+ pt = get_src_point
439
+ line_rct = Wx::Rect.new(pt.x.to_i, pt.y.to_i, 1, 1)
440
+
441
+ # include ending point
442
+ pt = get_trg_point
443
+ line_rct.union!(Wx::Rect.new(pt.x.to_i, pt.y.to_i, 1, 1))
444
+ end
445
+
446
+ # include unfinished point if the line is under construction
447
+ if @mode == LINEMODE::UNDERCONSTRUCTION || @mode == LINEMODE::SRCCHANGE || @mode == LINEMODE::TRGCHANGE
448
+ if line_rct.nil?
449
+ line_rct = Wx::Rect.new(@unfinished_point.x, @unfinished_point.y, 1, 1)
450
+ else
451
+ line_rct.union!(Wx::Rect.new(@unfinished_point.x, @unfinished_point.y, 1, 1))
452
+ end
453
+ end
454
+
455
+ line_rct ? line_rct : Wx::Rect.new
456
+ end
457
+
458
+ # Get the shape's absolute position in the canvas.
459
+ # @return [Wx::RealPoint] Shape's position
460
+ def get_absolute_position
461
+ get_dock_point_position(@dock_point)
462
+ end
463
+
464
+ # Get intersection point of the shape border and a line leading from
465
+ # 'start_pt' point to 'end_pt' point. The function can be overridden if necessary.
466
+ # @param [Wx::RealPoint] _start_pt Starting point of the virtual intersection line
467
+ # @param [Wx::RealPoint] _end_pt Ending point of the virtual intersection line
468
+ # @return [Wx::RealPoint] Intersection point
469
+ def get_border_point(_start_pt, _end_pt)
470
+ get_absolute_position
471
+ end
472
+
473
+ # Test whether the given point is inside the shape. The function
474
+ # can be overridden if necessary.
475
+ # @param pos Examined point
476
+ # @return TRUE if the point is inside the shape area, otherwise FALSE
477
+ def contains?(pos)
478
+ return true if @mode != LINEMODE::UNDERCONSTRUCTION && get_hit_linesegment(pos) >= 0
479
+ false
480
+ end
481
+
482
+ # Move the shape to the given absolute position. The function
483
+ # can be overridden if necessary.
484
+ # @param [Float] x X coordinate
485
+ # @param [Float] y Y coordinate
486
+ def move_to(x, y)
487
+ move_by(x - @prev_position.x, y - @prev_position.y)
488
+ @prev_position.x = x
489
+ @prev_position.y = y
490
+ end
491
+
492
+ # Move the shape by the given offset. The function
493
+ # can be overridden if necessary.
494
+ # @param [Float] x X offset
495
+ # @param [Float] y Y offset
496
+ def move_by(x, y)
497
+ @lst_points.each do |pt|
498
+ pt.x += x
499
+ pt.y += y
500
+ end
501
+
502
+ if @stand_alone
503
+ @src_point += [x, y]
504
+ @trg_point += [x, y]
505
+ end
506
+
507
+ update unless @child_shapes.empty?
508
+
509
+ get_diagram.set_modified if get_diagram
510
+ end
511
+
512
+ # Function called by the framework responsible for creation of shape handles
513
+ # at the creation time. The function can be overridden if necessary.
514
+ def create_handles
515
+ # first clear all previously used handles and then create new ones
516
+ @handles.clear
517
+
518
+ # create control points handles
519
+ @lst_points.size.times { |i| add_handle(Shape::Handle::TYPE::LINECTRL, i) }
520
+
521
+ # create border handles
522
+ add_handle(Shape::Handle::TYPE::LINESTART)
523
+ add_handle(Shape::Handle::TYPE::LINEEND)
524
+ end
525
+
526
+ # Event handler called during dragging of the shape handle.
527
+ # The function can be overridden if necessary.
528
+ #
529
+ # The function is called by the framework (by the shape canvas).
530
+ # @param [Wx::SF::Shape::Handle] handle Reference to dragged handle
531
+ def on_handle(handle)
532
+ case handle.type
533
+ when Shape::Handle::TYPE::LINECTRL
534
+ pt = @lst_points[handle.id]
535
+ if pt
536
+ pt.x = handle.get_position.x
537
+ pt.y = handle.get_position.y
538
+ end
539
+
540
+ when Shape::Handle::TYPE::LINEEND
541
+ @unfinished_point = handle.get_position
542
+ @trg_point = handle.get_position.to_real if @stand_alone
543
+
544
+ when Shape::Handle::TYPE::LINESTART
545
+ @unfinished_point = handle.get_position
546
+ @src_point = handle.get_position.to_real if @stand_alone
547
+ end
548
+
549
+ super
550
+ end
551
+
552
+ # Event handler called when the user finished dragging of the shape handle.
553
+ # The function can be overridden if necessary.
554
+ #
555
+ # The function is called by the framework (by the shape canvas).
556
+ # Default implementation does nothing.
557
+ # @param [Wx::SF::Shape::Handle] handle Reference to dragged handle
558
+ def on_end_handle(handle)
559
+ # update percentual offset of the line's ending points
560
+ parent = get_parent_canvas.get_shape_under_cursor
561
+
562
+ if parent && !@stand_alone
563
+ bb_rect = parent.get_bounding_box
564
+
565
+ case handle.type
566
+ when Shape::Handle::TYPE::LINESTART
567
+ if parent.id == @src_shape_id
568
+ @src_offset.x = (handle.get_position.x - bb_rect.left).to_f / bb_rect.width
569
+ @src_offset.y = (handle.get_position.y - bb_rect.top).to_f / bb_rect.height
570
+ end
571
+
572
+ when Shape::Handle::TYPE::LINEEND
573
+ if parent.id == @trg_shape_id
574
+ @trg_offset.x = (handle.get_position.x - bb_rect.left).to_f / bb_rect.width
575
+ @trg_offset.y = (handle.get_position.y - bb_rect.top).to_f / bb_rect.height
576
+ end
577
+ end
578
+ end
579
+
580
+ super
581
+ end
582
+
583
+ # Event handler called at the beginning of the shape dragging process.
584
+ # The function can be overridden if necessary.
585
+ #
586
+ # The function is called by the framework (by the shape canvas).
587
+ # @param [Wx::Point] pos Current mouse position
588
+ # @see Wx::SF::ShapeCanvas
589
+ def on_begin_drag(pos)
590
+ @prev_position = get_absolute_position
591
+
592
+ super
593
+ end
594
+
595
+ # Event handler called when the shape is double-clicked by
596
+ # the left mouse button. The function can be overridden if necessary.
597
+ #
598
+ # The function is called by the framework (by the shape canvas).
599
+ # @param [Wx::Point] pos Current mouse position
600
+ # @see Wx::SF::ShapeCanvas
601
+ def on_left_double_click(pos)
602
+ # HINT: override it for custom actions
603
+
604
+ if get_parent_canvas
605
+ # remove existing handle if exist otherwise create a new one at the
606
+ # given position
607
+ handle = get_parent_canvas.get_topmost_handle_at_position(pos)
608
+ if handle && handle.get_parent_shape == self
609
+ if handle.type == Shape::Handle::TYPE::LINECTRL
610
+ if has_style?(STYLE::EMIT_EVENTS)
611
+ evt = Wx::SF::ShapeHandleEvent.new(EVT_SF_LINE_HANDLE_REMOVE, id)
612
+ evt.set_shape(self)
613
+ evt.set_handle(handle)
614
+ get_parent_canvas.get_event_handler.process_event(evt)
615
+ end
616
+
617
+ @lst_points.delete_at(handle.id)
618
+
619
+ create_handles
620
+ show_handles(true)
621
+ end
622
+ else
623
+ index = get_hit_linesegment(pos)
624
+ if index > -1
625
+ @lst_points.insert(index, Wx::RealPoint.new(pos.x, pos.y))
626
+
627
+ create_handles
628
+ show_handles(true)
629
+
630
+ if has_style?(STYLE::EMIT_EVENTS)
631
+ handle = get_parent_canvas.get_topmost_handle_at_position(pos)
632
+ if handle
633
+ evt = ShapeHandleEvent.new(EVT_SF_LINE_HANDLE_ADD, id)
634
+ evt.set_shape(this)
635
+ evt.set_handle(handle)
636
+ get_parent_canvas.get_event_handler.process_event(evt)
637
+ end
638
+ end
639
+ end
640
+ end
641
+ end
642
+ end
643
+
644
+ # Scale the shape size by in both directions. The function can be overridden if necessary
645
+ # (new implementation should call default one or scale shape's children manually if necessary).
646
+ # @param [Float] x Horizontal scale factor
647
+ # @param [Float] y Vertical scale factor
648
+ # @param [Boolean] children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via update() function.
649
+ def scale(x, y, children = WITHCHILDREN)
650
+ @lst_points.each do |pt|
651
+ pt.x *= x
652
+ pt.y *= y
653
+ end
654
+
655
+ # call default function implementation (needed for scaling of shape's children)
656
+ super
657
+ end
658
+
659
+ # Get current working mode.
660
+ # @return [LINEMODE] Current working mode
661
+ # @see LINEMODE
662
+ def get_line_mode
663
+ @mode
664
+ end
665
+
666
+ protected
667
+
668
+ # Draw the shape in the normal way. The function can be overridden if necessary.
669
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
670
+ def draw_normal(dc)
671
+ dc.with_pen(@pen) do
672
+ draw_complete_line(dc)
673
+ end
674
+ end
675
+
676
+ # Draw the shape in the hower mode (the mouse cursor is above the shape).
677
+ # The function can be overridden if necessary.
678
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
679
+ def draw_hover(dc)
680
+ dc.with_pen(Wx::Pen.new(@hover_color, 1)) do
681
+ draw_complete_line(dc)
682
+ end
683
+ end
684
+
685
+ # Draw the shape in the highlighted mode (another shape is dragged over this
686
+ # shape and this shape will accept the dragged one if it will be dropped on it).
687
+ # The function can be overridden if necessary.
688
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
689
+ def draw_highlighted(dc)
690
+ dc.with_pen(Wx::Pen.new(@hover_color, 2)) do
691
+ draw_complete_line(dc)
692
+ end
693
+ end
694
+
695
+ # Draw completed line.
696
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
697
+ def draw_complete_line(dc)
698
+ return unless diagram
699
+
700
+ case @mode
701
+ when LINEMODE::READY
702
+ # draw basic line parts
703
+ src = trg = nil
704
+ line_segment_count.times do |i|
705
+ src, trg = get_line_segment(i)
706
+ dc.draw_line(src.to_point, trg.to_point)
707
+ end
708
+ # draw target arrow
709
+ @trg_arrow.draw(src, trg, dc) if @trg_arrow
710
+ # draw source arrow
711
+ if @src_arrow
712
+ src, trg = get_line_segment(0)
713
+ @src_arrow.draw(trg, src, dc)
714
+ end
715
+
716
+ when LINEMODE::UNDERCONSTRUCTION
717
+ # draw basic line parts
718
+ src = trg = nil
719
+ @lst_points.size.times do |i|
720
+ src, trg = get_line_segment(i)
721
+ dc.draw_line(src.to_point, trg.to_point)
722
+ end
723
+ # draw unfinished line segment if any (for interactive line creation)
724
+ dc.with_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PENSTYLE_DOT)) do
725
+ if @lst_points.size > 0
726
+ dc.draw_line(trg, @unfinished_point)
727
+ else
728
+ src_shape = diagram.find_shape(@src_shape_id)
729
+ if src_shape
730
+ if src_shape.get_connection_points.empty?
731
+ dc.draw_line((src_shape.get_border_point(src_shape.get_center, @unfinished_point.to_real)).to_point,
732
+ @unfinished_point)
733
+ else
734
+ dc.draw_line(get_mod_src_point.to_point, @unfinished_point)
735
+ end
736
+ end
737
+ end
738
+ end
739
+
740
+ when LINEMODE::SRCCHANGE
741
+ # draw basic line parts
742
+ src = trg = nil
743
+ @lst_points.size.times do |i|
744
+ src, trg = get_line_segment(i+1)
745
+ dc.draw_line(src.to_point, trg.to_point)
746
+ end
747
+
748
+ # draw linesegment being updated
749
+ src, trg = get_line_segment(0)
750
+
751
+ dc.set_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PENSTYLE_DOT)) unless @stand_alone
752
+ dc.draw_line(@unfinished_point, trg.to_point)
753
+ dc.set_pen(Wx::NULL_PEN) unless @stand_alone
754
+
755
+ when LINEMODE::TRGCHANGE
756
+ # draw basic line parts
757
+ src = trg = nil
758
+ if @lst_points.empty?
759
+ trg = get_src_point
760
+ else
761
+ @lst_points.size.times do |i|
762
+ src, trg = get_line_segment(i)
763
+ dc.draw_line(src.to_point, trg.to_point)
764
+ end
765
+ end
766
+ # draw linesegment being updated
767
+ dc.set_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PENSTYLE_DOT)) unless @stand_alone
768
+ dc.draw_line(trg.to_point, @unfinished_point)
769
+ dc.set_pen(Wx::NULL_PEN) unless @stand_alone
770
+ end
771
+ end
772
+
773
+ # Get index of the line segment intersecting the given point.
774
+ # @param [Wx::Point] pos Examined point
775
+ # @return [Integer] Zero-based index of line segment located under the given point
776
+ def get_hit_linesegment(pos)
777
+ return -1 unless get_bounding_box.contains?(pos)
778
+
779
+ pos = pos.to_point
780
+ # Get all polyline segments
781
+ line_segment_count.times do |i|
782
+ src, trg = get_line_segment(i)
783
+
784
+ # calculate line segment bounding box
785
+ ls_bb = Wx::Rect.new(src.to_point, trg.to_point)
786
+ ls_bb.inflate!(2)
787
+
788
+ # convert line segment to its parametric form
789
+ a = trg.y - src.y
790
+ b = src.x - trg.x
791
+ c = -a*src.x - b*src.y
792
+
793
+ # calculate distance of the line and give point
794
+ d = (a*pos.x + b*pos.y + c)/::Math.sqrt(a*a + b*b)
795
+ # NaN will be the result if src and trg are equal
796
+ # (which can happen for lines between parent and child shapes)
797
+ return i if (d.nan? || d.to_i.abs <= 5) && ls_bb.contains?(pos)
798
+ end
799
+
800
+ -1
801
+ end
802
+
803
+ # Set line shape's working mode.
804
+ # @param [LINEMODE] mode Working mode
805
+ # @see LINEMODE
806
+ def set_line_mode(mode)
807
+ @mode = mode
808
+ end
809
+
810
+ # Set next potential control point position (useful in LINEMODE::UNDERCONSTRUCTION working mode).
811
+ # @param [Wx::Point] pos New potential control point position
812
+ # @see LINEMODE
813
+ def set_unfinished_point(pos)
814
+ @unfinished_point = pos.to_point
815
+ end
816
+
817
+ # Get modified starting line point .
818
+ # @return [Wx::RealPoint] Modified starting line point
819
+ def get_mod_src_point
820
+ src_shape = diagram.find_shape(@src_shape_id)
821
+ return Wx::RealPoint.new unless src_shape
822
+
823
+ if @src_offset != DEFAULT::OFFSET
824
+ bb_rct = src_shape.get_bounding_box
825
+ mod_point = src_shape.get_absolute_position
826
+
827
+ mod_point.x += bb_rct.width.to_f * @src_offset.x
828
+ mod_point.y += bb_rct.height.to_f * @src_offset.y
829
+ else
830
+ mod_point = src_shape.get_center
831
+ end
832
+
833
+ conn_pt = src_shape.get_nearest_connection_point(mod_point)
834
+ mod_point = conn_pt.get_connection_point if conn_pt
835
+
836
+ mod_point
837
+ end
838
+
839
+ # Get modified ending line point .
840
+ # @return [Wx::RealPoint] Modified ending line point
841
+ def get_mod_trg_point
842
+ trg_shape = diagram.find_shape(@trg_shape_id)
843
+ return Wx::RealPoint.new unless trg_shape
844
+
845
+ if @trg_offset != DEFAULT::OFFSET
846
+ bb_rct = trg_shape.get_bounding_box
847
+ mod_point = trg_shape.get_absolute_position
848
+
849
+ mod_point.x += bb_rct.width.to_f * @trg_offset.x
850
+ mod_point.y += bb_rct.height.to_f * @trg_offset.y
851
+ else
852
+ mod_point = trg_shape.get_center
853
+ end
854
+
855
+ conn_pt = trg_shape.get_nearest_connection_point(mod_point)
856
+ mod_point = conn_pt.get_connection_point if conn_pt
857
+
858
+ mod_point
859
+ end
860
+
861
+ private
862
+
863
+ # Set control points. Deserialization only.
864
+ # @param [Array<Wx::RealPoint>] pts
865
+ def set_control_points(pts)
866
+ @lst_points.replace(pts)
867
+ end
868
+
869
+ # Serialization only
870
+ # @return [Wx::RealPoint]
871
+ def get_src_offset
872
+ @src_offset
873
+ end
874
+
875
+ # Deserialization only
876
+ # @param [Wx::RealPoint] offs
877
+ def set_src_offset(offs)
878
+ @src_offset = offs.to_real_point
879
+ end
880
+
881
+ # Serialization only
882
+ # @return [Wx::RealPoint]
883
+ def get_trg_offset
884
+ @trg_offset
885
+ end
886
+
887
+ # Deserialization only
888
+ # @param [Wx::RealPoint] offs
889
+ def set_trg_offset(offs)
890
+ @trg_offset = offs.to_real_point
891
+ end
892
+
893
+ # (De-)Serialization only
894
+ def serialize_src_point(*arg)
895
+ @src_point = arg.shift unless arg.empty?
896
+ @src_point
897
+ end
898
+
899
+ # (De-)Serialization only
900
+ def serialize_trg_point(*arg)
901
+ @trg_point = arg.shift unless arg.empty?
902
+ @trg_point
903
+ end
904
+
905
+ end
906
+
907
+ end