donjames
Topic Author
Posts: 25
Joined: 27 May 2013, 16:35

Rendering curves

12 Feb 2014, 15:28

Hi

I would like to use the Noesis renderer to render 2D primitives using the C++ API

Previously I was loading Xaml files and setting up the renderer like this:
Ptr<UIElement> xaml = LoadXaml<UIElement>(name.c_str());
Ptr<IRenderer> *xamlRenderer = new Ptr<IRenderer>;            
*xamlRenderer = CreateRenderer(xaml.GetPtr());
(*xamlRenderer)->SetAntialiasingMode(Noesis::Gui::AntialiasingMode_MSAA);
(*xamlRenderer)->SetTessellationQuality(Noesis::Gui::TessellationQuality_High);


But say I wanted to create say to create a path programatically, which is filled with some kind of brush. How would I do that?

Thanks
Don
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Rendering curves

12 Feb 2014, 20:48

First, let me fix your code because you are not using smart pointers correctly. This would be the equivalent to your code:
Ptr<UIElement> xaml = LoadXaml<UIElement>(name.c_str());
Ptr<IRenderer> xamlRenderer = CreateRenderer(xaml.GetPtr());
xamlRenderer->SetAntialiasingMode(Noesis::Gui::AntialiasingMode_MSAA);
xamlRenderer->SetTessellationQuality(Noesis::Gui::TessellationQuality_High);
And now, a sample of code with what you are asking for. Instead of calling LoadXaml() you could do for example:
// Brushes
Ptr<Brush> gray = *new SolidColorBrush(Color::LightGray);
Ptr<Brush> black = *new SolidColorBrush(Color::Black);

Ptr<RadialGradientBrush> radial = *new RadialGradientBrush();
radial->SetGradientOrigin(Point(0.75f, 0.25f));

Ptr<GradientStopCollection> stops = *new GradientStopCollection();

Ptr<GradientStop> stop0 = *new GradientStop();
stop0->SetOffset(0.0f);
stop0->SetColor(Color::Yellow);
stops->Add(stop0.GetPtr());

Ptr<GradientStop> stop1 = *new GradientStop();
stop1->SetOffset(0.5f);
stop1->SetColor(Color::Orange);
stops->Add(stop1.GetPtr());

Ptr<GradientStop> stop2 = *new GradientStop();
stop2->SetOffset(1.0f);
stop2->SetColor(Color::Red);
stops->Add(stop2.GetPtr());

radial->SetGradientStops(stops.GetPtr());

