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.
- checksums.yaml +7 -0
- data/.yardopts +12 -0
- data/CREDITS.md +18 -0
- data/INSTALL.md +39 -0
- data/LICENSE +21 -0
- data/README.md +118 -0
- data/assets/screenshot.png +0 -0
- data/bin/wx-shapes +9 -0
- data/lib/wx/shapes/arrow_base.rb +86 -0
- data/lib/wx/shapes/arrows/circle_arrow.rb +39 -0
- data/lib/wx/shapes/arrows/diamond_arrow.rb +33 -0
- data/lib/wx/shapes/arrows/open_arrow.rb +56 -0
- data/lib/wx/shapes/arrows/solid_arrow.rb +69 -0
- data/lib/wx/shapes/art/shape_canvas/page.xpm +73 -0
- data/lib/wx/shapes/auto_layout.rb +358 -0
- data/lib/wx/shapes/base.rb +33 -0
- data/lib/wx/shapes/canvas_history.rb +84 -0
- data/lib/wx/shapes/connection_point.rb +238 -0
- data/lib/wx/shapes/core.rb +19 -0
- data/lib/wx/shapes/diagram.rb +659 -0
- data/lib/wx/shapes/events.rb +389 -0
- data/lib/wx/shapes/printout.rb +136 -0
- data/lib/wx/shapes/serializable.rb +440 -0
- data/lib/wx/shapes/serialize/core.rb +40 -0
- data/lib/wx/shapes/serialize/id.rb +82 -0
- data/lib/wx/shapes/serialize/wx.rb +104 -0
- data/lib/wx/shapes/serializer/json.rb +258 -0
- data/lib/wx/shapes/serializer/yaml.rb +125 -0
- data/lib/wx/shapes/shape.rb +2129 -0
- data/lib/wx/shapes/shape_canvas.rb +3285 -0
- data/lib/wx/shapes/shape_data_object.rb +43 -0
- data/lib/wx/shapes/shape_handle.rb +287 -0
- data/lib/wx/shapes/shape_list.rb +161 -0
- data/lib/wx/shapes/shapes/bitmap_shape.rb +257 -0
- data/lib/wx/shapes/shapes/circle_shape.rb +136 -0
- data/lib/wx/shapes/shapes/control_shape.rb +483 -0
- data/lib/wx/shapes/shapes/curve_shape.rb +231 -0
- data/lib/wx/shapes/shapes/diamond_shape.rb +62 -0
- data/lib/wx/shapes/shapes/edit_text_shape.rb +317 -0
- data/lib/wx/shapes/shapes/ellipse_shape.rb +106 -0
- data/lib/wx/shapes/shapes/flex_grid_shape.rb +78 -0
- data/lib/wx/shapes/shapes/grid_shape.rb +404 -0
- data/lib/wx/shapes/shapes/line_shape.rb +907 -0
- data/lib/wx/shapes/shapes/multi_sel_rect.rb +214 -0
- data/lib/wx/shapes/shapes/ortho_shape.rb +357 -0
- data/lib/wx/shapes/shapes/polygon_shape.rb +294 -0
- data/lib/wx/shapes/shapes/rect_shape.rb +378 -0
- data/lib/wx/shapes/shapes/round_ortho_shape.rb +131 -0
- data/lib/wx/shapes/shapes/round_rect_shape.rb +142 -0
- data/lib/wx/shapes/shapes/square_shape.rb +119 -0
- data/lib/wx/shapes/shapes/text_shape.rb +324 -0
- data/lib/wx/shapes/thumbnail.rb +234 -0
- data/lib/wx/shapes/version.rb +12 -0
- data/lib/wx/shapes/wx.rb +29 -0
- data/lib/wx/shapes.rb +18 -0
- data/lib/wx/wx-shapes/base.rb +87 -0
- data/lib/wx/wx-shapes/cmd/sampler.rb +58 -0
- data/lib/wx/wx-shapes/cmd/test.rb +27 -0
- data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +7 -0
- data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
- data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
- data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
- data/rakelib/yard/yard-custom-templates.rb +2 -0
- data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
- data/samples/demo/art/AlignBottom.xpm +35 -0
- data/samples/demo/art/AlignCenter.xpm +35 -0
- data/samples/demo/art/AlignLeft.xpm +35 -0
- data/samples/demo/art/AlignMiddle.xpm +35 -0
- data/samples/demo/art/AlignRight.xpm +35 -0
- data/samples/demo/art/AlignTop.xpm +35 -0
- data/samples/demo/art/Bitmap.xpm +25 -0
- data/samples/demo/art/Circle.xpm +22 -0
- data/samples/demo/art/Curve.xpm +21 -0
- data/samples/demo/art/Diamond.xpm +22 -0
- data/samples/demo/art/EditText.xpm +21 -0
- data/samples/demo/art/Ellipse.xpm +22 -0
- data/samples/demo/art/FixedRect.xpm +22 -0
- data/samples/demo/art/FlexGrid.xpm +22 -0
- data/samples/demo/art/GC.xpm +23 -0
- data/samples/demo/art/Grid.xpm +22 -0
- data/samples/demo/art/Line.xpm +21 -0
- data/samples/demo/art/NoSource.xpm +69 -0
- data/samples/demo/art/OrthoLine.xpm +21 -0
- data/samples/demo/art/Rect.xpm +22 -0
- data/samples/demo/art/RoundOrthoLine.xpm +21 -0
- data/samples/demo/art/RoundRect.xpm +22 -0
- data/samples/demo/art/Shadow.xpm +23 -0
- data/samples/demo/art/StandAloneLine.xpm +22 -0
- data/samples/demo/art/Text.xpm +21 -0
- data/samples/demo/art/Tool.xpm +23 -0
- data/samples/demo/art/sample.xpm +251 -0
- data/samples/demo/demo.rb +658 -0
- data/samples/demo/frame_canvas.rb +422 -0
- data/samples/demo/images/motyl.bmp +0 -0
- data/samples/demo/images/motyl2.bmp +0 -0
- data/samples/sample1/art/sample.xpm +251 -0
- data/samples/sample1/sample.rb +263 -0
- data/samples/sample2/art/sample.xpm +251 -0
- data/samples/sample2/sample.rb +133 -0
- data/samples/sample2/sample_canvas.rb +35 -0
- data/samples/sample2/sample_shape.rb +108 -0
- data/samples/sample3/art/sample.xpm +251 -0
- data/samples/sample3/sample.rb +281 -0
- data/samples/sample4/art/sample.xpm +251 -0
- data/samples/sample4/sample.rb +180 -0
- data/tests/art/motyl.bmp +0 -0
- data/tests/lib/wxapp_runner.rb +64 -0
- data/tests/serializer_tests.rb +521 -0
- data/tests/test_grid_shapes.rb +42 -0
- data/tests/test_serialize.rb +7 -0
- data/tests/test_serialize_yaml.rb +17 -0
- metadata +242 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Wx::SF - layout algorithm classes
|
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
|
3
|
+
|
|
4
|
+
module Wx::SF
|
|
5
|
+
|
|
6
|
+
# Base class for all layouting algorithms.
|
|
7
|
+
class LayoutAlgorithm
|
|
8
|
+
|
|
9
|
+
# Function performing the layout change. All derived classes must implement it.
|
|
10
|
+
# @param [Array<Shape>] shapes List of shapes which should be layout-ed
|
|
11
|
+
def do_layout(shapes)
|
|
12
|
+
::Kernel.raise NotImplementedError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
|
|
17
|
+
# Calculate bounding box surrounding given shapes.
|
|
18
|
+
# @param [Array<Shape>] shapes List of shapes
|
|
19
|
+
# @return [Wx::Rect] Bounding box
|
|
20
|
+
def get_bounding_box(shapes)
|
|
21
|
+
shapes.inject(nil) do |rct_bb, shape|
|
|
22
|
+
rct_bb ? rct_bb.union!(shape.get_bounding_box) : shape.get_bounding_box
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Get overall extent of all given shapes calculated as a sum of their width and height.
|
|
27
|
+
# @param [Array<Shape>] shapes List of shapes
|
|
28
|
+
# @return [Wx::Size] Overall shapes extent
|
|
29
|
+
def get_shapes_extent(shapes)
|
|
30
|
+
shapes.inject(Wx::Size.new) do |ext_sz, shape|
|
|
31
|
+
sh_bb = shape.get_bounding_box
|
|
32
|
+
ext_sz.width += sh_bb.width
|
|
33
|
+
ext_sz.height += sh_bb.height
|
|
34
|
+
ext_sz
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get center point of given shapes.
|
|
39
|
+
# @param [Array<Shape>] shapes List of shapes
|
|
40
|
+
# @return [Wx::RealPoint] Center point
|
|
41
|
+
def get_shapes_center(shapes)
|
|
42
|
+
center = shapes.inject(Wx::RealPoint.new) do |centre, shape|
|
|
43
|
+
sh_pos = shape.get_absolute_position
|
|
44
|
+
centre.x += sh_pos.x
|
|
45
|
+
centre.y += sh_pos.y
|
|
46
|
+
centre
|
|
47
|
+
end
|
|
48
|
+
center.x /= shapes.size
|
|
49
|
+
center.y /= shapes.size
|
|
50
|
+
center
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get top-left point of bounding box surrounding given shapes.
|
|
54
|
+
# @param [Array<Shape>] shapes List of shapes
|
|
55
|
+
# @return [Wx::RealPoint] Top-left point of bounding box surrounding given shapes
|
|
56
|
+
def get_top_left(shapes)
|
|
57
|
+
shapes.inject(Wx::RealPoint.new(::Float::MAX, ::Float::MAX)) do |pos, shape|
|
|
58
|
+
sh_pos = shape.get_absolute_position
|
|
59
|
+
pos.x = sh_pos.x if sh_pos.x < pos.x
|
|
60
|
+
pos.y = sh_pos.y if sh_pos.y < pos.y
|
|
61
|
+
pos
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# pre-defined algorithms ###########################
|
|
69
|
+
|
|
70
|
+
# Class encapsulating algorithm which layouts all top-most shapes into circle registered under "Circle" name.
|
|
71
|
+
# The algorithm doesn't optimize connection lines crossing.
|
|
72
|
+
class LayoutCircle < LayoutAlgorithm
|
|
73
|
+
|
|
74
|
+
# Constructor.
|
|
75
|
+
def initialize
|
|
76
|
+
@distance_ratio = 1.0
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get or set ratio in which calculated distance between shapes will be reduced. Values less than
|
|
80
|
+
# 1 means that the distance will be smaller, values bigger than 1 means that the distance will be
|
|
81
|
+
# bigger.
|
|
82
|
+
attr_accessor :distance_ratio
|
|
83
|
+
|
|
84
|
+
# Function performing the layout change.
|
|
85
|
+
# @param [Array<Shape>] shapes List of shapes which should be layout-ed
|
|
86
|
+
def do_layout(shapes)
|
|
87
|
+
size_shapes = get_shapes_extent(shapes)
|
|
88
|
+
center = get_shapes_center(shapes)
|
|
89
|
+
|
|
90
|
+
# double x, y
|
|
91
|
+
step = 360.0 / shapes.size
|
|
92
|
+
degree = 0
|
|
93
|
+
rx = (size_shapes.x / 2) * @distance_ratio
|
|
94
|
+
ry = (size_shapes.y / 2) * @distance_ratio
|
|
95
|
+
|
|
96
|
+
shapes.each do |shape|
|
|
97
|
+
x = center.x + Math.cos(degree * Math::PI / 180 ) * rx
|
|
98
|
+
y = center.y + Math.sin( degree * Math::PI / 180 ) * ry
|
|
99
|
+
degree += step
|
|
100
|
+
shape.move_to(x, y)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Class encapsulating algorithm which layouts all top-most shapes into vertical tree registered under "Vertical Tree" name.
|
|
107
|
+
class LayoutVerticalTree < LayoutAlgorithm
|
|
108
|
+
|
|
109
|
+
# Constructor.
|
|
110
|
+
def initialize
|
|
111
|
+
@h_space = @v_space = 30.0
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get or set horizontal space between shapes.
|
|
115
|
+
attr_accessor :h_space
|
|
116
|
+
|
|
117
|
+
# Get or set vertical space between shapes.
|
|
118
|
+
attr_accessor :v_space
|
|
119
|
+
|
|
120
|
+
# Function performing the layout change.
|
|
121
|
+
# @param [Array<Shape>] shapes List of shapes which should be layout-ed
|
|
122
|
+
def do_layout(shapes)
|
|
123
|
+
start = get_top_left(shapes)
|
|
124
|
+
min_x = start.x
|
|
125
|
+
# find root items
|
|
126
|
+
shapes.each do |shape|
|
|
127
|
+
lst_connections = shape.get_assigned_connections(LineShape, Shape::CONNECTMODE::ENDING)
|
|
128
|
+
|
|
129
|
+
min_x, _ = process_node(shape, start.y, min_x, 0) if lst_connections.empty?
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
protected
|
|
134
|
+
|
|
135
|
+
# Process single shape.
|
|
136
|
+
# @param [Wx::Shape] node processed shape.
|
|
137
|
+
# @param [Float] y Vertical position of the shape.
|
|
138
|
+
# @param [Float] min_x
|
|
139
|
+
# @param [Integer] curr_max_width
|
|
140
|
+
# @return [Array(Float, Integer)]
|
|
141
|
+
def process_node(node, y, min_x, curr_max_width)
|
|
142
|
+
if node
|
|
143
|
+
node.move_to(min_x, y)
|
|
144
|
+
|
|
145
|
+
rct_bb = node.get_bounding_box
|
|
146
|
+
curr_max_width = rct_bb.width if rct_bb.width > curr_max_width
|
|
147
|
+
|
|
148
|
+
lst_neighbours = node.get_neighbours(Shape, Shape::CONNECTMODE::STARTING)
|
|
149
|
+
|
|
150
|
+
if lst_neighbours.empty?
|
|
151
|
+
min_x += curr_max_width + @h_space
|
|
152
|
+
else
|
|
153
|
+
lst_neighbours.each do |nbs|
|
|
154
|
+
unless nbs.get_parent_shape
|
|
155
|
+
min_x, curr_max_width = process_node(nbs, y + rct_bb.height + @v_space, min_x, curr_max_width)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
[min_x, curr_max_width]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Class encapsulating algorithm which layouts all top-most shapes into horizontal tree registered under "Horizontal Tree" name.
|
|
166
|
+
class LayoutHorizontalTree < LayoutAlgorithm
|
|
167
|
+
|
|
168
|
+
# Constructor.
|
|
169
|
+
def initialize
|
|
170
|
+
@h_space = @v_space = 30.0
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Get or set horizontal space between shapes.
|
|
174
|
+
attr_accessor :h_space
|
|
175
|
+
|
|
176
|
+
# Get or set vertical space between shapes.
|
|
177
|
+
attr_accessor :v_space
|
|
178
|
+
|
|
179
|
+
# Function performing the layout change.
|
|
180
|
+
# @param [Array<Shape>] shapes List of shapes which should be layout-ed
|
|
181
|
+
def do_layout(shapes)
|
|
182
|
+
start = get_top_left(shapes)
|
|
183
|
+
min_y = start.y
|
|
184
|
+
# find root items
|
|
185
|
+
shapes.each do |shape|
|
|
186
|
+
lst_connections = shape.get_assigned_connections(LineShape, Shape::CONNECTMODE::ENDING)
|
|
187
|
+
|
|
188
|
+
min_y, _ = process_node(shape, start.x, min_y, 0) if lst_connections.empty?
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
protected
|
|
193
|
+
|
|
194
|
+
# Process single shape.
|
|
195
|
+
# @param [Wx::Shape] node processed shape.
|
|
196
|
+
# @param [Float] x Vertical position of the shape.
|
|
197
|
+
# @param [Float] min_y
|
|
198
|
+
# @param [Integer] curr_max_height
|
|
199
|
+
# @return [Array(Float, Integer)]
|
|
200
|
+
def process_node(node, x, min_y, curr_max_height)
|
|
201
|
+
if node
|
|
202
|
+
node.move_to(x, min_y)
|
|
203
|
+
|
|
204
|
+
rct_bb = node.get_bounding_box
|
|
205
|
+
curr_max_height = rct_bb.height if rct_bb.height > curr_max_height
|
|
206
|
+
|
|
207
|
+
lst_neighbours = node.get_neighbours(Shape, Shape::CONNECTMODE::STARTING)
|
|
208
|
+
|
|
209
|
+
if lst_neighbours.empty?
|
|
210
|
+
min_y += curr_max_height + @v_space
|
|
211
|
+
else
|
|
212
|
+
lst_neighbours.each do |nbs|
|
|
213
|
+
unless nbs.get_parent_shape
|
|
214
|
+
min_y, curr_max_height = process_node(nbs, x + rct_bb.width + @h_space, min_y, curr_max_height)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
[min_y, curr_max_height]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Class encapsulating algorithm which layouts all top-most shapes into mesh registered under "Mesh" name.
|
|
225
|
+
# The algorithm doesn't optimize connection lines crossing.
|
|
226
|
+
class LayoutMesh < LayoutAlgorithm
|
|
227
|
+
|
|
228
|
+
# Constructor.
|
|
229
|
+
def initialize
|
|
230
|
+
@h_space = @v_space = 30.0
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Get or set horizontal space between shapes.
|
|
234
|
+
attr_accessor :h_space
|
|
235
|
+
|
|
236
|
+
# Get or set vertical space between shapes.
|
|
237
|
+
attr_accessor :v_space
|
|
238
|
+
|
|
239
|
+
# Function performing the layout change.
|
|
240
|
+
# @param [Array<Shape>] shapes List of shapes which should be layout-ed
|
|
241
|
+
def do_layout(shapes)
|
|
242
|
+
cols = Math.sqrt(shapes.size).floor
|
|
243
|
+
max_h = -@h_space
|
|
244
|
+
roffset = coffset = 0
|
|
245
|
+
|
|
246
|
+
start = get_top_left(shapes)
|
|
247
|
+
|
|
248
|
+
shapes.each_with_index do |shape, i|
|
|
249
|
+
if (i % cols) == 0
|
|
250
|
+
coffset = 0
|
|
251
|
+
roffset += max_h + @h_space
|
|
252
|
+
max_h = 0
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
shape.move_to(start.x + coffset, start.y + roffset)
|
|
256
|
+
|
|
257
|
+
rct_bb = shape.get_bounding_box
|
|
258
|
+
coffset += rct_bb.width + @v_space
|
|
259
|
+
|
|
260
|
+
max_h = rct_bb.height if rct_bb.height > max_h
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Module implements automatic diagram layout. The module allows to automatically layout shapes
|
|
267
|
+
# included in diagram manager/shape canvas/list of shapes by using several pre-defined layouting
|
|
268
|
+
# algorithms. The module should be used as it is.
|
|
269
|
+
module AutoLayout
|
|
270
|
+
|
|
271
|
+
class << self
|
|
272
|
+
|
|
273
|
+
def layout_algorithms_table
|
|
274
|
+
@layout_algorithms ||= {}
|
|
275
|
+
end
|
|
276
|
+
private :layout_algorithms_table
|
|
277
|
+
|
|
278
|
+
def register_layout_algorithm(name, klass)
|
|
279
|
+
layout_algorithms_table[name.to_s] = klass
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def get_layout_algorithm(name)
|
|
283
|
+
layout_algorithms_table.has_key?(name.to_s) ? layout_algorithms_table[name.to_s].new : nil
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def layout_algorithms
|
|
287
|
+
layout_algorithms_table.keys
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def each_layout_algorithm(&block)
|
|
291
|
+
if block
|
|
292
|
+
layout_algorithms_table.each_value { |klass| block.call(klass.new) }
|
|
293
|
+
else
|
|
294
|
+
::Enumerator.new { |y| layout_algorithms_table.each_value { |klass| y << klass.new } }
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# @overload layout(shapes, algname)
|
|
299
|
+
# Layout shapes included in given list.
|
|
300
|
+
# @param [Array<Shape>] shapes List of shapes
|
|
301
|
+
# @param [String] algname Algorithm name
|
|
302
|
+
# @return [Boolean] true if layout algorithm was found and executed, false otherwise
|
|
303
|
+
# @overload layout(diagram, algname)
|
|
304
|
+
# Layout shapes included in given diagram.
|
|
305
|
+
# @param [Diagram] diagram Reference to diagram
|
|
306
|
+
# @param [String] algname Algorithm name
|
|
307
|
+
# @return [Boolean] true if layout algorithm was found and executed, false otherwise
|
|
308
|
+
# @overload layout(canvas, algname)
|
|
309
|
+
# Layout shapes included in given shape canvas.
|
|
310
|
+
# @param [ShapeCanvas] canvas Reference to shape canvas
|
|
311
|
+
# @param [String] algname Algorithm name
|
|
312
|
+
# @return [Boolean] true if layout algorithm was found and executed, false otherwise
|
|
313
|
+
def layout(shapes, algname)
|
|
314
|
+
if shapes.is_a?(::Array)
|
|
315
|
+
alg = get_layout_algorithm(algname)
|
|
316
|
+
if alg
|
|
317
|
+
shapes.first.get_diagram.set_modified unless shapes.empty? || shapes.first.get_diagram.nil?
|
|
318
|
+
alg.do_layout(shapes)
|
|
319
|
+
return true
|
|
320
|
+
end
|
|
321
|
+
elsif shapes.is_a?(Diagram) || shapes.is_a?(ShapeCanvas)
|
|
322
|
+
alg = get_layout_algorithm(algname)
|
|
323
|
+
if alg
|
|
324
|
+
diagram = shapes.is_a?(Diagram) ? shapes : shapes.get_diagram
|
|
325
|
+
# layout all top level shapes excluding the line shapes
|
|
326
|
+
alg.do_layout(diagram.get_top_shapes.reject { |shp| shp.is_a?(LineShape) })
|
|
327
|
+
diagram.move_shapes_from_negatives
|
|
328
|
+
diagram.set_modified
|
|
329
|
+
update_canvas(diagram.get_shape_canvas) if diagram.get_shape_canvas
|
|
330
|
+
return true
|
|
331
|
+
end
|
|
332
|
+
else
|
|
333
|
+
::Kernel.raise ArgumentError, 'Expected array, diagram or canvas'
|
|
334
|
+
end
|
|
335
|
+
false
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
protected
|
|
339
|
+
|
|
340
|
+
# Update given shape canvas.
|
|
341
|
+
# @param [ShapeCanvas] canvas
|
|
342
|
+
def update_canvas(canvas)
|
|
343
|
+
canvas.center_shapes
|
|
344
|
+
canvas.update_virtual_size
|
|
345
|
+
canvas.update_multiedit_size
|
|
346
|
+
canvas.refresh(false)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
register_layout_algorithm('Circle', LayoutCircle)
|
|
352
|
+
register_layout_algorithm('Mesh', LayoutMesh)
|
|
353
|
+
register_layout_algorithm('Horizontal Tree', LayoutHorizontalTree)
|
|
354
|
+
register_layout_algorithm('Vertical Tree', LayoutVerticalTree)
|
|
355
|
+
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Wx::SF base definitions
|
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
|
3
|
+
|
|
4
|
+
module Wx::SF
|
|
5
|
+
|
|
6
|
+
class SFException < ::Exception; end
|
|
7
|
+
|
|
8
|
+
RECURSIVE = true
|
|
9
|
+
NORECURSIVE = false
|
|
10
|
+
DIRECT = true
|
|
11
|
+
INDIRECT = false
|
|
12
|
+
WITHCHILDREN = true
|
|
13
|
+
WITHOUTCHILDREN = false
|
|
14
|
+
ANY = nil
|
|
15
|
+
DELAYED = true
|
|
16
|
+
ACCEPT_ALL = :*
|
|
17
|
+
|
|
18
|
+
INCLUDE_PARENTS = true
|
|
19
|
+
WITHOUT_PARENTS = false
|
|
20
|
+
INITIALIZE = true
|
|
21
|
+
DONT_INITIALIZE = false
|
|
22
|
+
|
|
23
|
+
DEFAULT_ME_OFFSET = 5
|
|
24
|
+
SAVE_STATE = true
|
|
25
|
+
DONT_SAVE_STATE = false
|
|
26
|
+
FROM_PAINT = true
|
|
27
|
+
NOT_FROM_PAINT = false
|
|
28
|
+
PROMPT = true
|
|
29
|
+
NO_PROMPT = false
|
|
30
|
+
WITH_BACKGROUND = true
|
|
31
|
+
WITHOUT_BACKGROUND = false
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Wx::SF::CanvasHistory - canvas history class
|
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
|
3
|
+
|
|
4
|
+
module Wx::SF
|
|
5
|
+
|
|
6
|
+
class CanvasHistory
|
|
7
|
+
|
|
8
|
+
module DEFAULT
|
|
9
|
+
MAX_CANVAS_STATES = 25
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Constructor.
|
|
13
|
+
def initialize
|
|
14
|
+
@canvas_states = []
|
|
15
|
+
@current_state = nil
|
|
16
|
+
@current_state_index = nil
|
|
17
|
+
@max_states = DEFAULT::MAX_CANVAS_STATES
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_accessor :max_states
|
|
21
|
+
|
|
22
|
+
# Save current canvas state.
|
|
23
|
+
# @param [String] state serialized diagram state
|
|
24
|
+
def save_canvas_state(state)
|
|
25
|
+
# delete all states newer than the current state
|
|
26
|
+
if @current_state
|
|
27
|
+
@canvas_states.slice!(@current_state_index+1, @canvas_states.size)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# append new canvas state
|
|
31
|
+
@current_state_index = @canvas_states.size
|
|
32
|
+
@canvas_states << (@current_state = state)
|
|
33
|
+
|
|
34
|
+
# check the history bounds
|
|
35
|
+
if @canvas_states.size > @max_states
|
|
36
|
+
@canvas_states.shift
|
|
37
|
+
@current_state_index -= 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Perform the 'Undo' operation.
|
|
42
|
+
# @return [String] state to undo
|
|
43
|
+
def restore_older_state
|
|
44
|
+
return nil unless @current_state && @current_state_index>0
|
|
45
|
+
|
|
46
|
+
# move to previous canvas state and restore
|
|
47
|
+
@current_state_index -= 1
|
|
48
|
+
@current_state = @canvas_states[@current_state_index]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Perform the 'Redo' operation.
|
|
52
|
+
# @return [String] state to redo
|
|
53
|
+
def restore_newer_state
|
|
54
|
+
return nil unless @current_state && @current_state_index<(@canvas_states.size-1)
|
|
55
|
+
|
|
56
|
+
# move to next canvas state and restore
|
|
57
|
+
@current_state_index += 1
|
|
58
|
+
@current_state = @canvas_states[@current_state_index]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Clear all canvas history.
|
|
62
|
+
def clear
|
|
63
|
+
@canvas_states.clear
|
|
64
|
+
@current_state = nil
|
|
65
|
+
@current_state_index = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The function gives information whether the 'Undo' operation is available
|
|
69
|
+
# (exists any stored canvas state older than the current one.
|
|
70
|
+
# @return [Boolean] true if the 'undo' operation can be performed, otherwise false
|
|
71
|
+
def can_undo
|
|
72
|
+
@current_state && @current_state_index>0
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The function gives information whether the 'redo' operation is available
|
|
76
|
+
# (exists any stored canvas state newer than the current one.
|
|
77
|
+
# @return true if the 'redo' operation can be performed, otherwise false
|
|
78
|
+
def can_redo
|
|
79
|
+
@current_state && @current_state_index < (@canvas_states.size - 1)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Wx::SF::ConnectionPoint - shape connection point class
|
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
|
3
|
+
|
|
4
|
+
require 'wx/shapes/serializable'
|
|
5
|
+
|
|
6
|
+
module Wx::SF
|
|
7
|
+
|
|
8
|
+
class ConnectionPoint
|
|
9
|
+
|
|
10
|
+
include Wx::SF::Serializable
|
|
11
|
+
|
|
12
|
+
property :type
|
|
13
|
+
property :ortho_direction
|
|
14
|
+
property :relative_position
|
|
15
|
+
|
|
16
|
+
RADIUS = 3
|
|
17
|
+
|
|
18
|
+
# Connection point type
|
|
19
|
+
class CPTYPE < Wx::Enum
|
|
20
|
+
UNDEF = self.new(0)
|
|
21
|
+
TOPLEFT = self.new(1)
|
|
22
|
+
TOPMIDDLE = self.new(2)
|
|
23
|
+
TOPRIGHT = self.new(3)
|
|
24
|
+
CENTERLEFT = self.new(4)
|
|
25
|
+
CENTERMIDDLE = self.new(5)
|
|
26
|
+
CENTERRIGHT = self.new(6)
|
|
27
|
+
BOTTOMLEFT = self.new(7)
|
|
28
|
+
BOTTOMMIDDLE = self.new(8)
|
|
29
|
+
BOTTOMRIGHT = self.new(9)
|
|
30
|
+
CUSTOM = self.new(10)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Direction of orthogonal connection
|
|
34
|
+
class CPORTHODIR < Wx::Enum
|
|
35
|
+
UNDEF = self.new(1)
|
|
36
|
+
HORIZONTAL = self.new(2)
|
|
37
|
+
VERTICAL = self.new(3)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module DEFAULT
|
|
41
|
+
# Default value of Wx::SF::ConnectionPoint @rel_position data member
|
|
42
|
+
RELPOS = Wx::RealPoint.new(0, 0)
|
|
43
|
+
# Default value of Wx::SF::ConnectionPoint @ortho_dir data member
|
|
44
|
+
ORTHODIR = CPORTHODIR::UNDEF
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Constructor
|
|
48
|
+
# @overload initialize()
|
|
49
|
+
# default ctor for deserialize
|
|
50
|
+
# @overload initialize(parent, pos, id=nil)
|
|
51
|
+
# @param [Wx::SF::Shape] parent parent shape
|
|
52
|
+
# @param [Wx::RealPoint,Array(Float, Float)] pos relative position in percentages
|
|
53
|
+
# @param [Integer] id point id
|
|
54
|
+
# @overload initialize(parent, type)
|
|
55
|
+
# @param [Wx::SF::Shape] parent parent shape
|
|
56
|
+
# @param [Wx::SF::ConnectionPoint::CPTYPE] type connection point type
|
|
57
|
+
def initialize(*args)
|
|
58
|
+
@parent_shape, type_or_pos, id = *args
|
|
59
|
+
if type_or_pos # allow parent to be nil (mainly for testing purposes)
|
|
60
|
+
if CPTYPE === type_or_pos
|
|
61
|
+
@type = type_or_pos
|
|
62
|
+
@rel_position = DEFAULT::RELPOS
|
|
63
|
+
@id = nil
|
|
64
|
+
elsif Wx::RealPoint === type_or_pos || Array === type_or_pos
|
|
65
|
+
@type = CPTYPE::CUSTOM
|
|
66
|
+
@rel_position = Wx::RealPoint === type_or_pos ? type_or_pos : Wx::RealPoint.new(*type_or_pos)
|
|
67
|
+
@id = id
|
|
68
|
+
else
|
|
69
|
+
::Kernel.raise ArgumentError, 'Invalid arguments'
|
|
70
|
+
end
|
|
71
|
+
@ortho_dir = DEFAULT::ORTHODIR
|
|
72
|
+
@mouse_over = false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get connection point type.
|
|
77
|
+
# @return [CPTYPE] Connection point type
|
|
78
|
+
def get_type
|
|
79
|
+
@type
|
|
80
|
+
end
|
|
81
|
+
alias :type :get_type
|
|
82
|
+
|
|
83
|
+
# Set direction of orthogonal line's connection.
|
|
84
|
+
# @param [CPORTHODIR] dir Required direction
|
|
85
|
+
# @see CPORTHODIR
|
|
86
|
+
def set_ortho_direction(dir)
|
|
87
|
+
@ortho_dir = dir
|
|
88
|
+
end
|
|
89
|
+
alias :ortho_direction= :set_ortho_direction
|
|
90
|
+
|
|
91
|
+
# Get direction of orthogonal line's connection.
|
|
92
|
+
# @return [CPORTHODIR] Current direction
|
|
93
|
+
# @see CPORTHODIR
|
|
94
|
+
def get_ortho_direction
|
|
95
|
+
@ortho_dir
|
|
96
|
+
end
|
|
97
|
+
alias :ortho_direction :get_ortho_direction
|
|
98
|
+
|
|
99
|
+
# Set parent shape.
|
|
100
|
+
# @param [Wx::SF::Shape] parent parent shape
|
|
101
|
+
def set_parent_shape(parent)
|
|
102
|
+
@parent_shape = parent
|
|
103
|
+
end
|
|
104
|
+
alias :parent_shape= :set_parent_shape
|
|
105
|
+
|
|
106
|
+
# Get parent shape.
|
|
107
|
+
# @return [Wx::SF::Shape] parent shape
|
|
108
|
+
def get_parent_shape
|
|
109
|
+
@parent_shape
|
|
110
|
+
end
|
|
111
|
+
alias :parent_shape :get_parent_shape
|
|
112
|
+
|
|
113
|
+
# Set relative position of custom connection point.
|
|
114
|
+
# @param [Wx::RealPoint,Array(Float, Float)] pos Relative position in percentages
|
|
115
|
+
def set_relative_position(pos)
|
|
116
|
+
@rel_position = Wx::RealPoint === pos ? pos : Wx::RealPoint.new(*pos)
|
|
117
|
+
end
|
|
118
|
+
alias :relative_position= :set_relative_position
|
|
119
|
+
|
|
120
|
+
# Get relative position of custom connection point.
|
|
121
|
+
# @return [Wx::RealPoint] Relative position in percentages
|
|
122
|
+
def get_relative_position
|
|
123
|
+
@rel_position
|
|
124
|
+
end
|
|
125
|
+
alias :relative_position :get_relative_position
|
|
126
|
+
|
|
127
|
+
# Get absolute position of the connection point.
|
|
128
|
+
# @return [Wx::RealPoint] Absolute position of the connection point
|
|
129
|
+
def get_connection_point
|
|
130
|
+
if @parent_shape
|
|
131
|
+
rctParent = @parent_shape.get_bounding_box
|
|
132
|
+
|
|
133
|
+
case @type
|
|
134
|
+
when CPTYPE::TOPLEFT
|
|
135
|
+
return rctParent.top_left.to_real
|
|
136
|
+
|
|
137
|
+
when CPTYPE::TOPMIDDLE
|
|
138
|
+
return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, rctParent.top.to_f)
|
|
139
|
+
|
|
140
|
+
when CPTYPE::TOPRIGHT
|
|
141
|
+
return rctParent.top_right.to_real
|
|
142
|
+
|
|
143
|
+
when CPTYPE::CENTERLEFT
|
|
144
|
+
return Wx::RealPoint.new(rctParent.left.to_f, (rctParent.top + rctParent.height/2).to_f)
|
|
145
|
+
|
|
146
|
+
when CPTYPE::CENTERMIDDLE
|
|
147
|
+
return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, (rctParent.top + rctParent.height/2).to_f)
|
|
148
|
+
|
|
149
|
+
when CPTYPE::CENTERRIGHT
|
|
150
|
+
return Wx::RealPoint.new(rctParent.right.to_f, (rctParent.top + rctParent.height/2).to_f)
|
|
151
|
+
|
|
152
|
+
when CPTYPE::BOTTOMLEFT
|
|
153
|
+
return rctParent.bottom_left.to_real
|
|
154
|
+
|
|
155
|
+
when CPTYPE::BOTTOMMIDDLE
|
|
156
|
+
return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, rctParent.bottom.to_f)
|
|
157
|
+
|
|
158
|
+
when CPTYPE::BOTTOMRIGHT
|
|
159
|
+
return rctParent.bottom_right.to_real
|
|
160
|
+
|
|
161
|
+
when CPTYPE::CUSTOM
|
|
162
|
+
return Wx::RealPoint.new(rctParent.left + rctParent.width * @rel_position.x/100, rctParent.top + rctParent.height * @rel_position.y/100)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
Wx::RealPoint.new
|
|
166
|
+
end
|
|
167
|
+
alias :connection_point :get_connection_point
|
|
168
|
+
|
|
169
|
+
# Find out whether given point is inside the connection point.
|
|
170
|
+
# @param [Wx::Point] pos Examined point
|
|
171
|
+
# @return [Boolean] true if the point is inside the handle, otherwise false
|
|
172
|
+
def contains(pos)
|
|
173
|
+
# HINT: overload it for custom actions...
|
|
174
|
+
connection_point.distance_to(pos.to_real_point) < (3 * RADIUS)
|
|
175
|
+
end
|
|
176
|
+
alias :contains? :contains
|
|
177
|
+
|
|
178
|
+
# Draw connection point.
|
|
179
|
+
# @param [Wx::DC] dc Device context where the handle will be drawn
|
|
180
|
+
def draw(dc)
|
|
181
|
+
if @mouse_over
|
|
182
|
+
draw_hover(dc)
|
|
183
|
+
else
|
|
184
|
+
draw_normal(dc)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Refresh (repaint) the dock point
|
|
189
|
+
def refresh
|
|
190
|
+
@parent_shape.refresh(DELAYED)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
protected
|
|
194
|
+
|
|
195
|
+
# Draw the connection point in the normal way. The function can be overridden if necessary.
|
|
196
|
+
# @param [Wx::DC] dc Reference to device context where the shape will be drawn to
|
|
197
|
+
def draw_normal(dc)
|
|
198
|
+
# HINT: overload it for custom actions...
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Draw the connection point in the hover mode (the mouse cursor is above the shape).
|
|
202
|
+
# The function can be overridden if necessary.
|
|
203
|
+
# @param [Wx::DC] dc Reference to device context where the shape will be drawn to
|
|
204
|
+
def draw_hover(dc)
|
|
205
|
+
# HINT: overload it for custom actions...
|
|
206
|
+
dc.with_pen(Wx::BLACK_PEN) do
|
|
207
|
+
dc.with_brush(Wx::RED_BRUSH) do
|
|
208
|
+
dc.draw_circle(connection_point.to_point, RADIUS)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
private
|
|
214
|
+
|
|
215
|
+
# For deserialization
|
|
216
|
+
def set_type(type)
|
|
217
|
+
@type = type
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Event handler called when the mouse pointer is moving above shape canvas.
|
|
221
|
+
# @param [Wx::Point] pos Current mouse position
|
|
222
|
+
def _on_mouse_move(pos)
|
|
223
|
+
if contains?(pos)
|
|
224
|
+
unless @mouse_over
|
|
225
|
+
@mouse_over = true
|
|
226
|
+
refresh
|
|
227
|
+
end
|
|
228
|
+
else
|
|
229
|
+
if @mouse_over
|
|
230
|
+
@mouse_over = false
|
|
231
|
+
refresh
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
end
|