-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAQTAdapter.m
526 lines (449 loc) · 18.7 KB
/
AQTAdapter.m
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
//
// AQTAdapter.m
// AquaTerm
//
// Created by Per Persson on Sat Jul 12 2003.
// Copyright (c) 2003-2004 AquaTerm.
//
#import "AQTAdapter.h"
#import "AQTClientManager.h"
#import "AQTPlotBuilder.h"
@implementation AQTAdapter
/*" AQTAdapter is a class that provides an interface to the functionality of AquaTerm.
As such, it bridges the gap between client's procedural calls requesting operations
such as drawing a line or placing a label and the object-oriented graph being built.
The actual assembling of the graph is performed by an instance of class AQTPlotBuilder.
It seemlessly provides a connection to the viewer (AquaTerm.app) without any work on behalf of the client.
It also provides some utility functionality such an indexed colormap, and an optional
error handling callback function for the client.
Event handling of user input is provided through an optional callback function.
#Example: HelloAquaTerm.c
!{
#import <Foundation/Foundation.h>
#import <AquaTerm/AQTAdapter.h>
int main(void)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AQTAdapter *adapter = [[AQTAdapter alloc] init];
[adapter openPlotWithIndex:1];
[adapter setPlotSize:NSMakeSize(600,400)];
[adapter addLabel:@"HelloAquaTerm!" atPoint:NSMakePoint(300, 200) angle:0.0 align:1];
[adapter renderPlot];
[adapter release];
[pool release];
return 0;
}
}
!{gcc -ObjC main.c -o aqtex -lobjc -framework AquaTerm -framework Foundation}
!{gcc main.m -o aqtex -framework AquaTerm -framework Foundation}
"*/
/*" This is the designated initalizer, allowing for the default handler (an object vended by AquaTerm via OS X's distributed objects mechanism) to be replaced by a local instance. In most cases #init should be used, which calls #initWithHandler: with a nil argument."*/
-(id)initWithServer:(id)localServer
{
if(self = [super init]) {
BOOL serverIsOK = YES;
_clientManager = [AQTClientManager sharedManager];
if (localServer) {
[_clientManager setServer:localServer];
} else {
serverIsOK = [_clientManager connectToServer];
}
if (!serverIsOK) {
[self autorelease];
self = nil;
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(connectionDidDie:)
name:NSConnectionDidDieNotification
object:nil];
}
return self;
}
/*" Initializes an instance and sets up a connection to the handler object via DO. Launches AquaTerm if necessary. "*/
- (id)init
{
return [self initWithServer:nil];
}
- (void)release
{
[_clientManager logMessage:[NSString stringWithFormat:@"adapter rc = %d", [self retainCount]] logLevel:3];
[super release];
}
- (void)dealloc
{
[_clientManager logMessage:@"adapter dealloc, terminating connection." logLevel:3];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_clientManager terminateConnection];
[super dealloc];
}
/*" Optionally set an error handling routine of the form #customErrorHandler(NSString *errMsg) to override default behaviour. "*/
- (void)setErrorHandler:(void (*)(NSString *errMsg))fPtr
{
[_clientManager setErrorHandler:fPtr];
}
/*" Optionally set an event handling routine of the form #customEventHandler(int index, NSString *event).
The reference number of the plot that generated the event is passed in index and
the structure of the string event is @"type:data1:data2:..."
Currently supported events are:
_{event description}
_{0 NoEvent }
_{1:%{x,y}:%button MouseDownEvent }
_{2:%{x,y}:%key KeyDownEvent }
_{42:%{x,y}:%key ServerError }
_{43:%{x,y}:%key Error } "*/
- (void)setEventHandler:(void (*)(int32_t index, NSString *event))fPtr
{
[_clientManager setEventHandler:fPtr];
}
- (void)connectionDidDie:(id)x
{
// NSLog(@"in --> %@ %s line %d", NSStringFromSelector(_cmd), __FILE__, __LINE__);
// Make sure we can't access any invalid objects:
_selectedBuilder = nil;
}
#pragma mark === Control operations ===
/* Creates a new builder instance, adds it to the list of builders and makes it the selected builder. If the referenced builder exists, it is selected and cleared. */
/*" Open up a new plot with internal reference number refNum and make it the target for subsequent commands. If the referenced plot already exists, it is selected and cleared. Disables event handling for previously targeted plot. "*/
- (void)openPlotWithIndex:(int32_t)refNum
{
_selectedBuilder = [_clientManager newPlotWithIndex:refNum];
}
/*" Get the plot referenced by refNum and make it the target for subsequent commands. If no plot exists for refNum, the currently targeted plot remain unchanged. Disables event handling for previously targeted plot. Returns YES on success. "*/
- (BOOL)selectPlotWithIndex:(int32_t)refNum
{
BOOL didChangePlot = NO;
AQTPlotBuilder *tmpBuilder = [_clientManager selectPlotWithIndex:refNum];
if (tmpBuilder != nil)
{
_selectedBuilder = tmpBuilder;
didChangePlot = YES;
}
return didChangePlot;
}
/*" Set the limits of the plot area. Must be set %before any drawing command following an #openPlotWithIndex: or #clearPlot command or behaviour is undefined. "*/
- (void)setPlotSize:(NSSize)canvasSize
{
[_selectedBuilder setSize:canvasSize];
}
/*" Set title to appear in window titlebar, also default name when saving. "*/
- (void)setPlotTitle:(NSString *)title
{
[_selectedBuilder setTitle:title?title:@"Untitled"];
}
/*" Render the current plot in the viewer. "*/
- (void)renderPlot
{
if(_selectedBuilder)
{
[_clientManager renderPlot];
}
else
{
// Just inform user about what is going on...
[_clientManager logMessage:@"Warning: No plot selected" logLevel:2];
}
}
/*" Clears the current plot and resets default values. To keep plot settings, use #eraseRect: instead. "*/
- (void)clearPlot
{
_selectedBuilder = [_clientManager clearPlot];
}
/*" Closes the current plot but leaves viewer window on screen. Disables event handling. "*/
- (void)closePlot
{
[_clientManager closePlot];
_selectedBuilder = nil;
}
#pragma mark === Event handling ===
/*" Inform AquaTerm whether or not events should be passed from the currently selected plot. Deactivates event passing from any plot previously set to pass events. "*/
- (void)setAcceptingEvents:(BOOL)flag
{
[_clientManager setAcceptingEvents:flag];
}
/*" Reads the last event logged by the viewer. Will always return NoEvent unless #setAcceptingEvents: is called with a YES argument."*/
- (NSString *)lastEvent
{
[[NSRunLoop currentRunLoop] runMode:NSConnectionReplyMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
return [_clientManager lastEvent];
}
- (NSString *)waitNextEvent // FIXME: timeout? Hardcoded to 10s
{
NSString *event;
BOOL isRunning;
[self setAcceptingEvents:YES];
do {
isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
event = [_clientManager lastEvent];
isRunning = [event isEqualToString:@"0"]?YES:NO;
} while (isRunning);
[self setAcceptingEvents:NO];
return event;
}
#pragma mark === Plotting commands ===
/*" Set a clipping region (rectangular) to apply to all subsequent operations, until changed again by #setClipRect: or #setDefaultClipRect. "*/
- (void)setClipRect:(NSRect)clip
{
[_selectedBuilder setClipRect:clip];
}
/*" Restore clipping region to the deafult (object bounds), i.e. no clipping performed. "*/
- (void)setDefaultClipRect
{
[_selectedBuilder setDefaultClipRect];
}
/*" Return the number of color entries available in the currently active colormap. "*/
- (int32_t)colormapSize
{
int32_t size = AQT_COLORMAP_SIZE; // Default size
if (_selectedBuilder)
{
size = [_selectedBuilder colormapSize];
}
else
{
// Just inform user about what is going on...
[_clientManager logMessage:@"Warning: No plot selected" logLevel:2];
}
return size;
}
/*" Set an RGB entry in the colormap, at the position given by entryIndex. "*/
- (void)setColormapEntry:(int32_t)entryIndex red:(float)r green:(float)g blue:(float)b alpha:(float)a
{
AQTColor tmpColor;
tmpColor.red = r;
tmpColor.green = g;
tmpColor.blue = b;
tmpColor.alpha = a;
[_selectedBuilder setColor:tmpColor forColormapEntry:entryIndex];
}
- (void)setColormapEntry:(int32_t)entryIndex red:(float)r green:(float)g blue:(float)b
{
[self setColormapEntry:entryIndex red:r green:g blue:b alpha:1.0];
}
/*" Set an RGB entry in the colormap, at the position given by entryIndex. "*/
- (void)getColormapEntry:(int32_t)entryIndex red:(float *)r green:(float *)g blue:(float *)b alpha:(float *)a
{
AQTColor tmpColor = [_selectedBuilder colorForColormapEntry:entryIndex];
*r = tmpColor.red;
*g = tmpColor.green;
*b = tmpColor.blue;
*a = tmpColor.alpha;
}
- (void)getColormapEntry:(int32_t)entryIndex red:(float *)r green:(float *)g blue:(float *)b
{
float dummyAlpha;
[self getColormapEntry:entryIndex red:r green:g blue:b alpha:&dummyAlpha];
}
/*" Set the current color, used for all subsequent items, using the color stored at the position given by index in the colormap. "*/
- (void)takeColorFromColormapEntry:(int32_t)index
{
[_selectedBuilder takeColorFromColormapEntry:index];
}
/*" Set the background color, overriding any previous color, using the color stored at the position given by index in the colormap. "*/
- (void)takeBackgroundColorFromColormapEntry:(int32_t)index
{
[_selectedBuilder takeBackgroundColorFromColormapEntry:index];
}
/*" Set the current color, used for all subsequent items, using explicit RGB components. "*/
- (void)setColorRed:(float)r green:(float)g blue:(float)b alpha:(float)a
{
AQTColor newColor = (AQTColor){r, g, b, a};
[_selectedBuilder setColor:newColor];
}
- (void)setColorRed:(float)r green:(float)g blue:(float)b
{
[self setColorRed:r green:g blue:b alpha:1.0];
}
/*" Set the background color, overriding any previous color, using explicit RGB components. "*/
- (void)setBackgroundColorRed:(float)r green:(float)g blue:(float)b alpha:(float)a
{
AQTColor newColor = (AQTColor){r, g, b, a};
[_selectedBuilder setBackgroundColor:newColor];
}
- (void)setBackgroundColorRed:(float)r green:(float)g blue:(float)b
{
[self setBackgroundColorRed:r green:g blue:b alpha:1.0];
}
/*" Get current RGB color components by reference. "*/
- (void)getColorRed:(float *)r green:(float *)g blue:(float *)b alpha:(float *)a
{
AQTColor tmpColor = [_selectedBuilder color];
*r = tmpColor.red;
*g = tmpColor.green;
*b = tmpColor.blue;
*a = tmpColor.alpha;
}
- (void)getColorRed:(float *)r green:(float *)g blue:(float *)b
{
AQTColor tmpColor = [_selectedBuilder color];
*r = tmpColor.red;
*g = tmpColor.green;
*b = tmpColor.blue;
}
/*" Get background color components by reference. "*/
- (void)getBackgroundColorRed:(float *)r green:(float *)g blue:(float *)b alpha:(float *)a
{
AQTColor tmpColor = [_selectedBuilder backgroundColor];
*r = tmpColor.red;
*g = tmpColor.green;
*b = tmpColor.blue;
*a = tmpColor.alpha;
}
- (void)getBackgroundColorRed:(float *)r green:(float *)g blue:(float *)b
{
float dummyAlpha;
[self getBackgroundColorRed:r green:g blue:b alpha:&dummyAlpha];
}
/*" Set the font to be used. Applies to all future operations. Default is Times-Roman."*/
- (void)setFontname:(NSString *)newFontname
{
[_selectedBuilder setFontname:newFontname];
}
/*" Set the font size in points. Applies to all future operations. Default is 14pt. "*/
- (void)setFontsize:(float)newFontsize
{
[_selectedBuilder setFontsize:newFontsize];
}
/*" Add text at coordinate given by pos, rotated by angle degrees and aligned vertically and horisontally (with respect to pos and rotation) according to align. Horizontal and vertical align may be combined by an OR operation, e.g. (AQTAlignCenter | AQTAlignMiddle).
_{HorizontalAlign Description}
_{AQTAlignLeft LeftAligned}
_{AQTAlignCenter Centered}
_{AQTAlignRight RightAligned}
_{VerticalAlign -}
_{AQTAlignMiddle ApproxCenter}
_{AQTAlignBaseline Normal}
_{AQTAlignBottom BottomBoundsOfTHISString}
_{AQTAlignTop TopBoundsOfTHISString}
By specifying #shearAngle the text may be sheared in order to appear correctly in e.g. 3D plot labels.
The text can be either an NSString or an NSAttributedString. By using NSAttributedString a subset of the attributes defined in AppKit may be used to format the string beyond the fontface ans size. The currently supported attributes are
_{Attribute value}
_{@"NSSuperScript" raise-level}
_{@"NSUnderline" 0or1}
"*/
- (void)addLabel:(id)text atPoint:(NSPoint)pos angle:(float)angle shearAngle:(float)shearAngle align:(int32_t)just
{
[_selectedBuilder addLabel:text position:pos angle:angle shearAngle:shearAngle justification:just];
}
/*" Same as #addLabel:atPoint:angle:shearAngle:align: except that shearAngle defaults to 0."*/
- (void)addLabel:(id)text atPoint:(NSPoint)pos angle:(float)angle align:(int32_t)just
{
[_selectedBuilder addLabel:text position:pos angle:angle shearAngle:0.0 justification:just];
}
/*" Convenience form of #addLabel:atPoint:angle:shearAngle:align: for horizontal, left and baseline aligned text."*/
- (void)addLabel:(id)text atPoint:(NSPoint)pos
{
[_selectedBuilder addLabel:text position:pos angle:0.0 shearAngle:0.0 justification:(AQTAlignLeft | AQTAlignBaseline)];
}
/*" Set the current linewidth (in points), used for all subsequent lines. Any line currently being built by #moveToPoint:/#addLineToPoint will be considered finished since any coalesced sequence of line segments must share the same linewidth. Default linewidth is 1pt."*/
- (void)setLinewidth:(float)newLinewidth
{
[_selectedBuilder setLinewidth:newLinewidth];
}
/*" Set the current line style to pattern style, used for all subsequent lines. The linestyle is specified as a pattern, an array of at most 8 float, where even positions correspond to dash-lengths and odd positions correspond to gap-lengths. To produce e.g. a dash-dotted line, use the pattern {4.0, 2.0, 1.0, 2.0}."*/
- (void)setLinestylePattern:(float *)newPattern count:(int32_t)newCount phase:(float)newPhase
{
[_selectedBuilder setLinestylePattern:newPattern count:newCount phase:newPhase];
}
/*" Set the current line style to solid, used for all subsequent lines. This is the default."*/
- (void)setLinestyleSolid
{
[_selectedBuilder setLinestyleSolid];
}
/*" Set the current line cap style (in points), used for all subsequent lines. Any line currently being built by #moveToPoint:/#addLineToPoint will be considered finished since any coalesced sequence of line segments must share the same cap style.
_{capStyle Description}
_{AQTButtLineCapStyle ButtLineCapStyle}
_{AQTRoundLineCapStyle RoundLineCapStyle}
_{AQTSquareLineCapStyle SquareLineCapStyle}
Default is RoundLineCapStyle. "*/
- (void)setLineCapStyle:(int32_t)capStyle
{
[_selectedBuilder setLineCapStyle:capStyle];
}
/*" Moves the current point (in canvas coordinates) in preparation for a new sequence of line segments. "*/
- (void)moveToPoint:(NSPoint)point
{
[_selectedBuilder moveToPoint:point];
}
/*" Add a line segment from the current point (given by a previous #moveToPoint: or #addLineToPoint). "*/
- (void)addLineToPoint:(NSPoint)point
{
[_selectedBuilder addLineToPoint:point];
}
/*" Add a sequence of line segments specified by a list of start-, end-, and joinpoint(s) in points. Parameter pc is number of line segments + 1."*/
- (void)addPolylineWithPoints:(NSPoint *)points pointCount:(int32_t)pc
{
[_selectedBuilder addPolylineWithPoints:points pointCount:pc];
}
- (void)moveToVertexPoint:(NSPoint)point
{
[_selectedBuilder moveToVertexPoint:point];
}
- (void)addEdgeToVertexPoint:(NSPoint)point
{
[_selectedBuilder addEdgeToPoint:point];
}
/*" Add a polygon specified by a list of corner points. Number of corners is passed in pc."*/
- (void)addPolygonWithVertexPoints:(NSPoint *)points pointCount:(int32_t)pc
{
[_selectedBuilder addPolygonWithPoints:points pointCount:pc];
}
/*" Add a filled rectangle. Will attempt to remove any objects that will be covered by aRect."*/
- (void)addFilledRect:(NSRect)aRect
{
// FIXME: this may be very inefficent, maybe store a AQTClearRect object in the model instead?
// If the filled rect covers a substantial area, it is worthwile to clear it first.
if (NSWidth(aRect)*NSHeight(aRect) > 100.0)
{
[_clientManager clearPlotRect:aRect];
}
[_selectedBuilder addFilledRect:aRect];
}
/*" Remove any objects %completely inside aRect. Does %not force a redraw of the plot."*/
- (void)eraseRect:(NSRect)aRect
{
// FIXME: Possibly keep a list of rects to be erased and pass them before any append command??
[_clientManager clearPlotRect:aRect];
}
/*" Set a transformation matrix for images added by #addTransformedImageWithBitmap:size:clipRect:, see NSImage documentation for details. "*/
- (void)setImageTransformM11:(float)m11 m12:(float)m12 m21:(float)m21 m22:(float)m22 tX:(float)tX tY:(float)tY
{
AQTAffineTransformStruct trans;
trans.m11 = m11;
trans.m12 = m12;
trans.m21 = m21;
trans.m22 = m22;
trans.tX = tX;
trans.tY = tY;
[_selectedBuilder setImageTransform:trans];
}
/*" Set transformation matrix to unity, i.e. no transform. "*/
- (void)resetImageTransform
{
AQTAffineTransformStruct trans;
trans.m11 = 1.0;
trans.m22 = 1.0;
[_selectedBuilder setImageTransform:trans];
}
/*" Add a bitmap image of size bitmapSize scaled to fit destBounds, does %not apply transform. Bitmap format is 24bits per pixel in sequence RGBRGB... with 8 bits per color."*/
- (void)addImageWithBitmap:(const void *)bitmap size:(NSSize)bitmapSize bounds:(NSRect)destBounds
{
[_clientManager clearPlotRect:destBounds];
[_selectedBuilder addImageWithBitmap:bitmap size:bitmapSize bounds:destBounds];
}
/*" Deprecated, use #addTransformedImageWithBitmap:size: instead. Add a bitmap image of size bitmapSize %honoring transform, transformed image is clipped to destBounds. Bitmap format is 24bits per pixel in sequence RGBRGB... with 8 bits per color."*/
- (void)addTransformedImageWithBitmap:(const void *)bitmap size:(NSSize)bitmapSize clipRect:(NSRect)destBounds
{
[_selectedBuilder addTransformedImageWithBitmap:bitmap size:bitmapSize clipRect:destBounds];
}
/*" Add a bitmap image of size bitmapSize %honoring transform, transformed image is clipped to current clipRect. Bitmap format is 24bits per pixel in sequence RGBRGB... with 8 bits per color."*/
- (void)addTransformedImageWithBitmap:(const void *)bitmap size:(NSSize)bitmapSize
{
[_selectedBuilder addTransformedImageWithBitmap:bitmap size:bitmapSize];
}
/*******************************************
* Private methods *
*******************************************/
- (void)timingTestWithTag:(uint32_t)tag
{
[_clientManager timingTestWithTag:tag];
}
@end