1 /* an input matrix
2 */
3
4 /*
5
6 Copyright (C) 1991-2003 The National Gallery
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 */
23
24 /*
25
26 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27
28 */
29
30 /*
31 #define DEBUG
32 */
33
34 #include "ip.h"
35
36 static ClassmodelClass *parent_class = NULL;
37
38 static void
matrix_finalize(GObject * gobject)39 matrix_finalize( GObject *gobject )
40 {
41 Matrix *matrix;
42
43 g_return_if_fail( gobject != NULL );
44 g_return_if_fail( IS_MATRIX( gobject ) );
45
46 matrix = MATRIX( gobject );
47
48 #ifdef DEBUG
49 printf( "matrix_finalize\n" );
50 #endif /*DEBUG*/
51
52 /* My instance finalize stuff.
53 */
54 IM_FREE( matrix->value.coeff );
55
56 G_OBJECT_CLASS( parent_class )->finalize( gobject );
57 }
58
59 /* Rearrange our model for a new width/height.
60 */
61 gboolean
matrix_value_resize(MatrixValue * value,int width,int height)62 matrix_value_resize( MatrixValue *value, int width, int height )
63 {
64 double *coeff;
65 int x, y, i;
66
67 if( width == value->width && height == value->height )
68 return( TRUE );
69
70 if( !(coeff = IARRAY( NULL, width * height, double )) )
71 return( FALSE );
72
73 /* Set what we can with values from the old matrix.
74 */
75 for( i = 0, y = 0; y < height; y++ )
76 for( x = 0; x < width; x++, i++ )
77 if( y < value->height && x < value->width )
78 coeff[i] = value->coeff[x +
79 y * value->width];
80 else
81 coeff[i] = 0.0;
82
83 /* Install new values.
84 */
85 IM_FREE( value->coeff );
86 value->coeff = coeff;
87 value->width = width;
88 value->height = height;
89
90 return( TRUE );
91 }
92
93 /* Widgets for matrix edit.
94 */
95 typedef struct _MatrixEdit {
96 iDialog *idlg;
97
98 Matrix *matrix;
99
100 GtkWidget *width;
101 GtkWidget *height;
102 GtkWidget *display;
103 } MatrixEdit;
104
105 /* Done button hit.
106 */
107 /*ARGSUSED*/
108 static void
matrix_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)109 matrix_done_cb( iWindow *iwnd, void *client,
110 iWindowNotifyFn nfn, void *sys )
111 {
112 MatrixEdit *eds = (MatrixEdit *) client;
113
114 int width, height;
115
116 /* Parse values. We have to scan before we resize in case we are
117 * sizing smaller and we have unscanned changes at the edges.
118 */
119 view_scan_all();
120 eds->matrix->display = (MatrixDisplayType)
121 gtk_combo_box_get_active( GTK_COMBO_BOX( eds->display ) );
122 if( !get_geditable_pint( eds->width, &width ) ||
123 !get_geditable_pint( eds->height, &height ) ||
124 !matrix_value_resize( &eds->matrix->value, width, height ) ) {
125 nfn( sys, IWINDOW_ERROR );
126 return;
127 }
128
129 /* Rebuild object.
130 */
131 classmodel_update( CLASSMODEL( eds->matrix ) );
132 symbol_recalculate_all();
133
134 nfn( sys, IWINDOW_YES );
135 }
136
137 /* Build the insides of matrix edit.
138 */
139 static void
matrix_buildedit(iDialog * idlg,GtkWidget * work,MatrixEdit * eds)140 matrix_buildedit( iDialog *idlg, GtkWidget *work, MatrixEdit *eds )
141 {
142 Matrix *matrix = eds->matrix;
143
144 GtkSizeGroup *group;
145
146 /* Index with MatrixType.
147 */
148 static const char *display_names[] = {
149 N_( "Text" ),
150 N_( "Sliders" ),
151 N_( "Toggle buttons" ),
152 N_( "Text, plus scale and offset" )
153 };
154
155 group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
156
157 eds->width = build_glabeltext4( work, group, "Width" );
158 idialog_init_entry( idlg, eds->width,
159 "Width of matrix", "%d", matrix->value.width );
160 eds->height = build_glabeltext4( work, group, "Height" );
161 idialog_init_entry( idlg, eds->height,
162 "Height of matrix", "%d", matrix->value.height );
163 eds->display = build_goption( work, group, _( "Display as" ),
164 display_names, IM_NUMBER( display_names ), NULL, NULL );
165 gtk_combo_box_set_active( GTK_COMBO_BOX( eds->display ),
166 matrix->display );
167
168 UNREF( group );
169
170 gtk_widget_show_all( work );
171 }
172
173 static View *
matrix_view_new(Model * model,View * parent)174 matrix_view_new( Model *model, View *parent )
175 {
176 return( matrixview_new() );
177 }
178
179 /* Pop up a matrix edit box.
180 */
181 static void
matrix_edit(GtkWidget * parent,Model * model)182 matrix_edit( GtkWidget *parent, Model *model )
183 {
184 Matrix *matrix = MATRIX( model );
185 MatrixEdit *eds = INEW( NULL, MatrixEdit );
186 GtkWidget *idlg;
187
188 eds->matrix = matrix;
189
190 idlg = idialog_new();
191 iwindow_set_title( IWINDOW( idlg ), _( "Edit %s %s" ),
192 IOBJECT_GET_CLASS_NAME( model ),
193 IOBJECT( HEAPMODEL( model )->row )->name );
194 idialog_set_build( IDIALOG( idlg ),
195 (iWindowBuildFn) matrix_buildedit, eds, NULL, NULL );
196 idialog_set_callbacks( IDIALOG( idlg ),
197 iwindow_true_cb, NULL, idialog_free_client, eds );
198 idialog_add_ok( IDIALOG( idlg ),
199 matrix_done_cb, _( "Set %s" ),
200 IOBJECT_GET_CLASS_NAME( model ) );
201 iwindow_set_parent( IWINDOW( idlg ), parent );
202 idialog_set_iobject( IDIALOG( idlg ), IOBJECT( model ) );
203 iwindow_build( IWINDOW( idlg ) );
204
205 gtk_widget_show( GTK_WIDGET( idlg ) );
206 }
207
208 static gboolean
matrix_graphic_save(Classmodel * classmodel,GtkWidget * parent,const char * filename)209 matrix_graphic_save( Classmodel *classmodel,
210 GtkWidget *parent, const char *filename )
211 {
212 Matrix *matrix = MATRIX( classmodel );
213 DOUBLEMASK *dmask;
214 char buf[FILENAME_MAX];
215
216 if( !(dmask = matrix_model_to_dmask( matrix )) )
217 return( FALSE );
218
219 /* We don't want $VAR etc. in the filename we pass down to the file
220 * ops.
221 */
222 im_strncpy( buf, filename, FILENAME_MAX );
223 path_expand( buf );
224
225 if( im_write_dmask_name( dmask, buf ) ) {
226 error_vips_all();
227 IM_FREEF( im_free_dmask, dmask );
228 return( FALSE );
229 }
230 IM_FREEF( im_free_dmask, dmask );
231
232 mainw_recent_add( &mainw_recent_matrix, filename );
233
234 return( TRUE );
235 }
236
237 static gboolean
matrix_graphic_replace(Classmodel * classmodel,GtkWidget * parent,const char * filename)238 matrix_graphic_replace( Classmodel *classmodel,
239 GtkWidget *parent, const char *filename )
240 {
241 Matrix *matrix = MATRIX( classmodel );
242 Row *row = HEAPMODEL( matrix )->row;
243 iText *itext = ITEXT( HEAPMODEL( matrix )->rhs->itext );
244 DOUBLEMASK *dmask;
245 char txt[MAX_STRSIZE];
246 VipsBuf buf = VIPS_BUF_STATIC( txt );
247
248 /* We don't want $VAR etc. in the filename we pass down to the file
249 * ops.
250 */
251 im_strncpy( txt, filename, FILENAME_MAX );
252 path_expand( txt );
253
254 if( !(dmask = im_read_dmask( txt )) ) {
255 error_vips_all();
256 return( FALSE );
257 }
258
259 matrix_dmask_to_ip( dmask, &buf );
260 im_free_dmask( dmask );
261
262 if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) {
263 itext_set_edited( itext, TRUE );
264 (void) expr_dirty( row->expr, link_serial_new() );
265 }
266
267 mainw_recent_add( &mainw_recent_matrix, filename );
268
269 return( TRUE );
270 }
271
272 /* Members of matrix we automate.
273 */
274 static ClassmodelMember matrix_members[] = {
275 { CLASSMODEL_MEMBER_MATRIX, NULL, 0,
276 MEMBER_VALUE, NULL, N_( "Value" ),
277 G_STRUCT_OFFSET( Matrix, value ) },
278 { CLASSMODEL_MEMBER_DOUBLE, NULL, 0,
279 MEMBER_SCALE, "scale", N_( "Scale" ),
280 G_STRUCT_OFFSET( Matrix, scale ) },
281 { CLASSMODEL_MEMBER_DOUBLE, NULL, 0,
282 MEMBER_OFFSET, "offset", N_( "Offset" ),
283 G_STRUCT_OFFSET( Matrix, offset ) },
284 { CLASSMODEL_MEMBER_STRING, NULL, 0,
285 MEMBER_FILENAME, "filename", N_( "Filename" ),
286 G_STRUCT_OFFSET( Classmodel, filename ) },
287 { CLASSMODEL_MEMBER_ENUM, NULL, MATRIX_DISPLAY_LAST - 1,
288 MEMBER_DISPLAY, "display", N_( "Display" ),
289 G_STRUCT_OFFSET( Matrix, display ) }
290 };
291
292 static void
matrix_class_init(MatrixClass * class)293 matrix_class_init( MatrixClass *class )
294 {
295 GObjectClass *gobject_class = (GObjectClass *) class;
296 iObjectClass *iobject_class = (iObjectClass *) class;
297 ModelClass *model_class = (ModelClass *) class;
298 ClassmodelClass *classmodel_class = (ClassmodelClass *) class;
299
300 parent_class = g_type_class_peek_parent( class );
301
302 /* Create signals.
303 */
304
305 /* Init methods.
306 */
307 gobject_class->finalize = matrix_finalize;
308
309 iobject_class->user_name = _( "Matrix" );
310
311 model_class->view_new = matrix_view_new;
312 model_class->edit = matrix_edit;
313
314 classmodel_class->graphic_save = matrix_graphic_save;
315 classmodel_class->graphic_replace = matrix_graphic_replace;
316
317 classmodel_class->filetype = filesel_type_matrix;
318 classmodel_class->filetype_pref = "MATRIX_FILE_TYPE";
319
320 /* Static init.
321 */
322 model_register_loadable( MODEL_CLASS( class ) );
323
324 classmodel_class->members = matrix_members;
325 classmodel_class->n_members = IM_NUMBER( matrix_members );
326 }
327
328 static void
matrix_init(Matrix * matrix)329 matrix_init( Matrix *matrix )
330 {
331 #ifdef DEBUG
332 printf( "matrix_init\n" );
333 #endif /*DEBUG*/
334
335 matrix->value.coeff = NULL;
336 matrix->value.width = 0;
337 matrix->value.height = 0;
338 matrix->display = MATRIX_DISPLAY_TEXT;
339 matrix->scale = 1.0;
340 matrix->offset = 0.0;
341 matrix->selected = FALSE;
342
343 iobject_set( IOBJECT( matrix ), CLASS_MATRIX, NULL );
344 }
345
346 GtkType
matrix_get_type(void)347 matrix_get_type( void )
348 {
349 static GType type = 0;
350
351 if( !type ) {
352 static const GTypeInfo info = {
353 sizeof( MatrixClass ),
354 NULL, /* base_init */
355 NULL, /* base_finalize */
356 (GClassInitFunc) matrix_class_init,
357 NULL, /* class_finalize */
358 NULL, /* class_data */
359 sizeof( Matrix ),
360 32, /* n_preallocs */
361 (GInstanceInitFunc) matrix_init,
362 };
363
364 type = g_type_register_static( TYPE_CLASSMODEL,
365 "Matrix", &info, 0 );
366 }
367
368 return( type );
369 }
370
371 void
matrix_select(Matrix * matrix,int left,int top,int width,int height)372 matrix_select( Matrix *matrix, int left, int top, int width, int height )
373 {
374 if( !matrix->selected ||
375 matrix->range.left != left ||
376 matrix->range.top != top ||
377 matrix->range.width != width ||
378 matrix->range.height != height ) {
379 Row *row = HEAPMODEL( matrix )->row;
380
381 #ifdef DEBUG
382 printf( "matrix_select: "
383 "left=%d, top = %d, width = %d, height = %d\n",
384 left, top, width, height );
385 #endif /*DEBUG*/
386
387 matrix->selected = TRUE;
388 matrix->range.left = left;
389 matrix->range.top = top;
390 matrix->range.width = width;
391 matrix->range.height = height;
392 iobject_changed( IOBJECT( matrix ) );
393
394 /* Also make sure this row is selected.
395 */
396 row_select_ensure( row );
397
398 /* The range of cells selected has changed, so the workspace
399 * must update the status line too. row_select_ensure() only
400 * spots row on/off selects. Yuk!
401 */
402 iobject_changed( IOBJECT( row->ws ) );
403 }
404 }
405
406 void
matrix_deselect(Matrix * matrix)407 matrix_deselect( Matrix *matrix )
408 {
409 if( matrix->selected ) {
410 Row *row = HEAPMODEL( matrix )->row;
411
412 #ifdef DEBUG
413 printf( "matrix_deselect\n" );
414 #endif /*DEBUG*/
415
416 matrix->selected = FALSE;
417 iobject_changed( IOBJECT( matrix ) );
418
419 /* Also make sure this row is not selected.
420 */
421 row_deselect( row );
422 }
423 }
424
425 /* Guess a display type from a filename.
426 */
427 static int
matrix_guess_display(const char * fname)428 matrix_guess_display( const char *fname )
429 {
430 /* Choose display type based on filename suffix ... rec
431 * displays as 1, mor displays as 2, .con displays as 3, all others
432 * display as 0. Keep in sync with MatrixDisplayType.
433 */
434 static const FileselFileType *types[] = {
435 &filesel_xfile_type, // matrix
436 &filesel_rfile_type, // recombination
437 &filesel_mfile_type, // morphology
438 &filesel_cfile_type // convolution
439 };
440
441 int i;
442
443 if( !fname )
444 return( 0 );
445
446 for( i = 0; i < IM_NUMBER( types ); i++ )
447 if( is_file_type( types[i], fname ) )
448 return( i );
449
450 return( 0 );
451 }
452
453 /* Make an ip definition out of a DOUBLEMASK.
454 */
455 void
matrix_dmask_to_ip(DOUBLEMASK * dmask,VipsBuf * buf)456 matrix_dmask_to_ip( DOUBLEMASK *dmask, VipsBuf *buf )
457 {
458 int x, y;
459
460 /* Build matrix expression.
461 */
462 vips_buf_appends( buf, CLASS_MATRIX " " );
463
464 vips_buf_appends( buf, "[" );
465 for( y = 0; y < dmask->ysize; y++ ) {
466 vips_buf_appends( buf, "[" );
467 for( x = 0; x < dmask->xsize; x++ ) {
468 vips_buf_appendf( buf, "%g",
469 dmask->coeff[x + y*dmask->xsize] );
470 if( x != dmask->xsize - 1 )
471 vips_buf_appends( buf, "," );
472 }
473 vips_buf_appends( buf, "]" );
474 if( y != dmask->ysize - 1 )
475 vips_buf_appends( buf, "," );
476 }
477 vips_buf_appends( buf, "]" );
478
479 vips_buf_appendf( buf, "(%g) (%g) \"%s\" %d",
480 dmask->scale, dmask->offset, dmask->filename,
481 matrix_guess_display( dmask->filename ) );
482 }
483
484 /* Make a heap object out of a DOUBLEMASK.
485 */
486 gboolean
matrix_dmask_to_heap(Heap * heap,DOUBLEMASK * dmask,PElement * out)487 matrix_dmask_to_heap( Heap *heap, DOUBLEMASK *dmask, PElement *out )
488 {
489 Symbol *sym = compile_lookup( symbol_root->expr->compile,
490 CLASS_MATRIX );
491
492 PElement rhs;
493
494 if( !sym || !sym->expr || !sym->expr->compile ||
495 !heap_copy( heap, sym->expr->compile, out ) )
496 return( FALSE );
497
498 if( !heap_appl_add( heap, out, &rhs ) ||
499 !heap_matrix_new( heap,
500 dmask->xsize, dmask->ysize, dmask->coeff, &rhs ) ||
501 !heap_appl_add( heap, out, &rhs ) ||
502 !heap_real_new( heap, dmask->scale, &rhs ) ||
503 !heap_appl_add( heap, out, &rhs ) ||
504 !heap_real_new( heap, dmask->offset, &rhs ) ||
505 !heap_appl_add( heap, out, &rhs ) ||
506 !heap_managedstring_new( heap, dmask->filename, &rhs ) ||
507 !heap_appl_add( heap, out, &rhs ) ||
508 !heap_real_new( heap,
509 matrix_guess_display( dmask->filename ), &rhs ) )
510 return( FALSE );
511
512 return( TRUE );
513 }
514
515 /* Cast an IMASK to a DMASK.
516 */
517 DOUBLEMASK *
matrix_imask_to_dmask(INTMASK * imask)518 matrix_imask_to_dmask( INTMASK *imask )
519 {
520 DOUBLEMASK *dmask;
521 int i;
522
523 if( !(dmask = im_create_dmask( imask->filename,
524 imask->xsize, imask->ysize )) ) {
525 error_vips_all();
526 return( NULL );
527 }
528
529 dmask->scale = imask->scale;
530 dmask->offset = imask->offset;
531 for( i = 0; i < imask->xsize * imask->ysize; i++ )
532 dmask->coeff[i] = imask->coeff[i];
533
534 return( dmask );
535 }
536
537 /* Cast a DMASK to an IMASK.
538 */
539 INTMASK *
matrix_dmask_to_imask(DOUBLEMASK * dmask)540 matrix_dmask_to_imask( DOUBLEMASK *dmask )
541 {
542 INTMASK *imask;
543 int i;
544
545 if( !(imask = im_create_imask( dmask->filename,
546 dmask->xsize, dmask->ysize )) ) {
547 error_vips_all();
548 return( NULL );
549 }
550
551 imask->scale = dmask->scale;
552 imask->offset = dmask->offset;
553 for( i = 0; i < dmask->xsize * dmask->ysize; i++ )
554 imask->coeff[i] = dmask->coeff[i];
555
556 return( imask );
557 }
558
559 /* Make a heap object out of an INTMASK.
560 */
561 gboolean
matrix_imask_to_heap(Heap * heap,INTMASK * imask,PElement * out)562 matrix_imask_to_heap( Heap *heap, INTMASK *imask, PElement *out )
563 {
564 DOUBLEMASK *dmask;
565
566 if( !(dmask = matrix_imask_to_dmask( imask )) )
567 return( FALSE );
568 if( !matrix_dmask_to_heap( heap, dmask, out ) ) {
569 im_free_dmask( dmask );
570 return( FALSE );
571 }
572 im_free_dmask( dmask );
573
574 return( TRUE );
575 }
576
577 /* Make a DOUBLEMASK out of an ip value.
578 */
579 DOUBLEMASK *
matrix_ip_to_dmask(PElement * root)580 matrix_ip_to_dmask( PElement *root )
581 {
582 char buf[MAX_STRSIZE];
583 char name[FILENAME_MAX];
584 DOUBLEMASK *dmask;
585 double scale, offset;
586 char *filename;
587 int width, height;
588
589 if( !class_get_member_matrix_size( root,
590 MEMBER_VALUE, &width, &height ) )
591 return( NULL );
592
593 if( class_get_member_string( root, MEMBER_FILENAME, buf, MAX_STRSIZE ) )
594 filename = buf;
595 else {
596 if( !temp_name( name, "mat" ) )
597 return( NULL );
598
599 filename = name;
600 }
601
602 if( !(dmask = im_create_dmask( filename, width, height )) ) {
603 error_vips_all();
604 return( NULL );
605 }
606
607 if( !class_get_member_matrix( root, MEMBER_VALUE,
608 dmask->coeff, width * height, &width, &height ) ) {
609 IM_FREEF( im_free_dmask, dmask );
610 return( FALSE );
611 }
612
613 if( !class_get_member_real( root, MEMBER_SCALE, &scale ) )
614 scale = 1.0;
615 if( !class_get_member_real( root, MEMBER_OFFSET, &offset ) )
616 offset = 0.0;
617 dmask->scale = scale;
618 dmask->offset = offset;
619
620 return( dmask );
621 }
622
623 /* Make an INTMASK out of an ip value.
624 */
625 INTMASK *
matrix_ip_to_imask(PElement * root)626 matrix_ip_to_imask( PElement *root )
627 {
628 DOUBLEMASK *dmask;
629 INTMASK *imask;
630
631 if( !(dmask = matrix_ip_to_dmask( root )) )
632 return( NULL );
633
634 if( !(imask = matrix_dmask_to_imask( dmask )) ) {
635 IM_FREEF( im_free_dmask, dmask );
636 return( NULL );
637 }
638
639 return( imask );
640 }
641
642 DOUBLEMASK *
matrix_model_to_dmask(Matrix * matrix)643 matrix_model_to_dmask( Matrix *matrix )
644 {
645 DOUBLEMASK *dmask;
646 int i;
647
648 if( !(dmask = im_create_dmask( CLASSMODEL( matrix )->filename,
649 matrix->value.width, matrix->value.height )) ) {
650 error_vips_all();
651 return( NULL );
652 }
653
654 dmask->scale = matrix->scale;
655 dmask->offset = matrix->offset;
656 for( i = 0; i < matrix->value.width * matrix->value.height; i++ )
657 dmask->coeff[i] = matrix->value.coeff[i];
658
659 return( dmask );
660 }
661
662 gboolean
matrix_dmask_to_model(Matrix * matrix,DOUBLEMASK * dmask)663 matrix_dmask_to_model( Matrix *matrix, DOUBLEMASK *dmask )
664 {
665 int i;
666
667 if( !matrix_value_resize( &matrix->value,
668 dmask->xsize, dmask->ysize ) )
669 return( FALSE );
670
671 matrix->scale = dmask->scale;
672 matrix->offset = dmask->offset;
673 for( i = 0; i < matrix->value.width * matrix->value.height; i++ )
674 matrix->value.coeff[i] = dmask->coeff[i];
675 matrix->display =
676 (MatrixDisplayType) matrix_guess_display( dmask->filename );
677 IM_SETSTR( CLASSMODEL( matrix )->filename, dmask->filename );
678
679 return( TRUE );
680 }
681
682