2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
20 #include <dali/dali.h>
21 #include "shared/view.h"
26 using namespace Dali::Toolkit;
31 const Vector4 GRID_BACKGROUND_COLOR(0.85f, 0.85f, 0.85f, 1.0f);
32 const Vector4 CONTROL_POINT1_COLOR(Color::MAGENTA);
33 const Vector4 CONTROL_POINT2_COLOR(0.0, 0.9, 0.9, 1.0);
34 const Vector3 CONTROL_POINT1_ORIGIN(-100, 200, 0);
35 const Vector3 CONTROL_POINT2_ORIGIN(100, -200, 0);
36 const char* const CIRCLE1_IMAGE(DEMO_IMAGE_DIR "circle1.png");
37 const char* const CIRCLE2_IMAGE(DEMO_IMAGE_DIR "circle2.png");
38 const char* const ANIMATION_BACKGROUND(DEMO_IMAGE_DIR "slider-skin.9.png");
39 const char* APPLICATION_TITLE("Bezier curve animation");
40 const float ANIM_LEFT_FACTOR(0.2f);
41 const float ANIM_RIGHT_FACTOR(0.8f);
42 const int AXIS_LABEL_POINT_SIZE(7);
43 const float AXIS_LINE_SIZE(1.0f);
46 const char* CURVE_VERTEX_SHADER = DALI_COMPOSE_SHADER
48 attribute mediump vec2 aPosition;
49 uniform mediump mat4 uMvpMatrix;
53 gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy, 0.0, 1.0);
57 const char* CURVE_FRAGMENT_SHADER = DALI_COMPOSE_SHADER
59 uniform lowp vec4 uColor;
62 gl_FragColor = vec4(0.0,0.0,0.0,1.0);
67 inline float Clamp(float v, float min, float max)
69 if(v < min) return min;
70 if(v > max) return max;
74 struct HandlePositionConstraint
76 HandlePositionConstraint(float minRelX, float maxRelX, float minRelY, float maxRelY)
84 void operator()(Vector3& current, const PropertyInputContainer& inputs)
86 Vector3 size(inputs[0]->GetVector3());
87 current.x = Clamp(current.x, minRelX * size.x, maxRelX * size.x);
88 current.y = Clamp(current.y, minRelY * size.y, maxRelY * size.y);
97 void AnimatingPositionConstraint(Vector3& current, const PropertyInputContainer& inputs)
99 float positionFactor(inputs[0]->GetFloat()); // -1 - 2
100 Vector3 size(inputs[1]->GetVector3());
102 current.x = size.x * (positionFactor - 0.5f); // size * (-1.5 - 1.5)
105 } //unnamed namespace
107 class BezierCurveExample : public ConnectionTracker
110 BezierCurveExample(Application& application)
111 : mApplication(application),
129 mRelativeDragPoint(),
130 mLastControlPointPosition1(),
131 mLastControlPointPosition2(),
132 mPositionFactorIndex(),
134 mControlPoint1Id(0.0f),
135 mControlPoint2Id(0.0f),
136 mControlPointScale(0.5f),
137 mControlPointZoomScale(mControlPointScale * 2.0),
140 // Connect to the Application's Init signal
141 mApplication.InitSignal().Connect(this, &BezierCurveExample::Create);
144 ~BezierCurveExample()
146 // Nothing to do here;
149 // The Init signal is received once (only) during the Application lifetime
150 void Create(Application& application)
152 Window window = mApplication.GetWindow();
153 window.KeyEventSignal().Connect(this, &BezierCurveExample::OnKeyEvent);
155 CreateBackground(window);
157 mControlPointScale = 0.5f;
158 mControlPointZoomScale = mControlPointScale * 2.0f;
160 mContentLayer = Layer::New();
161 mContentLayer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
162 mContentLayer.TouchedSignal().Connect(this, &BezierCurveExample::OnTouchLayer);
163 mContentLayer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
164 window.Add(mContentLayer);
166 // 6 rows: title, grid, coords, play, anim1, anim2
167 TableView contentLayout = TableView::New(5, 1);
168 contentLayout.SetProperty(Dali::Actor::Property::NAME, "contentLayout");
169 contentLayout.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
170 contentLayout.SetCellPadding(Size(30, 30));
171 contentLayout.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
172 contentLayout.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
173 mContentLayer.Add(contentLayout);
175 // Create a TextLabel for the application title.
176 Toolkit::TextLabel label = Toolkit::TextLabel::New(APPLICATION_TITLE);
177 label.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
178 label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
179 label.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::BLACK);
180 contentLayout.Add(label);
181 contentLayout.SetFitHeight(0);
183 mGrid = Control::New();
185 mGrid.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH);
186 mGrid.SetResizePolicy(ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT);
188 mGrid.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
189 mGrid.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
190 mGrid.SetBackgroundColor(GRID_BACKGROUND_COLOR);
192 contentLayout.Add(mGrid);
193 contentLayout.SetCellAlignment(1, HorizontalAlignment::CENTER, VerticalAlignment::CENTER);
195 CreateControlPoints(mGrid); // Control points constrained to double height of grid
196 CreateAxisLabels(mGrid);
198 mCoefficientLabel = TextLabel::New();
199 mCoefficientLabel.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
200 mCoefficientLabel.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
201 mCoefficientLabel.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
202 mCoefficientLabel.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
204 contentLayout.Add(mCoefficientLabel);
205 SetLabel(Vector2(0, 0), Vector2(1, 1));
206 contentLayout.SetCellAlignment(2, HorizontalAlignment::CENTER, VerticalAlignment::CENTER);
207 contentLayout.SetFitHeight(2);
209 // Setup Play button and 2 icons to show off current anim and linear anim
211 PushButton play = PushButton::New();
212 play.SetProperty(Dali::Actor::Property::NAME, "Play");
213 play.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
214 play.SetProperty(Button::Property::LABEL, "Play");
215 play.ClickedSignal().Connect(this, &BezierCurveExample::OnPlayClicked);
217 contentLayout.Add(play);
218 contentLayout.SetCellAlignment(3, HorizontalAlignment::CENTER, VerticalAlignment::CENTER);
219 contentLayout.SetFitHeight(3);
221 auto animContainer = Control::New();
222 animContainer.SetProperty(Dali::Actor::Property::NAME, "AnimationContainer");
223 animContainer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
224 animContainer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
226 auto animRail = Control::New();
227 animRail.SetProperty(Control::Property::BACKGROUND, Property::Map().Add(Visual::Property::TYPE, Visual::IMAGE).Add(ImageVisual::Property::URL, ANIMATION_BACKGROUND));
228 animRail.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
229 animRail.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.666f, 0.2f, 1.0f));
230 animRail.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
231 animContainer.Add(animRail);
233 contentLayout.Add(animContainer);
234 contentLayout.SetFixedHeight(4, 150);
236 mAnimIcon1 = ImageView::New(CIRCLE1_IMAGE);
237 mAnimIcon1.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
238 mAnimIcon1.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
240 // Would like some means of setting and animating position as a percentage of
241 // parent size without using constraints, but this will have to suffice for the moment.
242 mPositionFactorIndex = mAnimIcon1.RegisterProperty("positionFactor", ANIM_LEFT_FACTOR); // range: 0-1 (+/- 1)
243 Constraint constraint = Constraint::New<Vector3>(mAnimIcon1, Actor::Property::POSITION, AnimatingPositionConstraint);
244 constraint.AddSource(Source(mAnimIcon1, mPositionFactorIndex));
245 constraint.AddSource(Source(animContainer, Actor::Property::SIZE));
248 animContainer.Add(mAnimIcon1);
250 // First UpdateCurve needs to run after size negotiation and after images have loaded
251 mGrid.OnRelayoutSignal().Connect(this, &BezierCurveExample::InitialUpdateCurve);
253 auto controlPoint1 = Control::DownCast(mControlPoint1);
256 controlPoint1.ResourceReadySignal().Connect(this, &BezierCurveExample::ControlPointReady);
259 auto controlPoint2 = Control::DownCast(mControlPoint2);
262 controlPoint2.ResourceReadySignal().Connect(this, &BezierCurveExample::ControlPointReady);
266 void ControlPointReady(Control control)
271 void InitialUpdateCurve(Actor actor)
276 void CreateBackground(Window window)
278 Toolkit::Control background = Dali::Toolkit::Control::New();
279 background.SetProperty(Actor::Property::ANCHOR_POINT, Dali::AnchorPoint::CENTER);
280 background.SetProperty(Actor::Property::PARENT_ORIGIN, Dali::ParentOrigin::CENTER);
281 background.SetResizePolicy(Dali::ResizePolicy::FILL_TO_PARENT, Dali::Dimension::ALL_DIMENSIONS);
284 map.Insert(Visual::Property::TYPE, Visual::COLOR);
285 map.Insert(ColorVisual::Property::MIX_COLOR, Vector4(253 / 255.0f, 245 / 255.0f, 230 / 255.0f, 1.0f));
286 background.SetProperty(Dali::Toolkit::Control::Property::BACKGROUND, map);
287 window.Add(background);
290 void CreateCubic(Actor parent)
292 // Create a mesh to draw the cubic as a single line
293 mCurve = Actor::New();
294 mCurve.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
295 mCurve.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
297 Shader shader = Shader::New(CURVE_VERTEX_SHADER, CURVE_FRAGMENT_SHADER);
299 Property::Map curveVertexFormat;
300 curveVertexFormat["aPosition"] = Property::VECTOR2;
301 mCurveVertices = VertexBuffer::New(curveVertexFormat);
302 Vector2 vertexData[2] = {Vector2(-0.5f, 0.5f), Vector2(0.5f, -0.5f)};
303 mCurveVertices.SetData(vertexData, 2);
305 Geometry geometry = Geometry::New();
306 geometry.AddVertexBuffer(mCurveVertices);
307 geometry.SetType(Geometry::LINE_STRIP);
309 Renderer renderer = Renderer::New(geometry, shader);
310 mCurve.AddRenderer(renderer);
314 Actor CreateControlPoint(Actor parent, const char* url, Vector3 position)
316 Actor actor = ImageView::New(url);
317 actor.SetProperty(Actor::Property::SCALE, mControlPointScale);
318 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
319 // Curve and line drawing works off current value (i.e. last update frame's value). Need to animate to ensure
320 // initial position is baked to both frames before initially drawing the curve.
321 auto positionAnimation = Animation::New(0.01f);
322 positionAnimation.AnimateTo(Property(actor, Actor::Property::POSITION), position, AlphaFunction::EASE_IN_OUT);
323 positionAnimation.Play();
324 positionAnimation.FinishedSignal().Connect(this, &BezierCurveExample::OnAnimationFinished);
326 // Set up constraints for drag/drop
327 Constraint constraint = Constraint::New<Vector3>(actor, Actor::Property::POSITION, HandlePositionConstraint(-0.5, 0.5, -1, 1));
328 constraint.AddSource(Source(parent, Actor::Property::SIZE));
331 actor.TouchedSignal().Connect(this, &BezierCurveExample::OnTouchControlPoint);
335 Actor CreateControlLine(VertexBuffer vertexBuffer)
337 Actor line = Actor::New();
338 line.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
339 line.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
341 Shader shader = Shader::New(CURVE_VERTEX_SHADER, CURVE_FRAGMENT_SHADER);
342 Geometry geometry = Geometry::New();
343 geometry.AddVertexBuffer(vertexBuffer);
344 geometry.SetType(Geometry::LINE_STRIP);
346 Renderer renderer = Renderer::New(geometry, shader);
347 line.AddRenderer(renderer);
351 void CreateControlPoints(Actor parent)
353 mControlPoint1 = CreateControlPoint(parent,
355 CONTROL_POINT1_ORIGIN);
356 mControlPoint1Id = mControlPoint1.GetProperty<int>(Actor::Property::ID);
358 mControlPoint2 = CreateControlPoint(parent,
360 CONTROL_POINT2_ORIGIN);
361 mControlPoint2Id = mControlPoint2.GetProperty<int>(Actor::Property::ID);
363 Property::Map lineVertexFormat;
364 lineVertexFormat["aPosition"] = Property::VECTOR2;
365 mLine1Vertices = VertexBuffer::New(lineVertexFormat);
366 mLine2Vertices = VertexBuffer::New(lineVertexFormat);
368 mControlLine1 = CreateControlLine(mLine1Vertices);
369 mControlLine2 = CreateControlLine(mLine2Vertices);
371 parent.Add(mControlLine1);
372 parent.Add(mControlLine2);
373 parent.Add(mControlPoint1);
374 parent.Add(mControlPoint2);
377 void CreateAxisLabels(Actor parent)
379 TextLabel progressionLabel = TextLabel::New("Progression");
380 progressionLabel.SetProperty(TextLabel::Property::POINT_SIZE, AXIS_LABEL_POINT_SIZE);
381 progressionLabel.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(-90.0f)), Vector3::ZAXIS));
382 progressionLabel.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
383 progressionLabel.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT);
384 CreateLine(progressionLabel, ParentOrigin::BOTTOM_LEFT);
386 TextLabel timeLabel = TextLabel::New("Time");
387 timeLabel.SetProperty(TextLabel::Property::POINT_SIZE, AXIS_LABEL_POINT_SIZE);
388 timeLabel.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
389 timeLabel.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT);
390 CreateLine(timeLabel, ParentOrigin::TOP_LEFT);
392 parent.Add(progressionLabel);
393 parent.Add(timeLabel);
396 void CreateLine(Actor parent, const Vector3& parentOrigin)
398 Control control = Control::New();
399 control.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
400 control.SetProperty(Actor::Property::PARENT_ORIGIN, parentOrigin);
401 control.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
402 control.SetProperty(Actor::Property::SIZE_HEIGHT, AXIS_LINE_SIZE);
403 control.SetBackgroundColor(Color::BLACK);
407 void SetLabel(Vector2 pos1, Vector2 pos2)
409 std::ostringstream oss;
410 oss.setf(std::ios::fixed, std::ios::floatfield);
412 oss << "( <color value='#971586'>" << pos1.x << ", " << pos1.y << ", </color>";
413 oss << "<color value='#e7640d'>" << pos2.x << ", " << pos2.y << "</color>";
414 oss << "<color value='black'> )</color>";
416 mCoefficientLabel.SetProperty(TextLabel::Property::TEXT, oss.str());
419 Vector2 AlignToGrid(Vector3 actorPos, Vector3 gridSize)
421 actorPos /= gridSize; // => -0.5 - 0.5
422 actorPos.x = Clamp(actorPos.x, -0.5f, 0.5f);
423 return Vector2(actorPos.x + 0.5f, 0.5f - actorPos.y);
426 void GetControlPoints(Vector2& pt1, Vector2& pt2)
428 Vector3 gridSize = mGrid.GetProperty<Vector3>(Actor::Property::SIZE); // Get target value
430 pt1 = AlignToGrid(mControlPoint1.GetCurrentProperty<Vector3>(Actor::Property::POSITION), gridSize);
431 pt2 = AlignToGrid(mControlPoint2.GetCurrentProperty<Vector3>(Actor::Property::POSITION), gridSize);
435 * @param[in] actor The actor to get the position from
436 * @param[out] point The point in the grid in the range -0.5 -> 0.5 in x and y, with y up.
437 * @param[out] position The actor position, floored to the nearest pixel
439 void GetPoint(Actor actor, Vector2& point, Vector2& position)
441 auto gridSize = mGrid.GetProperty<Vector3>(Actor::Property::SIZE); // Get target value
442 auto currentPosition = actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION); // Get constrained current value
444 position = Vector2(floor(currentPosition.x), floor(currentPosition.y));
446 point.x = Clamp(position.x / gridSize.x, -0.5f, 0.5f) + 0.5f;
447 point.y = 0.5f - position.y / gridSize.y;
452 Vector2 point1, point2;
453 Vector2 position1, position2;
454 const int NUMBER_OF_SEGMENTS(40);
456 GetPoint(mControlPoint1, point1, position1);
457 GetPoint(mControlPoint2, point2, position2);
459 if(position1 != mLastControlPointPosition1 ||
460 position2 != mLastControlPointPosition2)
462 mLastControlPointPosition1 = position1;
463 mLastControlPointPosition2 = position2;
465 SetLabel(point1, point2);
467 Path path = Path::New();
468 path.AddPoint(Vector3::ZERO);
469 path.AddPoint(Vector3(1.0f, 1.0f, 1.0f));
470 path.AddControlPoint(Vector3(point1.x, point1.y, 0));
471 path.AddControlPoint(Vector3(point2.x, point2.y, 0));
473 Dali::Vector<float> verts;
475 verts.Resize(2 * (NUMBER_OF_SEGMENTS + 1)); // 1 more point than segment
476 for(int i = 0; i <= NUMBER_OF_SEGMENTS; ++i)
478 Vector3 position, tangent;
479 path.Sample(i / float(NUMBER_OF_SEGMENTS), position, tangent);
480 verts[i * 2] = position.x - 0.5;
481 verts[i * 2 + 1] = 0.5 - position.y;
483 mCurveVertices.SetData(&verts[0], NUMBER_OF_SEGMENTS + 1);
485 Vector4 line1(-0.5f, 0.5f, point1.x - 0.5f, 0.5f - point1.y);
486 mLine1Vertices.SetData(line1.AsFloat(), 2);
488 Vector4 line2(0.5f, -0.5f, point2.x - 0.5f, 0.5f - point2.y);
489 mLine2Vertices.SetData(line2.AsFloat(), 2);
493 bool OnTouchControlPoint(Actor controlPoint, const TouchEvent& event)
495 if(event.GetPointCount() > 0)
497 if(event.GetState(0) == PointState::DOWN)
499 Vector2 screenPoint = event.GetScreenPosition(0);
500 mRelativeDragPoint = screenPoint;
501 mRelativeDragPoint -= Vector2(controlPoint.GetCurrentProperty<Vector3>(Actor::Property::POSITION));
502 mDragActor = controlPoint;
503 mDragAnimation = Animation::New(0.25f);
504 mDragAnimation.AnimateTo(Property(mDragActor, Actor::Property::SCALE), Vector3(mControlPointZoomScale, mControlPointZoomScale, 1.0f), AlphaFunction::EASE_OUT);
505 mDragAnimation.Play();
508 return false; // Don't mark this as consumed - let the layer get the touch
511 bool OnTouchLayer(Actor actor, const TouchEvent& event)
513 if(event.GetPointCount() > 0)
517 Vector3 position(event.GetScreenPosition(0));
519 mDragActor.SetProperty(Actor::Property::POSITION, position - Vector3(mRelativeDragPoint));
521 if(event.GetState(0) == PointState::UP) // Stop dragging
523 mDragAnimation = Animation::New(0.25f);
524 mDragAnimation.AnimateTo(Property(mDragActor, Actor::Property::SCALE), Vector3(mControlPointScale, mControlPointScale, 1.0f), AlphaFunction::EASE_IN);
525 mDragAnimation.FinishedSignal().Connect(this, &BezierCurveExample::OnAnimationFinished);
526 mDragAnimation.Play();
535 void OnAnimationFinished(Animation& animation)
540 bool OnPlayClicked(Button button)
542 if(!mBezierAnimation)
544 mBezierAnimation = Animation::New(mDuration);
546 mBezierAnimation.Stop();
547 mBezierAnimation.Clear();
549 float positionFactor = ANIM_LEFT_FACTOR;
552 positionFactor = ANIM_RIGHT_FACTOR;
561 GetControlPoints(pt1, pt2);
563 mBezierAnimation.AnimateTo(Property(mAnimIcon1, mPositionFactorIndex), positionFactor, AlphaFunction(pt1, pt2));
564 mBezierAnimation.Play();
569 * Main key event handler
571 void OnKeyEvent(const KeyEvent& event)
573 if(event.GetState() == KeyEvent::DOWN && (IsKey(event, DALI_KEY_ESCAPE) || IsKey(event, DALI_KEY_BACK)))
580 Application& mApplication;
581 Actor mControlPoint1;
582 Actor mControlPoint2;
585 ImageView mAnimIcon1;
586 ImageView mAnimIcon2;
589 TextLabel mCoefficientLabel;
593 Animation mDragAnimation;
594 Animation mBezierAnimation;
595 VertexBuffer mCurveVertices;
596 VertexBuffer mLine1Vertices;
597 VertexBuffer mLine2Vertices;
598 Vector2 mRelativeDragPoint;
599 Vector2 mLastControlPointPosition1;
600 Vector2 mLastControlPointPosition2;
601 Property::Index mPositionFactorIndex;
603 unsigned int mControlPoint1Id;
604 unsigned int mControlPoint2Id;
605 float mControlPointScale;
606 float mControlPointZoomScale;
610 int DALI_EXPORT_API main(int argc, char** argv)
612 Application application = Application::New(&argc, &argv);
614 BezierCurveExample test(application);
615 application.MainLoop();