wxruby3-shapes 0.9.0.pre.beta.3

Sign up to get free protection for your applications and to get access to all the features.
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