|
| 1 | +import remi.gui as gui |
| 2 | +from remi import start, App |
| 3 | +import math |
| 4 | + |
| 5 | +class SvgPolygon(gui.SvgPolyline): |
| 6 | + def __init__(self, _maxlen=None, *args, **kwargs): |
| 7 | + super(SvgPolygon, self).__init__(_maxlen, *args, **kwargs) |
| 8 | + self.type = 'polygon' |
| 9 | + |
| 10 | + def set_stroke(self, width=1, color='black'): |
| 11 | + """Sets the stroke properties. |
| 12 | +
|
| 13 | + Args: |
| 14 | + width (int): stroke width |
| 15 | + color (str): stroke color |
| 16 | + """ |
| 17 | + self.attributes['stroke'] = color |
| 18 | + self.attributes['stroke-width'] = str(width) |
| 19 | + |
| 20 | + def set_fill(self, color='black'): |
| 21 | + """Sets the fill color. |
| 22 | +
|
| 23 | + Args: |
| 24 | + color (str): stroke color |
| 25 | + """ |
| 26 | + self.style['fill'] = color |
| 27 | + self.attributes['fill'] = color |
| 28 | + |
| 29 | + def add_arrow_coord(self, line, arrow_height, arrow_width, recess): |
| 30 | + """ Determine the coordinates of an arrow head polygon |
| 31 | + with height (h) and width (w) and recess (r) |
| 32 | + pointing from the one but last to the last point of (poly)line (line). |
| 33 | + Note that the coordinates of an SvgLine and an SvgPolyline |
| 34 | + are stored in different variables. |
| 35 | + """ |
| 36 | + # arrow = SvgPolygon(_maxlen=4) |
| 37 | + if line.type == 'polyline': |
| 38 | + xe = line.coordsX[-1] |
| 39 | + ye = line.coordsY[-1] |
| 40 | + xp = line.coordsX[-2] |
| 41 | + yp = line.coordsY[-2] |
| 42 | + else: |
| 43 | + xe = line.attributes['x2'] |
| 44 | + ye = line.attributes['y2'] |
| 45 | + xp = line.attributes['x1'] |
| 46 | + yp = line.attributes['y1'] |
| 47 | + h = arrow_height |
| 48 | + if arrow_width == 0: |
| 49 | + w = arrow_height / 3 |
| 50 | + else: |
| 51 | + w = arrow_width |
| 52 | + r = recess |
| 53 | + self.add_coord(xe, ye) |
| 54 | + dx = xe - xp |
| 55 | + dy = ye - yp |
| 56 | + de = math.sqrt(dx**2 + dy**2) |
| 57 | + xh = xe - h * dx / de |
| 58 | + yh = ye - h * dy / de |
| 59 | + x1 = xh + w * dy / de |
| 60 | + y1 = yh - w * dx / de |
| 61 | + self.add_coord(x1, y1) |
| 62 | + x2 = xe - (h - r) * dx / de |
| 63 | + y2 = ye - (h - r) * dy / de |
| 64 | + self.add_coord(x2, y2) |
| 65 | + x3 = xh - w * dy / de |
| 66 | + y3 = yh + w * dx / de |
| 67 | + self.add_coord(x3, y3) |
| 68 | + |
| 69 | + |
| 70 | +class MyApp(App): |
| 71 | + """ Example drawing by Andries van Renssen |
| 72 | + including connected rectangular boxes, polylines and rhombusses. |
| 73 | + """ |
| 74 | + def __init__(self, *args): |
| 75 | + super(MyApp, self).__init__(*args) |
| 76 | + |
| 77 | + def main(self): |
| 78 | + self.frame = gui.VBox(width='100%', height='80%', |
| 79 | + style={'overflow': 'auto', |
| 80 | + 'background-color': '#eeffdd'}) |
| 81 | + self.sheet = gui.Svg(width='100%', height='100%') |
| 82 | + self.screen_width = 1000 |
| 83 | + self.screen_height = 600 |
| 84 | + self.int_id = 0 |
| 85 | + self.sheet.set_viewbox(0, 0, self.screen_width, self.screen_height) |
| 86 | + self.frame.append(self.sheet) |
| 87 | + nr_of_boxes = 2 |
| 88 | + box_names = ['Activity-A', 'Activity-B'] |
| 89 | + self.Draw_a_drawing_of_one_sheet(nr_of_boxes, box_names) |
| 90 | + return self.frame |
| 91 | + |
| 92 | + def Draw_a_drawing_of_one_sheet(self, nr_of_boxes, box_names): |
| 93 | + """ Draw a drawing with two boxes, each with a name inside |
| 94 | + and a polyline between the midpoints of the sides of the boxes, |
| 95 | + with half-way the polyline a rhombus with an id included. |
| 96 | + """ |
| 97 | + thickness = 2 # Line thickness |
| 98 | + center_x = [] # x of the center of box[i] on canvas |
| 99 | + center_y = [] # y of the center of box[i] on canvas |
| 100 | + mid_points = [] |
| 101 | + box_width = 100 # pixels |
| 102 | + box_height = 100 # pixels |
| 103 | + delta_x = self.screen_width / (nr_of_boxes + 1) |
| 104 | + delta_y = self.screen_height / (nr_of_boxes + 1) |
| 105 | + # Draw the boxes |
| 106 | + for box_nr in range(0, nr_of_boxes): |
| 107 | + center_x.append(delta_x + box_nr * delta_x) |
| 108 | + center_y.append(delta_y + box_nr * delta_y) |
| 109 | + name = box_names[box_nr] |
| 110 | + ident = str(box_nr + 1) |
| 111 | + # Draw one box at the specified location |
| 112 | + mid_points.append(self.box_type_1( |
| 113 | + center_x[box_nr], center_y[box_nr], |
| 114 | + name, ident, box_width,box_height)) |
| 115 | + |
| 116 | + # Draw a line with arrow head to the first box |
| 117 | + x2 = mid_points[0][3][0] |
| 118 | + y2 = mid_points[0][3][1] |
| 119 | + x1 = x2 - 150 |
| 120 | + y1 = y2 |
| 121 | + line_0 = gui.SvgLine(x1, y1, x2, y2) |
| 122 | + line_0.set_stroke(width=thickness, color='black') |
| 123 | + self.sheet.append(line_0) |
| 124 | + # Add an arrow head to line_0 |
| 125 | + head_0 = SvgPolygon(4) |
| 126 | + arrow_height = 20 |
| 127 | + arrow_width = arrow_height / 3 |
| 128 | + recess = arrow_height / 5 |
| 129 | + head_0.add_arrow_coord(line_0, arrow_height, arrow_width, recess) |
| 130 | + head_0.set_stroke(width=thickness, color='black') |
| 131 | + head_0.set_fill(color='blue') |
| 132 | + self.sheet.append(head_0) |
| 133 | + |
| 134 | + # Draw a rhombus polygon |
| 135 | + x = (center_x[0] + center_x[1]) / 2 |
| 136 | + y = (center_y[0] + center_y[1]) / 2 |
| 137 | + self.int_id += 1 |
| 138 | + str_id = str(self.int_id) |
| 139 | + hor_size = 15 # pixels |
| 140 | + vert_size = 25 # pixels |
| 141 | + rhombus = self.rhombus_polygon(x, y, str_id, hor_size, vert_size) |
| 142 | + |
| 143 | + # Determine points of the first polyline |
| 144 | + line_1_points = [] |
| 145 | + line_1_points.append(mid_points[0][2]) |
| 146 | + corner = [rhombus[0][0], mid_points[0][2][1]] |
| 147 | + line_1_points.append(corner) |
| 148 | + line_1_points.append(rhombus[0]) |
| 149 | + # Draw a polyline from box_1 to rhombus |
| 150 | + line1 = gui.SvgPolyline(_maxlen=4) |
| 151 | + for pt in line_1_points: |
| 152 | + line1.add_coord(*pt) |
| 153 | + line1.set_stroke(width=thickness, color='black') |
| 154 | + self.sheet.append(line1) |
| 155 | + |
| 156 | + # Determine points of the second polyline |
| 157 | + line_2_points = [] |
| 158 | + line_2_points.append(rhombus[1]) |
| 159 | + corner = [rhombus[1][0], mid_points[1][3][1]] |
| 160 | + line_2_points.append(corner) |
| 161 | + line_2_points.append(mid_points[1][3]) |
| 162 | + # Drawa polyline from rhombus to box_2 |
| 163 | + line2 = gui.SvgPolyline(_maxlen=4) |
| 164 | + for pt in line_2_points: |
| 165 | + line2.add_coord(pt[0], pt[1]) |
| 166 | + line2.set_stroke(width=thickness, color='black') |
| 167 | + self.sheet.append(line2) |
| 168 | + |
| 169 | + # Add an arrow head to line2 |
| 170 | + head = SvgPolygon(4) |
| 171 | + head.add_arrow_coord(line2, arrow_height, arrow_width, recess) |
| 172 | + head.set_stroke(width=thickness, color='black') |
| 173 | + head.set_fill(color='blue') |
| 174 | + self.sheet.append(head) |
| 175 | + |
| 176 | + def box_type_1(self, X, Y, name, ident, box_width, box_height): |
| 177 | + """ Draw a rectangular box of box_width and box_height |
| 178 | + with name and ident, |
| 179 | + on sheet with (X,Y) as its center on the canvas |
| 180 | + Return midpts = N(x,y), S(x,y), E(x,y), W(x,y). |
| 181 | + """ |
| 182 | + boxW2 = box_width / 2 |
| 183 | + boxH2 = box_height / 2 |
| 184 | + x0, y0 = X - boxW2, Y - boxH2 # Top_left of box |
| 185 | + x1, y1 = X + boxW2, Y + boxH2 # Bottom_right of box |
| 186 | + width = x1 - x0 |
| 187 | + height = y1 - y0 |
| 188 | + |
| 189 | + box = gui.SvgRectangle(x0, y0, width, height) |
| 190 | + box.set_stroke(width=2, color='black') |
| 191 | + box.set_fill(color='yellow') |
| 192 | + box_name = gui.SvgText(X, Y, name) |
| 193 | + box_name.attributes['text-anchor'] = 'middle' |
| 194 | + box_id = gui.SvgText(X, Y + 15, str(ident)) |
| 195 | + box_id.attributes['text-anchor'] = 'middle' |
| 196 | + self.sheet.append([box, box_name, box_id]) |
| 197 | + |
| 198 | + mid_north = [X, Y - boxH2] |
| 199 | + mid_south = [X, Y + boxH2] |
| 200 | + mid_east = [X + boxW2, Y] |
| 201 | + mid_west = [X - boxW2, Y] |
| 202 | + |
| 203 | + return mid_north, mid_south, mid_east, mid_west |
| 204 | + |
| 205 | + def rhombus_polygon(self, X, Y, str_id, hor_size, vert_size): |
| 206 | + """ Draw a rhombus polygon. |
| 207 | + Horizontal size (-hor_size, +hor_size) and |
| 208 | + vertical size (-vert_size, +vert_size). |
| 209 | + with its center on position X,Y |
| 210 | + and with its str_id as text in the middle. |
| 211 | + """ |
| 212 | + x0, y0 = X - hor_size, Y # mid_west |
| 213 | + x1, y1 = X, Y - vert_size # mid_north |
| 214 | + x2, y2 = X + hor_size, Y # mid_east |
| 215 | + x3, y3 = X, Y + vert_size # mid_south |
| 216 | + |
| 217 | + polygon = SvgPolygon(4) |
| 218 | + polygon.set_stroke(width=2, color='black') |
| 219 | + poly_name = gui.SvgText(X, Y + 5, str_id) |
| 220 | + poly_name.attributes['text-anchor'] = 'middle' |
| 221 | + self.sheet.append([polygon, poly_name]) |
| 222 | + |
| 223 | + mid_north = [x1, y1] |
| 224 | + mid_south = [x3, y3] |
| 225 | + mid_east = [x2, y2] |
| 226 | + mid_west = [x0, y0] |
| 227 | + |
| 228 | + polygon.add_coord(*mid_north) |
| 229 | + polygon.add_coord(*mid_east) |
| 230 | + polygon.add_coord(*mid_south) |
| 231 | + polygon.add_coord(*mid_west) |
| 232 | + |
| 233 | + return mid_north, mid_south, mid_east, mid_west |
| 234 | + |
| 235 | +if __name__ == "__main__": |
| 236 | + # starts the webserver |
| 237 | + # optional parameters |
| 238 | + start(MyApp,address='127.0.0.1', port=8081, multiple_instance=False, |
| 239 | + enable_file_cache=True, update_interval=0.1, start_browser=True) |
| 240 | + # start(MyApp, debug=True, address='0.0.0.0', port=0 ) |
0 commit comments