575 views
in Embedded Wizard Studio by

Hi all,

I have some questions about stroke path,

I stored my triangles in one Path object , so each triangle is an subpath

Now, I want to rotate the triangles.

1. Each triangle rotates around itself, just like the triangle in the middle of picture below 

2. How can I control the triangle independently?

    For example, subpath0 rotate( 60 degree ) , subpath1 rotate( 30 degree ) , subpath2 scale( 1.5,1.5 )

Thanks!!

 

Best Regards,

Andy Dong

 

1 Answer

0 votes
by

Hello Andy,

the typical approach: with the method Rotate() you can determine the rotation angle before adding the triangle to the path. To rotate the triangle around its center you will also need to use the method Translate() to move the origin of the coordinate system to the center of the triangle. Please see the documentation Apply 2D transformations during the path creation. Please note, the both methods PushMatrix() and PopMatrix() are convenient to store the transformation matrix before you apply a rotation and the to restore it again. It is important if you adds several triangles each with different rotation angle. For example:

Path.SetMaxNoOfSubPaths( 3 );

// TRIANGLE #1 at position 150,100 and -15°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 0, 3 );
Path.PushMatrix();

// Move to the center of the first triangle
Path.Translate( 150, 100 );
Path.Rotate( -15.0 );

// Add an equilateral triangle with 100 pixel long edge.
Path.Begin  ( 0, -50.0, 43.3 );
Path.AddLine( 0, 0.0, -43.3 );
Path.AddLine( 0, 50.0, 43.3 );
Path.Close( 0 );

// Restore the transformation matrix.
Path.PopMatrix();


// TRIANGLE #2 at position 250,175 and 45°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 1, 3 );
Path.PushMatrix();

// Move to the center of the first triangle
Path.Translate( 250, 175 );
Path.Rotate( 45.0 );

// Add an equilateral triangle with 100 pixel long edge.
Path.Begin  ( 1, -50.0, 43.3 );
Path.AddLine( 1, 0.0, -43.3 );
Path.AddLine( 1, 50.0, 43.3 );
Path.Close( 1 );

// Restore the transformation matrix.
Path.PopMatrix();

// TRIANGLE #3 at position 50,250 and 100°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 2, 3 );
Path.PushMatrix();

// Move to the center of the first triangle
Path.Translate( 50, 250 );
Path.Rotate( 100.0 );

// Add an equilateral triangle with 100 pixel long edge.
Path.Begin  ( 2, -50.0, 43.3 );
Path.AddLine( 2, 0.0, -43.3 );
Path.AddLine( 2, 50.0, 43.3 );
Path.Close( 2 );

// Restore the transformation matrix.
Path.PopMatrix();

In this example you see, that for every triangle we add exact he same coordinate 3-coordinates (-50,43.3, 0.0, -43.3, 50.0, 43.3). That the resulting triangles appear rotated and correctly positioned is determined by the preceding Rotate() and Translate() method invocation. To avoid, that rotation/position of triangle #1 affect the triangle #2 we use PushMatrix() and PopMatrix() invocation. They save the intermediate state of the matrix.

With the above example you create every triangle separately from three line segments. You can simplify this by using a  temporary sub-path. In other words, you store the basic form of the triangle in a first sub-path and then you copy this basic triangle by applying the right transformation:

Path.SetMaxNoOfSubPaths( 4 );

// The sub-path #0 stores the basic form of a triangle with its default size and orientation 0°
Path.InitSubPath( 0, 3 );
Path.Begin  ( 0, -50.0, 43.3 );
Path.AddLine( 0, 0.0, -43.3 );
Path.AddLine( 0, 50.0, 43.3 );
Path.Close( 0 );

// TRIANGLE #1 creates a copy of the previously prepare triangle at position 150,100 and -15°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 1, 3 );
Path.PushMatrix();
Path.Translate( 150, 100 );
Path.Rotate( -15.0 );

// Add a copy of the previously prepared sub-path
Path.AddCopy( 1, Path, 0, 0, 3 );
Path.Close( 1 );

// Restore the transformation matrix.
Path.PopMatrix();


// TRIANGLE #2 at position 250,175 and 45°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 2, 3 );
Path.PushMatrix();
Path.Translate( 250, 175 );
Path.Rotate( 45.0 );

// Add a copy of the previously prepared sub-path
Path.AddCopy( 2, Path, 0, 0, 3 );
Path.Close( 2 );

// Restore the transformation matrix.
Path.PopMatrix();

// TRIANGLE #3 at position 50,250 and 100°

