comparison plugin/transform/FeatureExtractionModelTransformer.cpp @ 350:d7c41483af8f

* Merge from transforms branch -- switch over to using Transform object properly
author Chris Cannam
date Fri, 07 Dec 2007 16:47:31 +0000
parents 277006c62fea
children 399ea254afd6
comparison
equal deleted inserted replaced
348:edda24bb85fc 350:d7c41483af8f
27 #include "data/model/DenseTimeValueModel.h" 27 #include "data/model/DenseTimeValueModel.h"
28 #include "data/model/NoteModel.h" 28 #include "data/model/NoteModel.h"
29 #include "data/model/FFTModel.h" 29 #include "data/model/FFTModel.h"
30 #include "data/model/WaveFileModel.h" 30 #include "data/model/WaveFileModel.h"
31 31
32 #include "TransformFactory.h"
33
32 #include <QMessageBox> 34 #include <QMessageBox>
33 35
34 #include <iostream> 36 #include <iostream>
35 37
36 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Model *inputModel, 38 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
37 QString pluginId, 39 const Transform &transform) :
38 const ExecutionContext &context, 40 ModelTransformer(in, transform),
39 QString configurationXml,
40 QString outputName) :
41 PluginTransformer(inputModel, context),
42 m_plugin(0), 41 m_plugin(0),
43 m_descriptor(0), 42 m_descriptor(0),
44 m_outputFeatureNo(0) 43 m_outputFeatureNo(0)
45 { 44 {
46 // std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; 45 // std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl;
46
47 QString pluginId = transform.getPluginIdentifier();
47 48
48 FeatureExtractionPluginFactory *factory = 49 FeatureExtractionPluginFactory *factory =
49 FeatureExtractionPluginFactory::instanceFor(pluginId); 50 FeatureExtractionPluginFactory::instanceFor(pluginId);
50 51
51 if (!factory) { 52 if (!factory) {
52 std::cerr << "FeatureExtractionModelTransformer: No factory available for plugin id \"" 53 std::cerr << "FeatureExtractionModelTransformer: No factory available for plugin id \""
53 << pluginId.toStdString() << "\"" << std::endl; 54 << pluginId.toStdString() << "\"" << std::endl;
54 return; 55 return;
55 } 56 }
56 57
57 m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); 58 DenseTimeValueModel *input = getConformingInput();
58 59 if (!input) {
60 std::cerr << "FeatureExtractionModelTransformer: Input model not conformable" << std::endl;
61 return;
62 }
63
64 m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate());
59 if (!m_plugin) { 65 if (!m_plugin) {
60 std::cerr << "FeatureExtractionModelTransformer: Failed to instantiate plugin \"" 66 std::cerr << "FeatureExtractionModelTransformer: Failed to instantiate plugin \""
61 << pluginId.toStdString() << "\"" << std::endl; 67 << pluginId.toStdString() << "\"" << std::endl;
62 return; 68 return;
63 } 69 }
64 70
65 m_context.makeConsistentWithPlugin(m_plugin); 71 TransformFactory::getInstance()->makeContextConsistentWithPlugin
66 72 (m_transform, m_plugin);
67 if (configurationXml != "") { 73
68 PluginXml(m_plugin).setParametersFromXml(configurationXml); 74 TransformFactory::getInstance()->setPluginParameters
69 } 75 (m_transform, m_plugin);
70
71 DenseTimeValueModel *input = getInput();
72 if (!input) return;
73 76
74 size_t channelCount = input->getChannelCount(); 77 size_t channelCount = input->getChannelCount();
75 if (m_plugin->getMaxChannelCount() < channelCount) { 78 if (m_plugin->getMaxChannelCount() < channelCount) {
76 channelCount = 1; 79 channelCount = 1;
77 } 80 }
83 << input->getChannelCount() << ")" << std::endl; 86 << input->getChannelCount() << ")" << std::endl;
84 return; 87 return;
85 } 88 }
86 89
87 std::cerr << "Initialising feature extraction plugin with channels = " 90 std::cerr << "Initialising feature extraction plugin with channels = "
88 << channelCount << ", step = " << m_context.stepSize 91 << channelCount << ", step = " << m_transform.getStepSize()
89 << ", block = " << m_context.blockSize << std::endl; 92 << ", block = " << m_transform.getBlockSize() << std::endl;
90 93
91 if (!m_plugin->initialise(channelCount, 94 if (!m_plugin->initialise(channelCount,
92 m_context.stepSize, 95 m_transform.getStepSize(),
93 m_context.blockSize)) { 96 m_transform.getBlockSize())) {
94 std::cerr << "FeatureExtractionModelTransformer: Plugin " 97 std::cerr << "FeatureExtractionModelTransformer: Plugin "
95 << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; 98 << pluginId.toStdString() << " failed to initialise!" << std::endl;
96 return; 99 return;
97 } 100 }
98 101
99 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); 102 Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
100 103
103 << pluginId.toStdString() << "\" has no outputs" << std::endl; 106 << pluginId.toStdString() << "\" has no outputs" << std::endl;
104 return; 107 return;
105 } 108 }
106 109
107 for (size_t i = 0; i < outputs.size(); ++i) { 110 for (size_t i = 0; i < outputs.size(); ++i) {
108 if (outputName == "" || outputs[i].identifier == outputName.toStdString()) { 111 if (m_transform.getOutput() == "" ||
112 outputs[i].identifier == m_transform.getOutput().toStdString()) {
109 m_outputFeatureNo = i; 113 m_outputFeatureNo = i;
110 m_descriptor = new Vamp::Plugin::OutputDescriptor 114 m_descriptor = new Vamp::Plugin::OutputDescriptor
111 (outputs[i]); 115 (outputs[i]);
112 break; 116 break;
113 } 117 }
114 } 118 }
115 119
116 if (!m_descriptor) { 120 if (!m_descriptor) {
117 std::cerr << "FeatureExtractionModelTransformer: Plugin \"" 121 std::cerr << "FeatureExtractionModelTransformer: Plugin \""
118 << pluginId.toStdString() << "\" has no output named \"" 122 << pluginId.toStdString() << "\" has no output named \""
119 << outputName.toStdString() << "\"" << std::endl; 123 << m_transform.getOutput().toStdString() << "\"" << std::endl;
120 return; 124 return;
121 } 125 }
122 126
123 // std::cerr << "FeatureExtractionModelTransformer: output sample type " 127 // std::cerr << "FeatureExtractionModelTransformer: output sample type "
124 // << m_descriptor->sampleType << std::endl; 128 // << m_descriptor->sampleType << std::endl;
138 minValue = m_descriptor->minValue; 142 minValue = m_descriptor->minValue;
139 maxValue = m_descriptor->maxValue; 143 maxValue = m_descriptor->maxValue;
140 haveExtents = true; 144 haveExtents = true;
141 } 145 }
142 146
143 size_t modelRate = m_input->getSampleRate(); 147 size_t modelRate = input->getSampleRate();
144 size_t modelResolution = 1; 148 size_t modelResolution = 1;
145 149
146 switch (m_descriptor->sampleType) { 150 switch (m_descriptor->sampleType) {
147 151
148 case Vamp::Plugin::OutputDescriptor::VariableSampleRate: 152 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
150 modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001); 154 modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001);
151 } 155 }
152 break; 156 break;
153 157
154 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: 158 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
155 modelResolution = m_context.stepSize; 159 modelResolution = m_transform.getStepSize();
156 break; 160 break;
157 161
158 case Vamp::Plugin::OutputDescriptor::FixedSampleRate: 162 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
159 modelRate = size_t(m_descriptor->sampleRate + 0.001); 163 modelRate = size_t(m_descriptor->sampleRate + 0.001);
160 break; 164 break;
217 } 221 }
218 222
219 m_output = model; 223 m_output = model;
220 } 224 }
221 225
222 if (m_output) m_output->setSourceModel(m_input); 226 if (m_output) m_output->setSourceModel(input);
223 } 227 }
224 228
225 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() 229 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
226 { 230 {
227 std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl; 231 std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl;
228 delete m_plugin; 232 delete m_plugin;
229 delete m_descriptor; 233 delete m_descriptor;
230 } 234 }
231 235
232 DenseTimeValueModel * 236 DenseTimeValueModel *
233 FeatureExtractionModelTransformer::getInput() 237 FeatureExtractionModelTransformer::getConformingInput()
234 { 238 {
235 DenseTimeValueModel *dtvm = 239 DenseTimeValueModel *dtvm =
236 dynamic_cast<DenseTimeValueModel *>(getInputModel()); 240 dynamic_cast<DenseTimeValueModel *>(getInputModel());
237 if (!dtvm) { 241 if (!dtvm) {
238 std::cerr << "FeatureExtractionModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; 242 std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
239 } 243 }
240 return dtvm; 244 return dtvm;
241 } 245 }
242 246
243 void 247 void
244 FeatureExtractionModelTransformer::run() 248 FeatureExtractionModelTransformer::run()
245 { 249 {
246 DenseTimeValueModel *input = getInput(); 250 DenseTimeValueModel *input = getConformingInput();
247 if (!input) return; 251 if (!input) return;
248 252
249 if (!m_output) return; 253 if (!m_output) return;
250 254
251 while (!input->isReady()) { 255 while (!input->isReady()) {
258 */ 262 */
259 std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl; 263 std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl;
260 sleep(1); 264 sleep(1);
261 } 265 }
262 266
263 size_t sampleRate = m_input->getSampleRate(); 267 size_t sampleRate = input->getSampleRate();
264 268
265 size_t channelCount = input->getChannelCount(); 269 size_t channelCount = input->getChannelCount();
266 if (m_plugin->getMaxChannelCount() < channelCount) { 270 if (m_plugin->getMaxChannelCount() < channelCount) {
267 channelCount = 1; 271 channelCount = 1;
268 } 272 }
269 273
270 float **buffers = new float*[channelCount]; 274 float **buffers = new float*[channelCount];
271 for (size_t ch = 0; ch < channelCount; ++ch) { 275 for (size_t ch = 0; ch < channelCount; ++ch) {
272 buffers[ch] = new float[m_context.blockSize + 2]; 276 buffers[ch] = new float[m_transform.getBlockSize() + 2];
273 } 277 }
278
279 size_t stepSize = m_transform.getStepSize();
280 size_t blockSize = m_transform.getBlockSize();
274 281
275 bool frequencyDomain = (m_plugin->getInputDomain() == 282 bool frequencyDomain = (m_plugin->getInputDomain() ==
276 Vamp::Plugin::FrequencyDomain); 283 Vamp::Plugin::FrequencyDomain);
277 std::vector<FFTModel *> fftModels; 284 std::vector<FFTModel *> fftModels;
278 285
279 if (frequencyDomain) { 286 if (frequencyDomain) {
280 for (size_t ch = 0; ch < channelCount; ++ch) { 287 for (size_t ch = 0; ch < channelCount; ++ch) {
281 FFTModel *model = new FFTModel 288 FFTModel *model = new FFTModel
282 (getInput(), 289 (getConformingInput(),
283 channelCount == 1 ? m_context.channel : ch, 290 channelCount == 1 ? m_input.getChannel() : ch,
284 m_context.windowType, 291 m_transform.getWindowType(),
285 m_context.blockSize, 292 blockSize,
286 m_context.stepSize, 293 stepSize,
287 m_context.blockSize, 294 blockSize,
288 false, 295 false,
289 StorageAdviser::PrecisionCritical); 296 StorageAdviser::PrecisionCritical);
290 if (!model->isOK()) { 297 if (!model->isOK()) {
291 QMessageBox::critical 298 QMessageBox::critical
292 (0, tr("FFT cache failed"), 299 (0, tr("FFT cache failed"),
299 model->resume(); 306 model->resume();
300 fftModels.push_back(model); 307 fftModels.push_back(model);
301 } 308 }
302 } 309 }
303 310
304 long startFrame = m_input->getStartFrame(); 311 long startFrame = m_input.getModel()->getStartFrame();
305 long endFrame = m_input->getEndFrame(); 312 long endFrame = m_input.getModel()->getEndFrame();
306 313
307 long contextStart = m_context.startFrame; 314 RealTime contextStartRT = m_transform.getStartTime();
308 long contextDuration = m_context.duration; 315 RealTime contextDurationRT = m_transform.getDuration();
316
317 long contextStart =
318 RealTime::realTime2Frame(contextStartRT, sampleRate);
319
320 long contextDuration =
321 RealTime::realTime2Frame(contextDurationRT, sampleRate);
309 322
310 if (contextStart == 0 || contextStart < startFrame) { 323 if (contextStart == 0 || contextStart < startFrame) {
311 contextStart = startFrame; 324 contextStart = startFrame;
312 } 325 }
313 326
325 setCompletion(0); 338 setCompletion(0);
326 339
327 while (!m_abandoned) { 340 while (!m_abandoned) {
328 341
329 if (frequencyDomain) { 342 if (frequencyDomain) {
330 if (blockFrame - int(m_context.blockSize)/2 > 343 if (blockFrame - int(blockSize)/2 >
331 contextStart + contextDuration) break; 344 contextStart + contextDuration) break;
332 } else { 345 } else {
333 if (blockFrame >= 346 if (blockFrame >=
334 contextStart + contextDuration) break; 347 contextStart + contextDuration) break;
335 } 348 }
336 349
337 // std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " 350 // std::cerr << "FeatureExtractionModelTransformer::run: blockFrame "
338 // << blockFrame << ", endFrame " << endFrame << ", blockSize " 351 // << blockFrame << ", endFrame " << endFrame << ", blockSize "
339 // << m_context.blockSize << std::endl; 352 // << blockSize << std::endl;
340 353
341 long completion = 354 long completion =
342 (((blockFrame - contextStart) / m_context.stepSize) * 99) / 355 (((blockFrame - contextStart) / stepSize) * 99) /
343 (contextDuration / m_context.stepSize); 356 (contextDuration / stepSize);
344 357
345 // channelCount is either m_input->channelCount or 1 358 // channelCount is either m_input.getModel()->channelCount or 1
346 359
347 for (size_t ch = 0; ch < channelCount; ++ch) { 360 for (size_t ch = 0; ch < channelCount; ++ch) {
348 if (frequencyDomain) { 361 if (frequencyDomain) {
349 int column = (blockFrame - startFrame) / m_context.stepSize; 362 int column = (blockFrame - startFrame) / stepSize;
350 for (size_t i = 0; i <= m_context.blockSize/2; ++i) { 363 for (size_t i = 0; i <= blockSize/2; ++i) {
351 fftModels[ch]->getValuesAt 364 fftModels[ch]->getValuesAt
352 (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); 365 (column, i, buffers[ch][i*2], buffers[ch][i*2+1]);
353 } 366 }
354 } else { 367 } else {
355 getFrames(ch, channelCount, 368 getFrames(ch, channelCount,
356 blockFrame, m_context.blockSize, buffers[ch]); 369 blockFrame, blockSize, buffers[ch]);
357 } 370 }
358 } 371 }
359 372
360 Vamp::Plugin::FeatureSet features = m_plugin->process 373 Vamp::Plugin::FeatureSet features = m_plugin->process
361 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); 374 (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
369 if (blockFrame == contextStart || completion > prevCompletion) { 382 if (blockFrame == contextStart || completion > prevCompletion) {
370 setCompletion(completion); 383 setCompletion(completion);
371 prevCompletion = completion; 384 prevCompletion = completion;
372 } 385 }
373 386
374 blockFrame += m_context.stepSize; 387 blockFrame += stepSize;
375 } 388 }
376 389
377 if (m_abandoned) return; 390 if (m_abandoned) return;
378 391
379 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); 392 Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
408 size -= offset; 421 size -= offset;
409 if (size <= 0) return; 422 if (size <= 0) return;
410 startFrame = 0; 423 startFrame = 0;
411 } 424 }
412 425
413 long got = getInput()->getData 426 DenseTimeValueModel *input = getConformingInput();
414 ((channelCount == 1 ? m_context.channel : channel), 427 if (!input) return;
428
429 long got = input->getData
430 ((channelCount == 1 ? m_input.getChannel() : channel),
415 startFrame, size, buffer + offset); 431 startFrame, size, buffer + offset);
416 432
417 while (got < size) { 433 while (got < size) {
418 buffer[offset + got] = 0.0; 434 buffer[offset + got] = 0.0;
419 ++got; 435 ++got;
420 } 436 }
421 437
422 if (m_context.channel == -1 && channelCount == 1 && 438 if (m_input.getChannel() == -1 && channelCount == 1 &&
423 getInput()->getChannelCount() > 1) { 439 input->getChannelCount() > 1) {
424 // use mean instead of sum, as plugin input 440 // use mean instead of sum, as plugin input
425 int cc = getInput()->getChannelCount(); 441 int cc = input->getChannelCount();
426 for (long i = 0; i < size; ++i) { 442 for (long i = 0; i < size; ++i) {
427 buffer[i] /= cc; 443 buffer[i] /= cc;
428 } 444 }
429 } 445 }
430 } 446 }
431 447
432 void 448 void
433 FeatureExtractionModelTransformer::addFeature(size_t blockFrame, 449 FeatureExtractionModelTransformer::addFeature(size_t blockFrame,
434 const Vamp::Plugin::Feature &feature) 450 const Vamp::Plugin::Feature &feature)
435 { 451 {
436 size_t inputRate = m_input->getSampleRate(); 452 size_t inputRate = m_input.getModel()->getSampleRate();
437 453
438 // std::cerr << "FeatureExtractionModelTransformer::addFeature(" 454 // std::cerr << "FeatureExtractionModelTransformer::addFeature("
439 // << blockFrame << ")" << std::endl; 455 // << blockFrame << ")" << std::endl;
440 456
441 int binCount = 1; 457 int binCount = 1;
470 } 486 }
471 } 487 }
472 488
473 if (binCount == 0) { 489 if (binCount == 0) {
474 490
475 SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); 491 SparseOneDimensionalModel *model =
476 if (!model) return; 492 getConformingOutput<SparseOneDimensionalModel>();
493 if (!model) return;
494
477 model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); 495 model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
478 496
479 } else if (binCount == 1) { 497 } else if (binCount == 1) {
480 498
481 float value = 0.0; 499 float value = 0.0;
482 if (feature.values.size() > 0) value = feature.values[0]; 500 if (feature.values.size() > 0) value = feature.values[0];
483 501
484 SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); 502 SparseTimeValueModel *model =
485 if (!model) return; 503 getConformingOutput<SparseTimeValueModel>();
504 if (!model) return;
505
486 model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); 506 model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
487 // std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; 507 // std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl;
488 508
489 } else if (m_descriptor->sampleType == 509 } else if (m_descriptor->sampleType ==
490 Vamp::Plugin::OutputDescriptor::VariableSampleRate) { 510 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
498 float velocity = 100; 518 float velocity = 100;
499 if (feature.values.size() > 2) velocity = feature.values[2]; 519 if (feature.values.size() > 2) velocity = feature.values[2];
500 if (velocity < 0) velocity = 127; 520 if (velocity < 0) velocity = 127;
501 if (velocity > 127) velocity = 127; 521 if (velocity > 127) velocity = 127;
502 522
503 NoteModel *model = getOutput<NoteModel>(); 523 NoteModel *model = getConformingOutput<NoteModel>();
504 if (!model) return; 524 if (!model) return;
505 525
506 model->addPoint(NoteModel::Point(frame, pitch, 526 model->addPoint(NoteModel::Point(frame, pitch,
507 lrintf(duration), 527 lrintf(duration),
508 velocity / 127.f, 528 velocity / 127.f,
511 } else { 531 } else {
512 532
513 DenseThreeDimensionalModel::Column values = feature.values; 533 DenseThreeDimensionalModel::Column values = feature.values;
514 534
515 EditableDenseThreeDimensionalModel *model = 535 EditableDenseThreeDimensionalModel *model =
516 getOutput<EditableDenseThreeDimensionalModel>(); 536 getConformingOutput<EditableDenseThreeDimensionalModel>();
517 if (!model) return; 537 if (!model) return;
518 538
519 model->setColumn(frame / model->getResolution(), values); 539 model->setColumn(frame / model->getResolution(), values);
520 } 540 }
521 } 541 }
531 // std::cerr << "FeatureExtractionModelTransformer::setCompletion(" 551 // std::cerr << "FeatureExtractionModelTransformer::setCompletion("
532 // << completion << ")" << std::endl; 552 // << completion << ")" << std::endl;
533 553
534 if (binCount == 0) { 554 if (binCount == 0) {
535 555
536 SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); 556 SparseOneDimensionalModel *model =
537 if (!model) return; 557 getConformingOutput<SparseOneDimensionalModel>();
538 model->setCompletion(completion, m_context.updates); 558 if (!model) return;
559 model->setCompletion(completion, true); //!!!m_context.updates);
539 560
540 } else if (binCount == 1) { 561 } else if (binCount == 1) {
541 562
542 SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); 563 SparseTimeValueModel *model =
543 if (!model) return; 564 getConformingOutput<SparseTimeValueModel>();
544 model->setCompletion(completion, m_context.updates); 565 if (!model) return;
566 model->setCompletion(completion, true); //!!!m_context.updates);
545 567
546 } else if (m_descriptor->sampleType == 568 } else if (m_descriptor->sampleType ==
547 Vamp::Plugin::OutputDescriptor::VariableSampleRate) { 569 Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
548 570
549 NoteModel *model = getOutput<NoteModel>(); 571 NoteModel *model =
550 if (!model) return; 572 getConformingOutput<NoteModel>();
551 model->setCompletion(completion, m_context.updates); 573 if (!model) return;
574 model->setCompletion(completion, true); //!!!m_context.updates);
552 575
553 } else { 576 } else {
554 577
555 EditableDenseThreeDimensionalModel *model = 578 EditableDenseThreeDimensionalModel *model =
556 getOutput<EditableDenseThreeDimensionalModel>(); 579 getConformingOutput<EditableDenseThreeDimensionalModel>();
557 if (!model) return; 580 if (!model) return;
558 model->setCompletion(completion, m_context.updates); 581 model->setCompletion(completion, true); //!!!m_context.updates);
559 } 582 }
560 } 583 }
561 584