-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfloorplanarea-6.py
160 lines (142 loc) · 7.13 KB
/
floorplanarea-6.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# coding: utf-8
#!python2
# coding: utf-8
import ui
import photos
class RoomAreaView(ui.View):
''' top level container, consisting of an imageview and an overlay.
ideally this might also contain a menu bar, for changing the mode - for instance you might have a button for enabling room drawing, another one for editing points, another for zoom/pan'''
def __init__(self, file):
# create image view that fills this top level view.
# since this RoomAreaView is the one that gets presented, it will be
# resized automatically, so we want the imageview to stay tied to the
# full view size, so we use flex. Also, the content mode is important
# to prevent the image from being squished
iv = ui.ImageView(frame=self.bounds)
iv.flex = 'wh'
iv.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
self.add_subview(iv)
# right now, read in file from constructor. a future improvement would
# be to have a file menu to select an image, which sets the iv.image
iv.image = ui.Image.named(file)
iv.touch_enabled = False
# add the overlay. this is the touch sensitive 'drawing' layer, and
# store a refernce to it. this is set to the same size as the
# imageview
rv = RoomAreaOverlay(frame=self.bounds, flex='wh')
self.add_subview(rv)
self.rv = rv
def will_close(self):
'''upon close, dump out the current area. do this by first getting the set of points. The zip notation lets us convert from a tuple of the form ((x0,y0),(x1,y1),...) to x=(x0,x1,...) and y=(y0,y1,...)'''
x, y = zip(*self.rv.pts)
print polygonArea(x, y, float(self.rv.scale.value)), self.rv.scale.value / 10
class RoomAreaOverlay(ui.View):
'''A touch sensitive overlay. Touches add to a list of points, which are then used to compute area. Lengths are shown for each line segment, and a scaling parameter is used to adjust the length of drawn lines to known dimensions'''
def __init__(self, *args, **kwargs):
# call View.__init__ so we can handle any standard view constructor
# arguments, such as frame, bg_color, etc
ui.View.__init__(self, *args, **kwargs)
self.touch_enabled = True
# store list of points, and also current point for use in dragging
self.pts = []
self.curridx = -1
# create a textfield to set scale. could be a slider instead
# scale is in units of feet/pixel, or really measurementunits/pixel
# it might be easier to have user enter this as 1/scale (pixel/measunit), so that it will be a number >1.
# If using a Slider instead, use self.scale.value instead of
# float(self.scale.text) elsewhere
self.scale = ui.Slider(frame=(400, 0, 500, 30))
self.scale.action = self.set_scale
self.add_subview(self.scale)
# self.scale.text=self.scale.value
# create a label showing current computer room area
self.lbl = ui.Label(frame=(200, 0, 130, 30))
self.lbl.text = 'Area'
self.add_subview(self.lbl)
self.lbl.bg_color = 'white'
def set_scale(self, sender):
'''action for self.scale. called whenever scale is changed. when that happens we update the computation of room area, and also call set_needs_display, which forces a redraw, thus redrawing distance units'''
self.update_area()
self.set_needs_display()
def update_area(self):
'''update the area label by computing the polygon area with current scale value'''
x, y = zip(*self.pts)
area = polygonArea(x, y, self.scale.value / 10)
self.lbl.text = 'Area: {} squnits'.format(area)
def touch_began(self, touch):
'''when starting a touch, fiest check if there are any points very near by. if so, set currpt to that to allow dragging previous points,
if not, add a new point
'''
self.curridx = -1
currpt = (touch.location.x, touch.location.y)
# search existing points for a near match
for i, p in enumerate(self.pts):
if abs(ui.Point(*p) - ui.Point(*currpt)) < 20:
self.curridx = i
# if not match found, add a new point
if self.curridx == -1:
self.pts.append(currpt)
self.set_needs_display()
def touch_moved(self, touch):
''' update the current point, and redraw'''
self.pts[self.curridx] = (touch.location.x, touch.location.y)
self.set_needs_display()
def touch_ended(self, touch):
''' called when lifting finger. append the final point to the permanent list of pts, then clear the current point, and redraw'''
self.curridx = -1
self.set_needs_display()
self.update_area()
def draw(self):
''' called by iOS whenever set_needs_display is called, or whenever os decides it is needed, for instance view rotates'''
# set color to red
ui.set_color((1, 0, 0))
# create a copy of self.pts, and append the current point
drawpts = self.pts
if drawpts:
# move path to first point:
pth = ui.Path()
pth.move_to(*drawpts[0])
p0 = drawpts[0]
for p in drawpts[1:]:
# for each point after the first, draw a line from the previous point, compute length vector L
# draw the line segment
pth.line_to(*p)
# compute point which is halfway along line between the
# start/end point. L is the vector pointing from start to
# finish. H is the Point at the halfway, referenced back to
# global origin
L = (ui.Point(*p) - ui.Point(*p0))
P0 = ui.Point(*p0)
H = P0 + L / 2 # halfway point
# put text at the halfway point, containing length ofmsegment *
# scale
ui.draw_string('%3.2f' % abs(
L * self.scale.value / 10), (H.x, H.y, 0, 0), color='red')
# set starting point for next line segment
p0 = p
pth.stroke() # draw the path
if len(drawpts) > 2: # 'close' the path to show area computed
with ui.GState():
pth = ui.Path()
pth.move_to(*drawpts[-1])
pth.line_to(*drawpts[0])
pth.set_line_dash([2, 3])
ui.set_color((1, .5, .5, .5))
pth.stroke()
# create a 10 pixel circle at last entered point
ui.Path.oval(drawpts[-1][0] - 5, drawpts[-1][1] - 5, 10, 10).fill()
# show circles for previously entered points. smaller and lighter
ui.set_color((1, .5, .5))
for p in drawpts[0:-1]:
ui.Path.oval(p[0] - 3, p[1] - 3, 6, 6).fill()
def polygonArea(X, Y, scale):
'''compute scaled area of polygon,assuming it is closed '''
area = 0
j = len(X) - 1
for i in range(len(X)):
area = area + (X[j] + X[i]) * (Y[j] - Y[i]) * scale**2
j = i
return abs(area / 2)
photos.pick_image().save("temp.jpg")
v = RoomAreaView('temp.jpg')
v.present('fullscreen')