Vuo  2.0.0
VuoRenderedLayers.c
Go to the documentation of this file.
1 
10 #include "type.h"
11 #include "VuoRenderedLayers.h"
12 #include "VuoImageText.h"
13 #include "VuoSceneText.h"
14 
16 #ifdef VUO_COMPILER
18  "title" : "Window State",
19  "description" : "A set of layers, transformed to their final positions for rendering",
20  "keywords" : [ ],
21  "version" : "1.0.0",
22  "dependencies" : [
23  "VuoInteraction",
24  "VuoImageText",
25  "VuoRectangle",
26  "VuoSceneObject",
27  "VuoSceneText",
28  "VuoWindowReference",
29  "VuoList_VuoInteraction",
30  "VuoList_VuoSceneObject"
31  ]
32  });
33 #endif
34 
39 bool VuoRenderedLayers_getTransformedLayer2(VuoRenderedLayers renderedLayers, float localToWorldMatrix[16], VuoSceneObject targetObject, VuoPoint2d *layerCenter, VuoPoint2d layerCorners[4]);
40 
47  unsigned long int pixelsWide,
48  unsigned long int pixelsHigh,
49  float backingScaleFactor,
50  VuoList_VuoInteraction interactions)
51 {
53  rl.rootSceneObject = rootSceneObject;
54  rl.pixelsWide = pixelsWide;
55  rl.pixelsHigh = pixelsHigh;
56  rl.backingScaleFactor = backingScaleFactor;
57  rl.window = 0;
58  rl.interactions = interactions;
59  rl.hasRootSceneObject = true;
60  rl.hasRenderingDimensions = true;
61  rl.hasWindow = false;
62  return rl;
63 }
64 
71  unsigned long int pixelsWide, unsigned long int pixelsHigh,
72  float backingScaleFactor,
73  VuoWindowReference window,
74  VuoList_VuoInteraction interactions)
75 {
77  rl.rootSceneObject = rootSceneObject;
78  rl.pixelsWide = pixelsWide;
79  rl.pixelsHigh = pixelsHigh;
80  rl.backingScaleFactor = backingScaleFactor;
81  rl.window = window;
82  rl.interactions = interactions;
83  rl.hasRootSceneObject = true;
84  rl.hasRenderingDimensions = true;
85  rl.hasWindow = true;
86  return rl;
87 }
88 
94 {
96 
97  rl.interactions = NULL;
98 
99  rl.hasRootSceneObject = false;
100  rl.rootSceneObject = VuoSceneObject_makeEmpty();
101 
102  rl.hasRenderingDimensions = false;
103  rl.pixelsWide = 0;
104  rl.pixelsHigh = 0;
105  rl.backingScaleFactor = 1;
106 
107  rl.hasWindow = false;
108  rl.window = NULL;
109 
110  return rl;
111 }
112 
118 {
119  renderedLayers->interactions = interactions;
120 }
121 
124 {
125  renderedLayers->hasRootSceneObject = true;
126  renderedLayers->rootSceneObject = rootSceneObject;
127 }
128 
131 {
132  renderedLayers->hasWindow = true;
133  renderedLayers->window = window;
134 }
135 
141 {
142  if (renderedLayers.interactions)
143  {
144  *interactions = renderedLayers.interactions;
145  return true;
146  }
147 
148  return false;
149 }
150 
152 bool VuoRenderedLayers_getRootSceneObject(const VuoRenderedLayers renderedLayers, VuoSceneObject *rootSceneObject)
153 {
154  if (renderedLayers.hasRootSceneObject)
155  {
156  *rootSceneObject = renderedLayers.rootSceneObject;
157  return true;
158  }
159 
160  return false;
161 }
162 
164 bool VuoRenderedLayers_getRenderingDimensions(const VuoRenderedLayers renderedLayers, unsigned long int *pixelsWide, unsigned long int *pixelsHigh, float *backingScaleFactor)
165 {
166  if (renderedLayers.hasRenderingDimensions)
167  {
168  *pixelsWide = renderedLayers.pixelsWide;
169  *pixelsHigh = renderedLayers.pixelsHigh;
170  *backingScaleFactor = renderedLayers.backingScaleFactor;
171  return true;
172  }
173 
174  return false;
175 }
176 
179 {
180  if (renderedLayers.hasWindow)
181  {
182  *window = renderedLayers.window;
183  return true;
184  }
185 
186  return false;
187 }
188 
192 bool VuoRenderedLayers_windowChanged(const VuoRenderedLayers accumulatedRenderedLayers, const VuoRenderedLayers newerRenderedLayers)
193 {
194  VuoWindowReference window;
195  if (VuoRenderedLayers_getWindow(newerRenderedLayers, &window))
196  {
197  VuoWindowReference oldWindow;
198  bool hasWindow = VuoRenderedLayers_getWindow(accumulatedRenderedLayers, &oldWindow);
199  if (! hasWindow || oldWindow != window)
200  return true;
201  }
202 
203  return false;
204 }
205 
218 void VuoRenderedLayers_update(VuoRenderedLayers *accumulatedRenderedLayers, const VuoRenderedLayers newerRenderedLayers,
219  bool *renderingDimensionsChanged)
220 {
221  *renderingDimensionsChanged = false;
222 
223  VuoRetain(newerRenderedLayers.interactions);
224  VuoRelease(accumulatedRenderedLayers->interactions);
225  VuoRenderedLayers_setInteractions(accumulatedRenderedLayers, newerRenderedLayers.interactions);
226 
227  VuoSceneObject rootSceneObject;
228  if (VuoRenderedLayers_getRootSceneObject(newerRenderedLayers, &rootSceneObject))
229  {
230  VuoSceneObject_retain(rootSceneObject);
231  VuoSceneObject_release(accumulatedRenderedLayers->rootSceneObject);
232  VuoRenderedLayers_setRootSceneObject(accumulatedRenderedLayers, rootSceneObject);
233  }
234 
235  VuoWindowReference window;
236  if (VuoRenderedLayers_getWindow(newerRenderedLayers, &window))
237  {
238  VuoRetain(window);
239  VuoRelease(accumulatedRenderedLayers->window);
240  VuoRenderedLayers_setWindow(accumulatedRenderedLayers, window);
241  }
242 
243  if (VuoRenderedLayers_getWindow(*accumulatedRenderedLayers, &window))
244  {
245  VuoInteger pixelsWide;
246  VuoInteger pixelsHigh;
247  float backingScaleFactor;
248  VuoWindowReference_getContentSize(window, &pixelsWide, &pixelsHigh, &backingScaleFactor);
249 
250  if (! accumulatedRenderedLayers->hasRenderingDimensions ||
251  accumulatedRenderedLayers->pixelsWide != pixelsWide || accumulatedRenderedLayers->pixelsHigh != pixelsHigh || accumulatedRenderedLayers->backingScaleFactor != backingScaleFactor)
252  *renderingDimensionsChanged = true;
253 
254  accumulatedRenderedLayers->hasRenderingDimensions = true;
255  accumulatedRenderedLayers->pixelsWide = pixelsWide;
256  accumulatedRenderedLayers->pixelsHigh = pixelsHigh;
257  accumulatedRenderedLayers->backingScaleFactor = backingScaleFactor;
258  }
259 }
260 
264 bool VuoRenderedLayers_findLayer(VuoRenderedLayers renderedLayers, VuoText layerName, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
265 {
266  return VuoSceneObject_find(renderedLayers.rootSceneObject, layerName, ancestorObjects, foundObject);
267 }
268 
272 bool VuoRenderedLayers_findLayerId(VuoRenderedLayers renderedLayers, uint64_t layerId, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
273 {
274  return VuoSceneObject_findById(renderedLayers.rootSceneObject, layerId, ancestorObjects, foundObject);
275 }
276 
283  VuoList_VuoSceneObject ancestorObjects,
284  VuoSceneObject targetObject,
285  VuoPoint3d* layerCenter3d,
286  VuoPoint3d layerCorners3d[4],
287  bool applyTargetTransform)
288 {
289  float matrix[16];
290  VuoPoint3d center = *layerCenter3d;
291  bool isText = targetObject.type == VuoSceneObjectSubType_Text && targetObject.text != NULL;
292 
293  if (targetObject.isRealSize || (isText && !targetObject.scaleWithScene))
294  {
295  // Real-size layer:
296  // Apply the layer's transformations to the center point.
297  unsigned long ancestorObjectCount = VuoListGetCount_VuoSceneObject(ancestorObjects);
298 
299  // apply local transform first
300  if(applyTargetTransform)
301  {
302  VuoTransform_getMatrix(targetObject.transform, matrix);
303  center = VuoTransform_transformPoint(matrix, center);
304  }
305 
306  for (unsigned long i = ancestorObjectCount; i >= 1; --i)
307  {
308  VuoSceneObject ancestorObject = VuoListGetValue_VuoSceneObject(ancestorObjects, i);
309  VuoTransform_getMatrix(ancestorObject.transform, matrix);
310  center = VuoTransform_transformPoint(matrix, center);
311  }
312  }
313  else
314  {
315  // Scaled layer:
316  // Apply the layer's transformations to each of its corner points.
317  unsigned long ancestorObjectCount = VuoListGetCount_VuoSceneObject(ancestorObjects);
318 
319  // local transform first
320  if(applyTargetTransform)
321  {
322  VuoTransform_getMatrix(targetObject.transform, matrix);
323 
324  for(int i = 0; i < 4; i++)
325  layerCorners3d[i] = VuoTransform_transformPoint(matrix, layerCorners3d[i]);
326 
327  center = VuoTransform_transformPoint(matrix, center);
328  }
329 
330  for (unsigned long i = ancestorObjectCount; i >= 1; --i)
331  {
332  VuoSceneObject ancestorObject = VuoListGetValue_VuoSceneObject(ancestorObjects, i);
333  VuoTransform_getMatrix(ancestorObject.transform, matrix);
334  if(layerCorners3d != NULL)
335  {
336  for (int i = 0; i < 4; ++i)
337  layerCorners3d[i] = VuoTransform_transformPoint(matrix, layerCorners3d[i]);
338  }
339  center = VuoTransform_transformPoint(matrix, center);
340  }
341  }
342 
343  *layerCenter3d = center;
344 }
345 
351 bool VuoRenderedLayers_getLayerCorners(const VuoSceneObject targetObject, VuoPoint3d layerCorners3d[4])
352 {
353  if( targetObject.mesh == NULL ||
354  targetObject.mesh->submeshCount < 1 ||
355  targetObject.mesh->submeshes[0].vertexCount < 3)
356  return false;
357 
358  VuoSubmesh layerQuad = targetObject.mesh->submeshes[0];
359 
360  // point3d because transformations are carried out using 3d transform functions
361  layerCorners3d[0] = VuoPoint3d_make(layerQuad.positions[0].x, layerQuad.positions[0].y, 0);
362  layerCorners3d[1] = VuoPoint3d_make(layerQuad.positions[1].x, layerQuad.positions[1].y, 0);
363  layerCorners3d[2] = VuoPoint3d_make(layerQuad.positions[2].x, layerQuad.positions[2].y, 0);
364  layerCorners3d[3] = VuoPoint3d_make(layerQuad.positions[3].x, layerQuad.positions[3].y, 0);
365 
366  return true;
367 }
368 
372 VuoPoint3d VuoRenderedLayers_getQuadCenter(VuoPoint3d layerCorners3d[4])
373 {
374  VuoPoint3d c = layerCorners3d[0];
375 
376  for(int i = 1; i < 4; i++)
377  {
378  c.x += layerCorners3d[i].x;
379  c.y += layerCorners3d[i].y;
380  }
381 
382  return VuoPoint3d_multiply(c, .25);
383 }
384 
388 bool VuoRenderedLayers_getRectRecursive(VuoRenderedLayers renderedLayers, float compositeMatrix[16], VuoSceneObject targetObject, VuoRectangle* rect, bool rectIsInitialized)
389 {
390  bool foundRect = rectIsInitialized;
391  VuoPoint2d layerCenter;
392  VuoPoint2d layerCorners[4];
393 
394  // apply targetObject transform
395  float localToWorldMatrix[16];
396  float modelMatrix[16];
397 
398  VuoTransform_getMatrix(targetObject.transform, modelMatrix);
399  VuoTransform_multiplyMatrices4x4(modelMatrix, compositeMatrix, localToWorldMatrix);
400 
401  if( VuoRenderedLayers_getTransformedLayer2(renderedLayers, localToWorldMatrix, targetObject, &layerCenter, layerCorners) )
402  {
403  VuoRectangle thisRect = VuoRenderedLayers_getBoundingBox(layerCorners);
404 
405  if(rectIsInitialized)
406  *rect = VuoRectangle_union(*rect, thisRect);
407  else
408  *rect = thisRect;
409 
410  foundRect = true;
411  }
412 
413  int children = VuoListGetCount_VuoSceneObject(targetObject.childObjects);
414 
415  for(int i = 1; i <= children; i++)
416  {
417  VuoSceneObject child = VuoListGetValue_VuoSceneObject(targetObject.childObjects, i);
418 
419  if( VuoRenderedLayers_getRectRecursive(renderedLayers, localToWorldMatrix, child, rect, foundRect) )
420  foundRect = true;
421  }
422 
423  return foundRect;
424 }
425 
430 {
431  float identity[16] = {
432  1, 0, 0, 0,
433  0, 1, 0, 0,
434  0, 0, 1, 0,
435  0, 0, 0, 1,
436  };
437 
438  return VuoRenderedLayers_getRectRecursive(renderedLayers, identity, layer, rect, false);
439 }
440 
447  VuoRenderedLayers renderedLayers,
448  VuoList_VuoSceneObject ancestorObjects,
449  VuoSceneObject targetObject,
450  VuoPoint2d *layerCenter,
451  VuoPoint2d layerCorners[4],
452  bool includeChildrenInBounds)
453 {
454  for (int i = 0; i < 4; ++i)
455  layerCorners[i] = (VuoPoint2d){NAN,NAN};
456 
457  // Get the layer's corner points.
458  VuoPoint3d layerCorners3d[4];
459 
460  if(includeChildrenInBounds)
461  {
462  VuoRectangle rect;
463 
464  if(!VuoRenderedLayers_getRect(renderedLayers, targetObject, &rect))
465  return false;
466 
467  VuoPoint2d c = rect.center;
468  VuoPoint2d e = VuoPoint2d_multiply(rect.size, .5);
469 
470  layerCorners3d[0] = VuoPoint3d_make( c.x - e.x, c.y - e.y, 0. );
471  layerCorners3d[1] = VuoPoint3d_make( c.x + e.x, c.y - e.y, 0. );
472  layerCorners3d[2] = VuoPoint3d_make( c.x - e.x, c.y + e.y, 0. );
473  layerCorners3d[3] = VuoPoint3d_make( c.x + e.x, c.y + e.y, 0. );
474  }
475  else
476  {
477  if( !VuoRenderedLayers_getLayerCorners(targetObject, layerCorners3d) )
478  return false;
479  }
480 
481  bool isText = targetObject.type == VuoSceneObjectSubType_Text && targetObject.text != NULL;
482 
483  // if includeChildren is true VuoRenderedLayers_getRect will have already applied scale
484  if(!includeChildrenInBounds && !isText && targetObject.shader != NULL)
485  {
486  for (int i = 0; i < 4; ++i)
487  {
488  layerCorners3d[i].x *= targetObject.shader->objectScale;
489  layerCorners3d[i].y *= targetObject.shader->objectScale;
490  }
491  }
492 
493  // Transform the layer to the rendered layers' coordinate space.
494  VuoPoint3d layerCenter3d = VuoRenderedLayers_getQuadCenter(layerCorners3d);
495 
496  VuoRenderedLayers_applyTransforms(renderedLayers, ancestorObjects, targetObject, &layerCenter3d, layerCorners3d, !includeChildrenInBounds);
497 
498  for (int i = 0; i < 4; ++i)
499  layerCorners[i] = VuoPoint2d_make(layerCorners3d[i].x, layerCorners3d[i].y);
500 
501  *layerCenter = VuoPoint2d_make(layerCenter3d.x, layerCenter3d.y);
502 
503  return true;
504 }
505 
509 bool VuoRenderedLayers_getTransformedPoint(VuoRenderedLayers renderedLayers, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject targetObject, VuoPoint2d point, VuoPoint2d *transformedPoint)
510 {
511  if(targetObject.shader == NULL)
512  return false;
513 
514  VuoPoint3d tp = VuoPoint3d_make(point.x, point.y, 0);
515 
516  tp.x *= targetObject.shader->objectScale;
517  tp.y *= targetObject.shader->objectScale;
518 
519  // Transform the layer to the rendered layers' coordinate space.
520  VuoRenderedLayers_applyTransforms(renderedLayers, ancestorObjects, targetObject, &tp, NULL, true);
521 
522  *transformedPoint = VuoPoint2d_make(tp.x, tp.y);
523 
524  return true;
525 }
526 
530 bool VuoRenderedLayers_getInverseTransformedPointLayer(VuoRenderedLayers renderedLayers, uint64_t targetLayer, VuoPoint2d point, VuoPoint2d* localPoint)
531 {
533  VuoLocal(ancestors);
534  VuoSceneObject target;
535 
536  if( VuoRenderedLayers_findLayerId(renderedLayers, targetLayer, ancestors, &target) )
537  {
539 
540  if(VuoRenderedLayers_getInverseTransformedPoint(renderedLayers, ancestors, dummy, point, localPoint))
541  return true;
542  }
543 
544  return false;
545 }
549 bool VuoRenderedLayers_getInverseTransformedPoint(VuoRenderedLayers renderedLayers, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject targetObject, VuoPoint2d point, VuoPoint2d *inverseTransformedPoint)
550 {
551  float localToWorldMatrix[16];
552  float tmp[16];
553  float modelMatrix[16];
554 
555  // start off with the targetObject matrix, then work up
556  VuoTransform_getMatrix(targetObject.transform, localToWorldMatrix);
557 
558  int count = VuoListGetCount_VuoSceneObject(ancestorObjects);
559 
560  for(int i = count; i > 0; i--)
561  {
562  // apply targetObject transform
563  VuoSceneObject node = VuoListGetValue_VuoSceneObject(ancestorObjects, i);
564 
565  VuoTransform_copyMatrix4x4(localToWorldMatrix, tmp);
566  VuoTransform_getMatrix(node.transform, modelMatrix);
567  VuoTransform_multiplyMatrices4x4(modelMatrix, tmp, localToWorldMatrix);
568  }
569 
570  float worldToLocalMatrix[16];
571  VuoTransform_invertMatrix4x4(localToWorldMatrix, worldToLocalMatrix);
572 
573  VuoPoint3d point3d = VuoPoint3d_make(point.x, point.y, 0);
574  VuoPoint3d invPoint = VuoTransform_transformPoint(worldToLocalMatrix, point3d);
575 
576  if(targetObject.shader != NULL)
577  {
578  invPoint.x /= targetObject.shader->objectScale;
579  invPoint.y /= targetObject.shader->objectScale;
580  }
581 
582  *inverseTransformedPoint = VuoPoint2d_make(invPoint.x, invPoint.y);
583 
584  return true;
585 }
586 
590 bool VuoRenderedLayers_getTransformedLayer2(VuoRenderedLayers renderedLayers, float localToWorldMatrix[16], VuoSceneObject targetObject, VuoPoint2d *layerCenter, VuoPoint2d layerCorners[4])
591 {
592  // Get the layer's corner points.
593  VuoPoint3d layerCorners3d[4];
594 
595  if( !VuoRenderedLayers_getLayerCorners(targetObject, layerCorners3d) )
596  return false;
597 
598  bool isText = targetObject.type == VuoSceneObjectSubType_Text && targetObject.text != NULL;
599 
600  if(targetObject.shader == NULL && !isText)
601  return false;
602 
603  if(!isText)
604  {
605  for (int i = 0; i < 4; ++i)
606  {
607  layerCorners3d[i].x *= targetObject.shader->objectScale;
608  layerCorners3d[i].y *= targetObject.shader->objectScale;
609  }
610  }
611 
612  // Transform the layer to the rendered layers' coordinate space.
613  VuoPoint3d center = VuoPoint3d_make(0,0,0);
614 
615  if (targetObject.isRealSize || isText)
616  {
617  // Real-size layer:
618  // Apply the layer's transformations to the center point.
619  center = VuoTransform_transformPoint(localToWorldMatrix, center);
620 
621  float widthScale = 1, heightScale = 1;
622 
623  // Scale the layer's corner points to the rendered layers' coordinate space.
624  if(isText)
625  {
626  float verticalScale = 1.;
627  float rotationZ = 0.;
628  if (targetObject.scaleWithScene)
629  {
630  VuoTransform transform = VuoTransform_makeFromMatrix4x4(localToWorldMatrix);
631  widthScale = transform.scale.x;
632  verticalScale = transform.scale.y / transform.scale.x;
633  rotationZ = VuoTransform_getEuler(transform).z;
634  }
635 
636  VuoPoint2d size = VuoRenderedLayers_getTextSize(renderedLayers, targetObject.text, targetObject.font, targetObject.scaleWithScene, verticalScale, rotationZ, targetObject.wrapWidth, true);
637 
638  center.xy += VuoSceneText_getAnchorOffset(targetObject, verticalScale, rotationZ, targetObject.wrapWidth,
639  targetObject.scaleWithScene ? VuoGraphicsWindowDefaultWidth * renderedLayers.backingScaleFactor : renderedLayers.pixelsWide,
640  renderedLayers.backingScaleFactor);
641 
642  widthScale *= size.x;
643  heightScale *= size.y;
644  }
645  else
646  {
647  VuoImage image = VuoShader_getUniform_VuoImage(targetObject.shader, "texture");
648 
649  widthScale = 2. * (float)image->pixelsWide / (float)renderedLayers.pixelsWide;
650  heightScale = widthScale * (float)image->pixelsHigh / (float)image->pixelsWide;
651 
652  VuoReal combinedScaleFactor = 1;
653 
654  if (targetObject.preservePhysicalSize)
655  combinedScaleFactor = renderedLayers.backingScaleFactor / image->scaleFactor;
656 
657  widthScale *= combinedScaleFactor;
658  heightScale *= combinedScaleFactor;
659  }
660 
661  for (int i = 0; i < 4; ++i)
662  {
663  layerCorners3d[i].x *= widthScale;
664  layerCorners3d[i].y *= heightScale;
665 
666  layerCorners3d[i].x += center.x;
667  layerCorners3d[i].y += center.y;
668  }
669  }
670  else
671  {
672  // Scaled layer:
673  // Apply the layer's transformations to each of its corner points.
674  for (int i = 0; i < 4; ++i)
675  layerCorners3d[i] = VuoTransform_transformPoint(localToWorldMatrix, layerCorners3d[i]);
676 
677  center = VuoTransform_transformPoint(localToWorldMatrix, center);
678  }
679 
680  for (int i = 0; i < 4; ++i)
681  layerCorners[i] = VuoPoint2d_make(layerCorners3d[i].x, layerCorners3d[i].y);
682 
683  *layerCenter = VuoPoint2d_make(center.x, center.y);
684 
685  return true;
686 }
687 
691 VuoPoint2d VuoRenderedLayers_getTextSize(VuoRenderedLayers renderedLayers, VuoText text, VuoFont font, bool scaleWithScene, float verticalScale, float rotationZ, float wrapWidth, bool includeTrailingWhiteSpace)
692 {
693  // If we don't know the size of the render destination,
694  // we can't calculate the size of real-size text.
695  if (!scaleWithScene && (renderedLayers.pixelsWide < 1 || renderedLayers.pixelsHigh < 1))
696  return VuoPoint2d_make(0, 0);
697 
698  VuoRectangle textBounds = VuoImage_getTextRectangle(text, font, renderedLayers.backingScaleFactor, verticalScale, rotationZ, wrapWidth, includeTrailingWhiteSpace);
699 
700  float w = textBounds.size.x;
701  float h = textBounds.size.y;
702 
703  VuoPoint2d size;
704  if (scaleWithScene)
705  size.x = (w / (float)(VuoGraphicsWindowDefaultWidth * renderedLayers.backingScaleFactor)) * 2;
706  else
707  size.x = (w / (float)renderedLayers.pixelsWide) * 2;
708  size.y = size.x * (h / w);
709 
710  return size;
711 }
712 
717 {
718  VuoPoint2d min = VuoPoint2d_make( INFINITY, INFINITY);
719  VuoPoint2d max = VuoPoint2d_make(-INFINITY, -INFINITY);
720  for (int i = 0; i < 4; ++i)
721  {
722  if (isnan(layerCorners[i].x) || isnan(layerCorners[i].y))
723  goto nan;
724 
725  if (layerCorners[i].x < min.x)
726  min.x = layerCorners[i].x;
727  if (layerCorners[i].x > max.x)
728  max.x = layerCorners[i].x;
729  if (layerCorners[i].y < min.y)
730  min.y = layerCorners[i].y;
731  if (layerCorners[i].y > max.y)
732  max.y = layerCorners[i].y;
733  }
734  return VuoRectangle_make(
735  min.x + (max.x-min.x)/2.,
736  min.y + (max.y-min.y)/2.,
737  max.x-min.x,
738  max.y-min.y);
739 nan:
740  return (VuoRectangle){
741  (VuoPoint2d){NAN,NAN},
742  (VuoPoint2d){NAN,NAN}
743  };
744 }
745 
749 bool VuoRenderedLayers_isPointInQuad(VuoPoint2d corners[4], VuoPoint2d point)
750 {
751  // Split the quad into two triangles with points specified counter-clockwise, and check each triangle.
752  // https://stackoverflow.com/questions/2049582/how-to-determine-a-point-in-a-triangle
753  VuoPoint2d triangles[] = { corners[0], corners[1], corners[2],
754  corners[3], corners[2], corners[1] };
755  for (int i = 0; i < 2; ++i)
756  {
757  VuoPoint2d p0 = triangles[3*i];
758  VuoPoint2d p1 = triangles[3*i+1];
759  VuoPoint2d p2 = triangles[3*i+2];
760  float area = 1./2.*(-p1.y*p2.x + p0.y*(-p1.x + p2.x) + p0.x*(p1.y - p2.y) + p1.x*p2.y);
761  float s = 1./(2.*area)*(p0.y*p2.x - p0.x*p2.y + (p2.y - p0.y)*point.x + (p0.x - p2.x)*point.y);
762  float t = 1./(2.*area)*(p0.x*p1.y - p0.y*p1.x + (p0.y - p1.y)*point.x + (p1.x - p0.x)*point.y);
763  if (s >= 0 && t >= 0 && s + t <= 1)
764  return true;
765  }
766 
767  return false;
768 }
769 
773 bool VuoRenderedLayers_isPointInLayerRecursive(VuoRenderedLayers renderedLayers, float compositeMatrix[16], VuoSceneObject targetObject, VuoPoint2d point)
774 {
775  VuoPoint2d layerCenter;
776  VuoPoint2d layerCorners[4];
777 
778  // apply targetObject transform
779  float localToWorldMatrix[16];
780  float modelMatrix[16];
781 
782  VuoTransform_getMatrix(targetObject.transform, modelMatrix);
783  VuoTransform_multiplyMatrices4x4(modelMatrix, compositeMatrix, localToWorldMatrix);
784 
785  if( VuoRenderedLayers_getTransformedLayer2(renderedLayers, localToWorldMatrix, targetObject, &layerCenter, layerCorners) )
786  {
787  if( VuoRenderedLayers_isPointInQuad(layerCorners, point) )
788  return true;
789  }
790 
791  int children = VuoListGetCount_VuoSceneObject(targetObject.childObjects);
792 
793  for(int i = 1; i <= children; i++)
794  {
795  VuoSceneObject child = VuoListGetValue_VuoSceneObject(targetObject.childObjects, i);
796  if( VuoRenderedLayers_isPointInLayerRecursive(renderedLayers, localToWorldMatrix, child, point) )
797  return true;
798  }
799 
800  return false;
801 }
802 
808 bool VuoRenderedLayers_isPointInLayer(VuoRenderedLayers renderedLayers, VuoText layerName, VuoPoint2d point)
809 {
810  float aspect = (double) renderedLayers.pixelsHigh / (double) renderedLayers.pixelsWide;
811 
812  if( point.x < -1 || point.x > 1 || point.y < -aspect || point.y > aspect )
813  return false;
814 
815  VuoSceneObject layer;
817  VuoLocal(ancestors);
818 
819  if(!VuoRenderedLayers_findLayer(renderedLayers, layerName, ancestors, &layer))
820  return false;
821 
822  float modelMatrix[16], tmp[16];
823  float composite[16] = {
824  1, 0, 0, 0,
825  0, 1, 0, 0,
826  0, 0, 1, 0,
827  0, 0, 0, 1,
828  };
829 
830  for(int i = 1; i <= VuoListGetCount_VuoSceneObject(ancestors); i++)
831  {
833  VuoTransform_getMatrix(o.transform, modelMatrix);
834  VuoTransform_multiplyMatrices4x4(modelMatrix, composite, tmp);
835  VuoTransform_copyMatrix4x4(tmp, composite);
836  }
837 
838  return VuoRenderedLayers_isPointInLayerRecursive(renderedLayers, composite, layer, point);
839 }
840 
846 bool VuoRenderedLayers_isPointInLayerId(VuoRenderedLayers renderedLayers, uint64_t layerId, VuoPoint2d point)
847 {
848  float aspect = (double) renderedLayers.pixelsHigh / (double) renderedLayers.pixelsWide;
849 
850  if( point.x < -1 || point.x > 1 || point.y < -aspect || point.y > aspect )
851  return false;
852 
853  VuoSceneObject layer;
855  VuoLocal(ancestors);
856 
857  if (!VuoSceneObject_findById(renderedLayers.rootSceneObject, layerId, ancestors, &layer))
858  return false;
859 
860  float modelMatrix[16], tmp[16];
861  float composite[16] = {
862  1, 0, 0, 0,
863  0, 1, 0, 0,
864  0, 0, 1, 0,
865  0, 0, 0, 1,
866  };
867 
868  for(int i = 1; i <= VuoListGetCount_VuoSceneObject(ancestors); i++)
869  {
871  VuoTransform_getMatrix(o.transform, modelMatrix);
872  VuoTransform_multiplyMatrices4x4(modelMatrix, composite, tmp);
873  VuoTransform_copyMatrix4x4(tmp, composite);
874  }
875 
876  return VuoRenderedLayers_isPointInLayerRecursive(renderedLayers, composite, layer, point);
877 }
878 
883  uint64_t id,
884  bool *anyHover,
885  bool *anyPressed,
886  bool *anyReleased,
887  bool *anyClicked)
888 {
889  *anyHover = false;
890  *anyPressed = false;
891  *anyReleased = false;
892  *anyClicked = false;
893 
894  if(renderedLayers.interactions == NULL || VuoListGetCount_VuoInteraction(renderedLayers.interactions) < 1)
895  return;
896 
897  for(int i = 1; i <= VuoListGetCount_VuoInteraction(renderedLayers.interactions); i++)
898  {
899  VuoInteraction it = VuoListGetValue_VuoInteraction(renderedLayers.interactions, i);
900 
901  if( VuoRenderedLayers_isPointInLayerId(renderedLayers, id, it.position) )
902  {
903  *anyHover = true;
904 
905  if(it.isPressed)
906  *anyPressed = true;
907 
908  if( it.type == VuoInteractionType_Click )
909  *anyClicked = true;
910 
911  if( it.type == VuoInteractionType_Release )
912  *anyReleased = true;
913  }
914  }
915 }
921 {
922  json_object *o = NULL;
923 
924  VuoSceneObject rootSceneObject;
925  if (json_object_object_get_ex(js, "rootSceneObject", &o))
926  rootSceneObject = VuoSceneObject_makeFromJson(o);
927  else
928  rootSceneObject = VuoSceneObject_makeEmpty();
929 
930  VuoInteger pixelsWide = 0;
931  if (json_object_object_get_ex(js, "pixelsWide", &o))
932  pixelsWide = json_object_get_int64(o);
933 
934  VuoInteger pixelsHigh = 0;
935  if (json_object_object_get_ex(js, "pixelsHigh", &o))
936  pixelsHigh = json_object_get_int64(o);
937 
938  float backingScaleFactor = 1;
939  if (json_object_object_get_ex(js, "backingScaleFactor", &o))
940  backingScaleFactor = VuoReal_makeFromJson(o);
941 
942  VuoList_VuoInteraction interactions = NULL;
943  if (json_object_object_get_ex(js, "interactions", &o))
944  interactions = VuoList_VuoInteraction_makeFromJson(o);
945 
946  VuoRenderedLayers rl = VuoRenderedLayers_make(rootSceneObject, pixelsWide, pixelsHigh, backingScaleFactor, interactions);
947 
948  VuoWindowReference window = 0;
949  if (json_object_object_get_ex(js, "window", &o))
950  VuoRenderedLayers_setWindow(&rl, window);
951 
952  return rl;
953 }
954 
960 {
961  json_object *js = json_object_new_object();
962 
963  json_object *rootSceneObjectObject = VuoSceneObject_getJson(value.rootSceneObject);
964  json_object_object_add(js, "rootSceneObject", rootSceneObjectObject);
965 
966  json_object *pixelsWideObject = json_object_new_int64(value.pixelsWide);
967  json_object_object_add(js, "pixelsWide", pixelsWideObject);
968 
969  json_object *pixelsHighObject = json_object_new_int64(value.pixelsHigh);
970  json_object_object_add(js, "pixelsHigh", pixelsHighObject);
971 
972  if (value.hasWindow)
973  {
974  json_object *windowObject = VuoWindowReference_getJson(value.window);
975  json_object_object_add(js, "window", windowObject);
976  }
977 
978  json_object *bsfObject = VuoReal_getJson(value.backingScaleFactor);
979  json_object_object_add(js, "backingScaleFactor", bsfObject);
980 
981  json_object *interactionObj = VuoList_VuoInteraction_getJson(value.interactions);
982  json_object_object_add(js, "interactions", interactionObj);
983 
984  return js;
985 }
986 
992 {
993  char *rootSummary = VuoSceneObject_getSummary(value.rootSceneObject);
994  char *windowSummary = VuoWindowReference_getSummary(value.window);
995 
996  char *summary = VuoText_format("%lux%lu<br>%s<br>%s", value.pixelsWide, value.pixelsHigh, windowSummary, rootSummary);
997 
998  free(windowSummary);
999  free(rootSummary);
1000 
1001  return summary;
1002 }