1#import <QuartzCore/QuartzCore.h>
2#import <Social/Social.h>
3
4#import "ModelViewController.h"
5#import "Utils.h"
6#import "drawContext.h"
7#import "iosUtils.h"
8
9#import "AppDelegate.h"
10
11@interface ModelViewController ()
12- (void)configureView;
13@end
14
15@implementation ModelViewController
16
17#pragma mark - Managing the detail item
18@synthesize glView;
19
20- (void)setDetailItem:(id)newDetailItem
21{
22  if(_detailItem != newDetailItem) {
23    _detailItem = newDetailItem;
24    // Update the view.
25    [self configureView];
26  }
27}
28
29- (void)configureView
30{
31  // Update the user interface for the detail item.
32  if(self.detailItem) {
33    self.detailDescriptionLabel.text = [self.detailItem description];
34  }
35}
36
37- (void)viewDidAppear:(BOOL)animated
38{
39  _progressLabel.frame = CGRectMake(50, self.view.frame.size.height - 25,
40                                    _progressLabel.frame.size.width,
41                                    _progressLabel.frame.size.height);
42  _progressIndicator.frame = CGRectMake(20, self.view.frame.size.height - 25,
43                                        _progressIndicator.frame.size.width,
44                                        _progressIndicator.frame.size.height);
45  [_progressIndicator
46    addGestureRecognizer:[[UITapGestureRecognizer alloc]
47                           initWithTarget:self
48                                   action:@selector
49                                   (handleProgressIndicatorTap:)]];
50  [_progressLabel setHidden:YES];
51  [_progressIndicator setHidden:YES];
52  [self.navigationController setToolbarHidden:YES animated:NO];
53  if(self.initialModel != nil) {
54    [self.glView load:self.initialModel];
55    [[NSNotificationCenter defaultCenter]
56      postNotificationName:@"refreshParameters"
57                    object:nil];
58    //[self.initialModel release];
59    self.initialModel = nil;
60  }
61}
62
63- (void)viewDidLoad
64{
65  [super viewDidLoad];
66  // Do any additional setup after loading the view, typically from a nib.
67  [self configureView];
68  [_singleTap requireGestureRecognizerToFail:_doubleTap];
69  scaleFactor = 1.;
70  setObjCBridge((__bridge void *)self);
71  [[NSNotificationCenter defaultCenter] addObserver:self
72                                           selector:@selector(requestRender)
73                                               name:@"requestRender"
74                                             object:nil];
75
76  _runStopButton =
77    [[UIBarButtonItem alloc] initWithTitle:@"Run"
78                                     style:UIBarButtonItemStylePlain
79                                    target:self
80                                    action:@selector(compute)];
81  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
82     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]) {
83    UIBarButtonItem *model =
84      [[UIBarButtonItem alloc] initWithTitle:@"Model list"
85                                       style:UIBarButtonItemStylePlain
86                                      target:self
87                                      action:@selector(showModelsList)];
88    [self.navigationItem
89      setRightBarButtonItems:[NSArray
90                               arrayWithObjects:_runStopButton, model, nil]];
91  }
92  else {
93    UIBarButtonItem *settings =
94      [[UIBarButtonItem alloc] initWithTitle:@"Parameters"
95                                       style:UIBarButtonItemStylePlain
96                                      target:self
97                                      action:@selector(showSettings)];
98    [self.navigationItem
99      setRightBarButtonItems:[NSArray
100                               arrayWithObjects:_runStopButton, settings, nil]];
101  }
102
103  UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]
104    initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
105                         target:nil
106                         action:nil];
107  UIBarButtonItem *prevButton = [[UIBarButtonItem alloc]
108    initWithBarButtonSystemItem:UIBarButtonSystemItemRewind
109                         target:self
110                         action:@selector(prevAnimation)];
111  _stopButton = [[UIBarButtonItem alloc]
112    initWithBarButtonSystemItem:UIBarButtonSystemItemPause
113                         target:self
114                         action:@selector(stopAnimation:)];
115  [_stopButton setEnabled:NO];
116  _playButton = [[UIBarButtonItem alloc]
117    initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
118                         target:self
119                         action:@selector(playAnimation:)];
120  UIBarButtonItem *nextButton = [[UIBarButtonItem alloc]
121    initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward
122                         target:self
123                         action:@selector(nextAnimation)];
124  self.toolbarItems = [[NSArray alloc]
125    initWithObjects:flexibleSpace, prevButton, _stopButton, _playButton,
126                    nextButton, flexibleSpace, nil];
127}
128
129- (void)viewWillTransitionToSize:(CGSize)size
130       withTransitionCoordinator:
131         (id<UIViewControllerTransitionCoordinator>)coordinator
132{
133  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
134  _progressLabel.frame = CGRectMake(50, self.view.frame.size.height - 25,
135                                    _progressLabel.frame.size.width,
136                                    _progressLabel.frame.size.height);
137  _progressIndicator.frame = CGRectMake(20, self.view.frame.size.height - 25,
138                                        _progressIndicator.frame.size.width,
139                                        _progressIndicator.frame.size.height);
140}
141
142- (void)compute
143{
144  AppDelegate *appDelegate =
145    (AppDelegate *)[UIApplication sharedApplication].delegate;
146  appDelegate->compute = YES;
147  [_runStopButton setAction:@selector(stop)];
148  [_runStopButton setTitle:@"Stop"];
149  [_progressLabel setText:@""];
150  [_progressLabel setHidden:NO];
151  [_progressIndicator setHidden:NO];
152  [_progressIndicator startAnimating];
153  self.navigationItem.hidesBackButton = YES;
154
155  [[UIApplication sharedApplication] cancelAllLocalNotifications];
156  dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
157    onelab_cb("compute");
158    dispatch_async(dispatch_get_main_queue(), ^{
159      AppDelegate *appDelegate =
160        (AppDelegate *)[UIApplication sharedApplication].delegate;
161      appDelegate->compute = NO;
162      [[NSNotificationCenter defaultCenter]
163        postNotificationName:@"refreshParameters"
164                      object:nil];
165      [self->_runStopButton setAction:@selector(compute)];
166      [self->_runStopButton setTitle:@"Run"];
167      [self->_progressLabel setHidden:YES];
168      [self->_progressIndicator stopAnimating];
169      [self->_progressIndicator setHidden:YES];
170      self.navigationItem.hidesBackButton = NO;
171    });
172  });
173}
174
175- (void)stop
176{
177  onelab_cb("stop");
178}
179
180- (void)playAnimation:(UIBarButtonItem *)sender
181{
182  [_playButton setEnabled:NO];
183  [_stopButton setEnabled:YES];
184  _animation = [NSTimer scheduledTimerWithTimeInterval:0.5
185                                                target:self
186                                              selector:@selector(nextAnimation)
187                                              userInfo:nil
188                                               repeats:YES];
189}
190
191- (void)stopAnimation:(UIBarButtonItem *)sender
192{
193  [_animation invalidate];
194  _animation = nil;
195  [_playButton setEnabled:YES];
196  [_stopButton setEnabled:NO];
197}
198
199- (void)nextAnimation
200{
201  animation_next();
202  [self requestRender];
203}
204
205- (void)prevAnimation
206{
207  animation_prev();
208  [self requestRender];
209}
210
211- (IBAction)pinch:(UIPinchGestureRecognizer *)sender
212{
213  if([sender numberOfTouches] <= 2) {
214    float mScale = scaleFactor;
215    if(sender.state == UIGestureRecognizerStateBegan)
216      mScale = scaleFactor;
217    else if(sender.state == UIGestureRecognizerStateChanged)
218      mScale = scaleFactor * [sender scale];
219    else if(sender.state == UIGestureRecognizerStateEnded) {
220      scaleFactor *= [sender scale];
221      mScale = scaleFactor;
222    }
223    else if(sender.state == UIGestureRecognizerStateCancelled) {
224      mScale = scaleFactor;
225    }
226    mScale = MAX(0.1, mScale);
227    glView->mContext->eventHandler(2, mScale);
228  }
229  [glView drawView];
230}
231
232- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
233{
234  UITouch *touch = [[event allTouches] anyObject];
235  CGPoint touchPoint = [touch locationInView:self.view];
236  glView->mContext->eventHandler(0, touchPoint.x, touchPoint.y);
237}
238
239- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
240{
241  [self touchesEnded:touches withEvent:event];
242}
243
244- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
245{
246  UITouch *touch = [[event allTouches] anyObject];
247  CGPoint touchPoint = [touch locationInView:self.view];
248  glView->mContext->eventHandler(4, touchPoint.x, touchPoint.y);
249}
250
251- (IBAction)singleTap:(UITapGestureRecognizer *)sender
252{
253  [self.navigationController
254    setToolbarHidden:(!(self.navigationController.toolbarHidden &&
255                        !((AppDelegate *)[UIApplication sharedApplication]
256                            .delegate)
257                           ->compute &&
258                        number_of_animation() > 0))
259            animated:YES];
260}
261
262- (IBAction)doubleTap:(UITapGestureRecognizer *)sender
263{
264  scaleFactor = 1;
265  glView->mContext->eventHandler(10);
266  [glView drawView];
267}
268
269- (void)showModelsList
270{
271  AppDelegate *appDelegate =
272    (AppDelegate *)[UIApplication sharedApplication].delegate;
273  if(appDelegate->compute) {
274    UIAlertController *alert = [UIAlertController
275      alertControllerWithTitle:@"Cannot show model list"
276                       message:@"Computation has to complete before a new "
277                               @"model can be selected"
278                preferredStyle:UIAlertControllerStyleAlert];
279    UIAlertAction *dismissButton =
280      [UIAlertAction actionWithTitle:@"Dismiss"
281                               style:UIAlertActionStyleDefault
282                             handler:^(UIAlertAction *action){}];
283    [alert addAction:dismissButton];
284    [self presentViewController:alert animated:YES completion:nil];
285    return;
286  }
287  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
288     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]) {
289    [UIView transitionWithView:appDelegate.window
290                      duration:0.5
291                       options:UIViewAnimationOptionTransitionFlipFromRight
292                    animations:^{
293                      appDelegate.window.rootViewController =
294                        appDelegate.modelListController;
295                    }
296                    completion:nil];
297  }
298  [self.navigationController popToRootViewControllerAnimated:YES];
299}
300
301- (void)showSettings
302{
303  [self performSegueWithIdentifier:@"showSettingsSegue" sender:self];
304}
305
306- (void)requestRender
307{
308  if([[UIApplication sharedApplication] applicationState] ==
309     UIApplicationStateActive)
310    [glView drawView];
311}
312
313#pragma mark - Split view
314
315void messageFromCpp(void *self, std::string level, std::string msg)
316{
317  if(level == "RequestRender") {
318    [(__bridge id)self performSelectorOnMainThread:@selector(requestRender)
319                                        withObject:nil
320                                     waitUntilDone:YES];
321    [[NSNotificationCenter defaultCenter]
322      postNotificationName:@"refreshParameters"
323                    object:nil];
324  }
325  else if(level == "Progress") {
326    [(__bridge id)self
327      performSelectorOnMainThread:@selector(setProgress:)
328                       withObject:[Utils getStringFromCString:msg.c_str()]
329                    waitUntilDone:YES];
330  }
331  else if(level == "Error") {
332    [(__bridge id)self
333      performSelectorOnMainThread:@selector(showError:)
334                       withObject:[Utils getStringFromCString:msg.c_str()]
335                    waitUntilDone:YES];
336  }
337}
338
339- (void)setProgress:(NSString *)progress
340{
341  [_progressLabel setText:progress];
342}
343
344- (void)showError:(NSString *)msg
345{
346  // remove document path from error message
347  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
348                                                       NSUserDomainMask, YES);
349  NSString *docPath = [[paths objectAtIndex:0] stringByAppendingString:@"/"];
350  NSString *str = [msg stringByReplacingOccurrencesOfString:docPath
351                                                 withString:@""];
352  UIAlertController *alert =
353    [UIAlertController alertControllerWithTitle:@"Error"
354                                        message:str
355                                 preferredStyle:UIAlertControllerStyleAlert];
356  UIAlertAction *dismissButton =
357    [UIAlertAction actionWithTitle:@"Dismiss"
358                             style:UIAlertActionStyleDefault
359                           handler:^(UIAlertAction *action){}];
360  [alert addAction:dismissButton];
361  [self presentViewController:alert animated:YES completion:nil];
362}
363
364- (void)handleProgressIndicatorTap:(id)sender
365{
366  [_progressLabel setHidden:!_progressLabel.hidden];
367}
368
369void getBitmap(void *self, const char *text, int textsize, unsigned char **map,
370               int *height, int *width, int *realWidth)
371{
372  [(__bridge id)self getBitmapFromStringObjC:text
373                                withTextSize:textsize
374                                       inMap:map
375                                    inHeight:height
376                                     inWidth:width
377                                 inRealWidth:realWidth];
378}
379
380- (void)getBitmapFromStringObjC:(const char *)text
381                   withTextSize:(int)textsize
382                          inMap:(unsigned char **)map
383                       inHeight:(int *)height
384                        inWidth:(int *)width
385                    inRealWidth:(int *)realWidth
386{
387  UILabel *lbl =
388    [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1024, 7 * textsize / 6)];
389  lbl.font = [UIFont systemFontOfSize:textsize];
390  [lbl setText:[Utils getStringFromCString:text]];
391  [lbl setBackgroundColor:[UIColor clearColor]];
392  CGSize lblSize =
393    [[lbl text] sizeWithAttributes:@{NSFontAttributeName : [lbl font]}];
394  *realWidth = lblSize.width;
395  int i = 2;
396  while(i < *realWidth) i *= 2;
397  *width = i;
398  *height = lblSize.height;
399  i = 2;
400  while(i < *height) i *= 2;
401  *height = i;
402
403  UIGraphicsBeginImageContextWithOptions(CGSizeMake(*width, *height), NO, 0.0);
404  [lbl.layer renderInContext:UIGraphicsGetCurrentContext()];
405  UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
406  UIGraphicsEndImageContext();
407  CGImageRef bitmap = [img CGImage];
408  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
409  unsigned char *rawData =
410    (unsigned char *)calloc(*height * *width * 4, sizeof(unsigned char));
411  *map = (unsigned char *)calloc(*height * *width, sizeof(unsigned char));
412  NSUInteger bytesPerPixel = 4;
413  NSUInteger bytesPerRow = bytesPerPixel * *width;
414  NSUInteger bitsPerComponent = 8;
415  CGContextRef context = CGBitmapContextCreate(
416    rawData, *width, *height, bitsPerComponent, bytesPerRow, colorSpace,
417    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
418  CGColorSpaceRelease(colorSpace);
419
420  CGContextDrawImage(context, CGRectMake(0, 0, *width, *height), bitmap);
421  CGContextRelease(context);
422
423  // rawData contains the image data in the RGBA8888 pixel format.
424  for(int byteIndex = 0; byteIndex < *width * *height * 4; byteIndex += 4)
425    *(*map + byteIndex / 4) = rawData[byteIndex + 3];
426  free(rawData);
427}
428
429- (IBAction)startRotation:(UIButton *)sender
430{
431  glView->rotate = !glView->rotate;
432  if(glView->rotate)
433    [sender setImage:[UIImage imageNamed:@"icon_translate.png"]
434            forState:UIControlStateNormal];
435  else
436    [sender setImage:[UIImage imageNamed:@"icon_rotate.png"]
437            forState:UIControlStateNormal];
438}
439
440@end
441