// Initialize the Sub-Path for the first triangle
Path.InitSubPath( 3, 3 );
Path.PushMatrix();
Path.Translate( 50, 250 );
Path.Rotate( 100.0 );

// Add a copy of the previously prepared sub-path
Path.AddCopy( 3, Path, 0, 0, 3 );
Path.Close( 3 );

// Restore the transformation matrix.
Path.PopMatrix();

// The basic form of the triangle is not needed anymore.
Path.InitSubPath( 0, 0 );

The both above approaches create/initialize new paths. They are thus ideal if you receive new data for all the triangles and you re-create all triangles. If the data changes for few of the triangles, then you can re-initialize the corresponding sub-path only. All other sub-paths will retain their contents. In this case, however, you should prepare the path to have enough capacity for the maximum number of triangles. For example, if your aplication will show up to 100 triangles:

// Initialize the path to be able to store up to 100 triangles
Path.SetMaxNoOfSubPaths( 100 );

Later when you receive new data for triangle number e.g. 17, you just reinitialize this sub-path:

// Initialize the Sub-Path for the updated triangle
Path.InitSubPath( 17, 3 );
Path.PushMatrix();
Path.Translate( ... );
[...]

Completely another approach is to access and modify the coordinates of the respectove nodes. Please see Evaluate and modify the coordinates stored in the Path Data object. With this approach you can take care of the rotation and translation of the triangle coordinates. You just assign the resulting coordinates to the path. Please note, to do this the respective sub-path has to contain the edges already - it has to be initialized and loaded with some data. Otherwise you can't access the coordfinates.

I hope it helps you further.

Best regards

Paul Banach

by
Hi Paul Banach,

 

Thanks for your quick reply!

It really helps me a lot.

I will try it. Thanks!!

 

Best Regards,

Andy Dong
by

Hi Paul Banach,

I had already tried it and there comes up a new question.

For example, I have 100 subpath ( triangles ) and one of position is (100,150)

I have a cursor when I move the cursor to (100,150) and then I can select this subpath

I know subpath is not an object so I can't use method Core::View FindViewAtPosition.

Is there a way I can implement this function ?

Thanks!

 

Best Regards,

Andy Dong

by

Hello Andy,

knowing the coordinates of each triangle you can implement code to find the triangle at the position X, Y. It expects a little bit arithmetic. Assuming you evaluate the coordinates stored in the Path Data object only, then  following is a loop to search for the triangle enclosing the touched point:

var float x     = SimpleTouchHandler.CurrentPos.x;
var float y     = SimpleTouchHandler.CurrentPos.y;
var int32 count = Path.GetMaxNoOfSubPaths();

// Evaluate all sub-paths -> each containing a triangle
for ( count = count - 1; count >= 0; count = count - 1 )
{
  // Get the coordinates of the triangle
  var float x0 = Path.GetNodeX( count, 0 );
  var float y0 = Path.GetNodeY( count, 0 );
  var float x1 = Path.GetNodeX( count, 1 );
  var float y1 = Path.GetNodeY( count, 1 );
  var float x2 = Path.GetNodeX( count, 2 );
  var float y2 = Path.GetNodeY( count, 2 );

  // Does x, y lie within the triangle?
  var float dX   = x  - x2;
  var float dY   = y  - y2;
  var float dX21 = x2 - x1;
  var float dY12 = y1 - y2;
  var float D    = dY12 * ( x0 - x2 ) + dX21 * ( y0 - y2 );
  var float s    = dY12 * dX + dX21 * dY;
  var float t    = ( y2 - y0 ) * dX + ( x0 - x2 ) * dY;

  if (( D < 0.0 ) && ( s <= 0.0 ) && ( t <= 0.0 ) && (( s + t ) >= D ))
  {
    trace "Found triangle number ", count;
    return;
  }

  if (( D >= 0.0 ) && ( s >= 0.0 ) && ( t >= 0.0 ) && (( s + t ) <= D ))
  {
    trace "Found triangle number ", count;
    return;
  }
}

trace "NOT FOUND!";

If you want one of the triangles to appear selected (e.g. with different color, stroke thickness, etc.), then use a separate Path Data object and Stroked Path view for this single triangle only.

Best regards

Paul Banach

by
Hi Paul Banach,

Thank you so much

I will try it.

 

Best Regards,

Andy Dong

Ask Embedded Wizard

Welcome to the question and answer site for Embedded Wizard users and UI developers.

Ask your question and receive answers from the Embedded Wizard support team or from other members of the community!

Embedded Wizard Website | Privacy Policy | Imprint

...