// Geometry
Ptr<StreamGeometry> geometry = *new StreamGeometry();
{
    StreamGeometryContext context = geometry->Open();
    context.BeginFigure(Point(260.0f, 200.0f), true);
    context.ArcTo(Point(140.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
    context.ArcTo(Point(260.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
}

// Path
Ptr<Gui::Path> path = *new Gui::Path();
path->SetFill(radial.GetPtr());
path->SetStroke(black.GetPtr());
path->SetStrokeThickness(4.0f);
path->SetData(geometry.GetPtr());

// Root canvas
Ptr<Canvas> xaml= *new Canvas();
xaml->SetBackground(gray.GetPtr());
xaml->GetChildren()->Add(path.GetPtr());
Then, you create the rendererer with that (this is the same as your code):
Ptr<IRenderer> xamlRenderer = CreateRenderer(xaml.GetPtr());
xamlRenderer->SetAntialiasingMode(Noesis::Gui::AntialiasingMode_MSAA);
xamlRenderer->SetTessellationQuality(Noesis::Gui::TessellationQuality_High);
 
donjames
Topic Author
Posts: 25
Joined: 27 May 2013, 16:35

Re: Rendering curves

13 Feb 2014, 06:52

Thanks

Regarding this piece of code:
Ptr<StreamGeometry> geometry = *new StreamGeometry();
{
    StreamGeometryContext context = geometry->Open();
    context.BeginFigure(Point(260.0f, 200.0f), true);
    context.ArcTo(Point(140.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
    context.ArcTo(Point(260.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
}
Is there a way that I can explicitly call the "Close"?

The reason is that I will create the shape with multiple function calls, each will say add a few points to the path.

So I want to get access to a StreamGeoemetryContext but not close it at the end of the scope it is defined, but in a different scope when I explicitly call it to be closed.

Thanks
Don
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Rendering curves

13 Feb 2014, 11:56

Yes, we will add a Close() method in the next version. Although for now you can emulate it easily by copying the StreamGeometryContext, each time a copy is destroyed, the new geometry is added to the StreamGeometry.
 
donjames
Topic Author
Posts: 25
Joined: 27 May 2013, 16:35

Re: Rendering curves

13 Feb 2014, 21:25

Background: I am implementing a JS HTML5 Canvas bindings in my app which I want to render with Noesis

I have found some issues that I wanted to get your feedback:

1) Is BeginFigure equivalent of moveTo?

i.e. if I had something like this:
cs.beginPath();
cs.moveTo(100,100);
cs.lineTo(200, 200);
cs.moveTo(100,200);
cs.lineTo(100, 200);
cs.endPath();
Would that translate into 1 StreamGeomety that has 2 calls to BeginFigure
Or must I create two StreamGeomety and associated paths?

Some other questions,

2) There isn't also a bezierCurveTo. Is there a way to do that?

3) Also missing is rect, arc or ellipses.
e.g.
cs.beginPath();
cs.moveTo(100,200);
cs.lineTo(100, 200);
cs.ellipse(....);
cs.rect(...)
cs.endPath();
I assume that I can just draw those by adding my own geometry points. But I wanted to double check in case that wasn't a good idea
 
donjames
Topic Author
Posts: 25
Joined: 27 May 2013, 16:35

Re: Rendering curves

14 Feb 2014, 02:12

I am not sure how the stream geometry context really works.

I build the geometry iterativley, and not just in one scope

Your example was this:
void funcComposite( .. geometry ...)
{
    StreamGeometryContext context = geometry->Open();
    context.BeginFigure(Point(260.0f, 200.0f), true);
    context.ArcTo(Point(140.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
    context.ArcTo(Point(260.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
}
Does that produce the same result as this:
void funcPartA( .. geometry ...)
{
    StreamGeometryContext context = geometry->Open();
    context.BeginFigure(Point(260.0f, 200.0f), true);
}
void funcPartB( .. geometry ...)
{
    StreamGeometryContext context = geometry->Open();
   context.ArcTo(Point(140.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
}
void funcPartC( .. geometry ...)
{
    StreamGeometryContext context = geometry->Open();
    context.ArcTo(Point(260.0f, 200.0f), Drawing::Size(60.0f, 60.0f), 0, false, true);
}
Then if I call:
funcPartA(..);
funcPartB(..);
funcPartC(..);
Will that be the same as funcComposite?

Again, I don't have all the information in one scope to add all the geometry, it is done iteratively over several function calls.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Rendering curves

14 Feb 2014, 13:38


1) Is BeginFigure equivalent of moveTo?

i.e. if I had something like this:

cs.beginPath();
cs.moveTo(100,100);
cs.lineTo(200, 200);
cs.moveTo(100,200);
cs.lineTo(100, 200);
cs.endPath();

Would that translate into 1 StreamGeomety that has 2 calls to BeginFigure
Or must I create two StreamGeomety and associated paths?
Yes, BeginFigure is equivalent to a moveTo. You can use several BeginFigures to create paths. But all those path will use the same Fill and Stroke. When you need to apply different brushes you need to create a new StreamGeometry (and corresponding Path object in the tree).

2) There isn't also a bezierCurveTo. Is there a way to do that?
Yes, CubicTo, QuadraticTo, SmoothCubicTo, SmoothQuadraticTo are all of them Beziers with different degrees. The Smooth functions ensure you G1 continuity between segments.
3) Also missing is rect, arc or ellipses.
You also have the ArcTo for arcs. The rest of primitives you are asking for (rect, ellipses, etc) can all be done using this minimalistic commands. The idea of StreamGeometry is not adding redudancy. If you need high-level primitives you can use the different Geometry implementations like Ellipse or Rect.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Rendering curves

14 Feb 2014, 13:44

I am not sure how the stream geometry context really works.
Yes, it works exactly like that. Although I don't know if we should clear the geometry on each Open(). For now, that is not done, I would have to check the behavior of WPF here. Anyway, it would be better if you do a single Open() and then copy the StreamGeometryContext to your different function to continue adding commands to the geometry.

For now, both solutions produce the same result.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Rendering curves

14 Feb 2014, 13:50

Having said that, and sorry for posting several times, I am thinking that probably you could extend noesisGUI creating a new class that derives from UIElement and implement the OnRender method.
void OnRender(DrawingContext* context);
DrawingContext offers a lot more flexibility for drawing:
class DrawingContext
{
    /// Draws an ellipse
    void DrawEllipse(Brush* brush, Pen* pen, const Drawing::Point& center, NsFloat32 radiusX,
        NsFloat32 radiusY);

    /// Draws the specified Geometry using the specified Brush and Pen
    void DrawGeometry(Brush* brush, Pen* pen, Geometry* geometry);

    /// Draws an image into the region defined by the specified Rect
    void DrawImage(ImageSource* imageSource, const Drawing::Rect& rect);

    /// Draws a line with the specified Pen
    void DrawLine(Pen* pen, const Drawing::Point& p0, const Drawing::Point& p1);

    /// Draws a rectangle
    void DrawRectangle(Brush* brush, Pen* pen, const Drawing::Rect& rect);

    /// Draws a rounded rectangle
    void DrawRoundedRectangle(Brush* brush, Pen* pen, const Drawing::Rect& rect, NsFloat32 radiusX,
        NsFloat32 radiusY);

    /// Draws formatted text at the specified location
    void DrawText(FormattedText* formattedText, const Drawing::Rect& bounds);

    /// Pops the last opacity mask, opacity, clip, effect, or transform operation that was pushed
    /// onto the drawing context
    void Pop();

    /// Pushes the specified clip region onto the drawing context
    void PushClip(Geometry* clipGeometry);

    /// Pushes the specified opacity setting onto the drawing context
    void PushOpacity(NsFloat32 opacity);

    /// Pushes the specified opacity mask onto the drawing context
    void PushOpacityMask(Brush* opacityMask);

    /// Pushes the specified Transform onto the drawing context
    void PushTransform(Transform* transform);
};
 
donjames
Topic Author
Posts: 25
Joined: 27 May 2013, 16:35

Re: Rendering curves

14 Feb 2014, 16:09

Yes, BeginFigure is equivalent to a moveTo. You can use several BeginFigures to create paths. But all those path will use the same Fill and Stroke. When you need to apply different brushes you need to create a new StreamGeometry (and corresponding Path object in the tree).

I assume then that means that you should not add the same instance of a path twice to the canvas children.

ie. the following will not work:

.. make m_pPath have certain brushes for fill and stroke
m_pCanvas->GetChildren()->Add(m_pPath);
... make m_pPath have different brushes for fill and stroke
m_pCanvas->GetChildren()->Add(m_pPath);

Is that correct?

If so then I should create two identical paths and set them independently with brushes for fill/stroke.

And does this do a (deep) copy of the path?

m_pPathCopy = *new Gui::Path();
Geometry *pGeom = m_pPath->GetData();
Geometry *pGeomCopy = (Geometry *) pGeom->Clone().GetPtr();
m_pPathCopy->SetData(pGeomCopy);

And does this do a deep copy of a brush:
m_pBrushCopy = *(Brush*) m_pBrush->Clone().GetPtr();
Last edited by donjames on 15 Feb 2014, 15:24, edited 3 times in total.

Who is online

Users browsing this forum: Ahrefs [Bot] and 22 guests