/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ #include "../opennurbs_public_examples.h" #include "../example_userdata/example_ud.h" #define INTERNAL_INITIALIZE_MODEL(model) Internal_SetExampleModelProperties(model,OPENNURBS__FUNCTION__,__FILE__) static void Internal_SetExampleModelProperties( ONX_Model& model, const char* function_name, const char* source_file_name ) { const bool bHaveFunctionName = (nullptr != function_name && 0 != function_name[0]); if ( !bHaveFunctionName ) function_name = ""; const bool bHaveFileName = (nullptr != source_file_name && 0 != source_file_name[0]); if (!bHaveFileName) source_file_name = ""; model.m_sStartSectionComments = "This was file created by OpenNURBS toolkit example code."; // set application information const ON_wString wide_function_name(function_name); const ON_wString wide_source_file_name(source_file_name); model.m_properties.m_Application.m_application_name = bHaveFunctionName ? ON_wString::FormatToString(L"OpenNURBS toolkit Example: %ls() function", static_cast(wide_function_name)) : ON_wString(L"OpenNURBS Examples"); model.m_properties.m_Application.m_application_URL = L"http://www.opennurbs.org"; model.m_properties.m_Application.m_application_details = bHaveFileName ? ON_wString::FormatToString(L"Opennurbs examples are in the file %ls.", static_cast(wide_source_file_name)) : ON_wString::FormatToString(L"Opennurbs examples are example_*.cpp files."); // some notes if (bHaveFunctionName && bHaveFileName) { model.m_properties.m_Notes.m_notes = ON_wString::FormatToString( L"This .3dm file was made with the OpenNURBS toolkit example function %s() defined in source code file %ls.", static_cast(wide_function_name), static_cast(wide_source_file_name)); model.m_properties.m_Notes.m_bVisible = model.m_properties.m_Notes.m_notes.IsNotEmpty(); } // set revision history information model.m_properties.m_RevisionHistory.NewRevision(); } static bool Internal_WriteExampleModel( const ONX_Model& model, const wchar_t* filename, ON_TextLog& error_log ) { int version = 0; // writes model to archive return model.Write( filename, version, &error_log ); } ON_3dmObjectAttributes* Internal_CreateManagedAttributes( int layer_index, const wchar_t* name ) { ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); attributes->m_layer_index = layer_index; attributes->m_name = name; return attributes; } static bool write_points_example( const wchar_t* filename, ON_TextLog& error_log ) { // example demonstrates how to write a singe points and point clouds ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); // file settings (units, tolerances, views, ...) // OPTIONAL - change values from defaults model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Meters; model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.01; model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% // layer table // define some layers model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); const int point1_layer_index = model.AddLayer(L"my layer",ON_Color::Black); const int pointcloud_layer_index = model.AddLayer(L"red points",ON_Color::SaturatedRed); const int point2_layer_index = model.AddLayer(L"one blue point",ON_Color::SaturatedBlue); // we'll put 2 red and one blue point in a group ON_Group group; group.SetName(L"group of points"); group.SetIndex(0); model.AddModelComponent(group, true); // single point at (1,4,5) on default layer ON_Point* point1 = new ON_Point(ON_3dPoint( 1.0, 4.0, 5.0 )); point1->AttachUserData( new CExampleWriteUserData("write_points_example()-point1") ); model.AddManagedModelGeometryComponent( point1, Internal_CreateManagedAttributes(point1_layer_index,L"first point") ); // point "cloud" with 3 points on red point cloud layer ON_PointCloud* pointcloud = new ON_PointCloud(); pointcloud->AppendPoint(ON_3dPoint( 1.0, 6.0, 5.0 )); pointcloud->AppendPoint(ON_3dPoint( 1.5, 4.5, 6.0 )); pointcloud->AppendPoint(ON_3dPoint( 2.0, 5.0, 7.0 )); pointcloud->AttachUserData( new CExampleWriteUserData("write_points_example()-pointcloud") ); ON_3dmObjectAttributes* pointcloud_attributes = Internal_CreateManagedAttributes(pointcloud_layer_index, L"3 points"); pointcloud_attributes->AddToGroup(group.Index()); model.AddManagedModelGeometryComponent( pointcloud, pointcloud_attributes ); // single point at (3,2,4) on red point layer and in group with the pointcloud ON_Point* point2 = new ON_Point(ON_3dPoint( 3.0, 2.0, 4.0 )); ON_3dmObjectAttributes* point2_attributes = Internal_CreateManagedAttributes(point2_layer_index, L"last point"); point2_attributes->AddToGroup(group.Index()); point2->AttachUserData( new CExampleWriteUserData("write_points_example()-point2") ); model.AddManagedModelGeometryComponent( point2, point2_attributes); return Internal_WriteExampleModel(model, filename, error_log); } static bool write_curves_example( const wchar_t* filename, ON_TextLog& error_log ) { // example demonstrates how to write a NURBS curve, line, and circle ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); // file settings (units, tolerances, views, ...) model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Inches; model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.001; model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% // add some layers model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); const int line_layer_index = model.AddLayer(L"line layer",ON_Color::Black); const int wiggle_layer_index = model.AddLayer(L"green NURBS wiggle",ON_Color::SaturatedGreen); const int circles_layer_index = model.AddLayer(L"blue circles",ON_Color::SaturatedBlue); { // add a line ON_Object* managed_line = new ON_LineCurve( ON_Line( ON_3dPoint(1.0,2.0,-1.5), ON_3dPoint(5.0,3.0,2.0) ) ); model.AddManagedModelGeometryComponent( managed_line, Internal_CreateManagedAttributes(line_layer_index, L"straight line curve") ); } { // add a wiggly cubic curve ON_NurbsCurve* wiggle = new ON_NurbsCurve( 3, // dimension false, // true if rational 4, // order = degree+1 6 // number of control vertices ); int i; for ( i = 0; i < wiggle->CVCount(); i++ ) { ON_3dPoint pt( 2*i, -i, (i-3)*(i-3) ); // pt = some 3d point wiggle->SetCV( i, pt ); } // ON_NurbsCurve's have order+cv_count-2 knots. wiggle->SetKnot(0, 0.0); wiggle->SetKnot(1, 0.0); wiggle->SetKnot(2, 0.0); wiggle->SetKnot(3, 1.5); wiggle->SetKnot(4, 2.3); wiggle->SetKnot(5, 4.0); wiggle->SetKnot(6, 4.0); wiggle->SetKnot(7, 4.0); model.AddManagedModelGeometryComponent( wiggle, Internal_CreateManagedAttributes(wiggle_layer_index, L"wiggly cubic curve") ); } { // add two circles ON_ArcCurve* circle1 = new ON_ArcCurve( ON_Circle( ON_3dPoint(1.0,2.0,-1.5), 3.0 ) ); model.AddManagedModelGeometryComponent( circle1, Internal_CreateManagedAttributes(circles_layer_index, L"radius 3 circle") ); ON_ArcCurve* circle2 = new ON_ArcCurve( ON_Circle( ON_3dPoint(1.0,2.0,-1.5), 5.0 ) ); model.AddManagedModelGeometryComponent( circle2, Internal_CreateManagedAttributes(circles_layer_index, L"radius 5 circle") ); } return Internal_WriteExampleModel(model, filename, error_log); } static bool write_surfaces_example( const wchar_t* filename, ON_TextLog& error_log ) { // example demonstrates how to write a NURBS surface ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // The code between the comment bands has nothing to do with I/O. // It is simply an easy way to get a NURBS surface to write. const int bIsRational = false; const int dim = 3; const int u_degree = 2; const int v_degree = 3; const int u_cv_count = 3; const int v_cv_count = 5; // The knot vectors do NOT have the 2 superfluous knots // at the start and end of the knot vector. If you are // coming from a system that has the 2 superfluous knots, // just ignore them when writing a 3dm file. double u_knot[ u_cv_count + u_degree - 1 ]; double v_knot[ v_cv_count + v_degree - 1 ]; // make up a quadratic knot vector with no interior knots u_knot[0] = u_knot[1] = 0.0; u_knot[2] = u_knot[3] = 1.0; // make up a cubic knot vector with one simple interior knot v_knot[0] = v_knot[1] = v_knot[2] = 0.0; v_knot[3] = 1.5; v_knot[4] = v_knot[5] = v_knot[6] = 2.0; // Rational control points can be in either homogeneous // or euclidean form. Non-rational control points do not // need to specify a weight. ON_3dPoint CV[u_cv_count][v_cv_count]; int i, j; for ( i = 0; i < u_cv_count; i++ ) { for ( j = 0; j < v_cv_count; j++ ) { CV[i][j].x = i; CV[i][j].y = j; CV[i][j].z = i-j; } } // write a line on the default layer ON_NurbsSurface nurbs_surface( dim, bIsRational, u_degree+1, v_degree+1, u_cv_count, v_cv_count ); for ( i = 0; i < nurbs_surface.KnotCount(0); i++ ) nurbs_surface.SetKnot( 0, i, u_knot[i] ); for ( j = 0; j < nurbs_surface.KnotCount(1); j++ ) nurbs_surface.SetKnot( 1, j, v_knot[j] ); for ( i = 0; i < nurbs_surface.CVCount(0); i++ ) { for ( j = 0; j < nurbs_surface.CVCount(1); j++ ) { nurbs_surface.SetCV( i, j, CV[i][j] ); } } model.AddModelGeometryComponent(&nurbs_surface, nullptr); // model.AddDefaultLayer(L"NURBS surface", ON_Color::UnsetColor); return Internal_WriteExampleModel(model, filename, error_log); } static bool write_mesh_example( const wchar_t* filename, ON_TextLog& error_log ) { // example demonstrates how to create and write a mesh ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); model.AddDefaultLayer(L"mesh", ON_Color::Black); // create a mesh to write // The mesh is a pyramid with 4 triangular sides and a quadranglar // base. The mesh has 5 vertices and 5 faces. // The side faces share normals at their common vertices. The // quadrangular base has normals different from the side normal. // Coincident vertices that have distinct normals must be // duplicated in the vertex list. // // The apex will be at (1,1.5,4) with normal (0,0,1). // The base corners will be at (0,0,0), (0,2,0), (2,3,0), (0,3,0). bool bHasVertexNormals = true; // we will specify vertex normals bool bHasTexCoords = false; // we will not specify texture coordinates const int vertex_count = 5+4; // 4 duplicates for different base normals const int face_count = 5; // 4 triangle sides and a quad base ON_Mesh mesh( face_count, vertex_count, bHasVertexNormals, bHasTexCoords); // The SetVertex(), SetNormal(), SetTCoord() and SetFace() functions // return true if successful and false if input is illegal. It is // a good idea to inspect this returned value. // vertex #0: apex location and normal mesh.SetVertex( 0, ON_3dPoint(1.0, 1.5, 5.0) ); mesh.SetVertexNormal( 0, ON_3dVector(0.0, 0.0, 1.0) ); // vertex #1: SW corner vertex for sides mesh.SetVertex( 1, ON_3dPoint(0.0, 0.0, 0.0) ); mesh.SetVertexNormal( 1, ON_3dVector(-1.0, -1.0, 0.0) ); // set normal will unitize if needed // vertex #2: SE corner vertex for sides mesh.SetVertex( 2, ON_3dPoint(2.0, 0.0, 0.0) ); mesh.SetVertexNormal( 2, ON_3dVector(+1.0, -1.0, 0.0) ); // vertex #3: NE corner vertex for sides mesh.SetVertex( 3, ON_3dPoint(2.0, 3.0, 0.0) ); mesh.SetVertexNormal( 3, ON_3dVector(+1.0, +1.0, 0.0) ); // vertex #4: NW corner vertex for sides mesh.SetVertex( 4, ON_3dPoint(0.0, 3.0, 0.0) ); mesh.SetVertexNormal( 4, ON_3dVector(-1.0, +1.0, 0.0) ); // vertex #5: SW corner vertex for base mesh.SetVertex( 5, ON_3dPoint(0.0, 0.0, 0.0) ); // == location of v1 mesh.SetVertexNormal( 5, ON_3dVector(0.0, 0.0, -1.0) ); // vertex #6: SE corner vertex for base mesh.SetVertex( 6, ON_3dPoint(2.0, 0.0, 0.0) ); // == location of v2 mesh.SetVertexNormal( 6, ON_3dVector(0.0, 0.0, -1.0) ); // vertex #7: SW corner vertex for base mesh.SetVertex( 7, ON_3dPoint(2.0, 3.0, 0.0) ); // == location of v3 mesh.SetVertexNormal( 7, ON_3dVector(0.0, 0.0, -1.0) ); // vertex #8: SW corner vertex for base mesh.SetVertex( 8, ON_3dPoint(0.0, 3.0, 0.0) ); // == location of v4 mesh.SetVertexNormal( 8, ON_3dVector(0.0, 0.0, -1.0) ); // faces have vertices ordered counter-clockwise // South side triangle mesh.SetTriangle( 0, 1, 2, 0 ); // East side triangle mesh.SetTriangle( 1, 2, 3, 0 ); // North side triangle mesh.SetTriangle( 2, 3, 4, 0 ); // West side triangle mesh.SetTriangle( 3, 4, 1, 0 ); // last face is quadrangular base mesh.SetQuad( 4, 5, 8, 7, 6 ); if ( !mesh.HasVertexNormals() ) mesh.ComputeVertexNormals(); ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // Avoid copying the mesh - useful technique for large objects model.AddModelGeometryComponentForExperts(false, &mesh, false, nullptr, true); return Internal_WriteExampleModel(model, filename, error_log); } static bool write_mesh_with_material_example( const wchar_t* filename, ON_TextLog& error_log ) { // example demonstrates how to create and write a mesh ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); model.AddDefaultLayer(L"mesh", ON_Color::Black); const bool bResolveIdAndNameConflicts = true; // example demonstrates how to create and write a mesh that uses // a rendering material. You may want to study write_mesh_example() // before examining this function. // // The key to attaching a texture is to create a mesh // with texture coordinates, create a material using the // texture as a bitmap, and to attach the material to the // mesh before writing the mesh. // rendering material with a texture map. ON_Material material; material.SetIndex(0); material.SetAmbient( ON_Color( 40, 40, 40 ) ); material.SetDiffuse( ON_Color( 220, 220, 220 ) ); material.SetEmission( ON_Color::Black ); material.SetSpecular( ON_Color( 180, 180, 180 ) ); material.SetShine( 0.35*ON_Material::MaxShine ); // 0 = flat // MaxShine() = shiney material.SetTransparency( 0.2 ); // 0 = opaque, 1 = transparent // Texture and bump bitmaps can be Windows bitmap (.BMP), Targa (.TGA), // JPEG (.JPG), PCX or PNG files. Version 1 of Rhino will not support // filenames using unicode or multibyte character sets. As soon as // Rhino supports these character sets, the const char* filename will // changed to const _TCHAR*. // For Rhino to find the texture bitmap, the .3dm file and the // .bmp file need to be in the same directory. ON_Texture texture; texture.m_image_file_reference.SetRelativePath(L"./example_texture.bmp"); material.AddTexture( texture ); // The render material name is a string used to identify rendering // materials in RIB, POV, OBJ, ..., files. In Rhino, the render // material name is set with the SetObjectMaterial command and can // be viewed in the Info tab of the dialog displayed by the // Properties command. material.SetName( L"my render material" ); model.AddModelComponent(material, bResolveIdAndNameConflicts); bool bHasVertexNormals = false; // we will NOT specify vertex normals bool bHasTexCoords = true; // we will specify texture coordinates const int vertex_count = 40; const int face_count = 28; ON_Mesh mesh( face_count, vertex_count, bHasVertexNormals, bHasTexCoords); // The SetVertex(), SetNormal(), SetTextureCoord() and SetQuad() functions // return true if successful and false if input is illegal. It is // a good idea to inspect this returned value. // cook up a 5 x 8 grid of vertices int vertex_index = 0; int i, j; for ( i = 0; i <= 4; i++ ) { for ( j = 0; j <= 7; j++ ) { ON_3fPoint v( (float)i, (float)j, (float)(sin( 2.0*3.14159*j/7.0 ) + cos( 3.14159*i/4.0 )) ); mesh.SetVertex( vertex_index, v ); // 3d location // normalized texture coordinate double tcoord_u = i/4.0; double tcoord_v = j/7.0; mesh.SetTextureCoord( vertex_index, tcoord_u, tcoord_v ); // 2d texture coordinate vertex_index++; } } // faces have vertices ordered counter-clockwise // cook up a 4 x 7 grid of quadrangular faces int face_index = 0; for ( i = 0; i < 4; i++ ) { for ( j = 0; j < 7; j++ ) { int vi[4]; // indices of corner vertices vi[0] = i*8 + j; // vertex at "lower left" corner vi[1] = vi[0]+8; // vertex at "lower right" corner vi[2] = vi[1]+1; // vertex at "upper left" corner vi[3] = vi[0]+1; // vertex at "upper right" corner mesh.SetQuad( face_index, vi[0], vi[1], vi[2], vi[3] ); face_index++; } } // Most applications expect vertex normals. // If they are not present, ComputeVertexNormals sets // them by averaging face normals. if ( !mesh.HasVertexNormals() ) mesh.ComputeVertexNormals(); ON_3dmObjectAttributes attributes; attributes.m_name = "my mesh with material"; attributes.m_material_index = 0; attributes.SetMaterialSource( ON::material_from_object ); const bool bManagedGeometry = false; // mesh not copied const bool bManagedAttributes = false; // attributes not copied model.AddModelGeometryComponentForExperts(bManagedGeometry, &mesh, bManagedAttributes, &attributes, bResolveIdAndNameConflicts); return Internal_WriteExampleModel(model, filename, error_log); } static bool write_spot_light_example( const wchar_t* filename, ON_TextLog& error_log ) { // create a blue spotlight shining on a white plane ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); // white material for surface ON_Material material; material.SetIndex(0); material.SetAmbient( ON_Color::White ); material.SetDiffuse( ON_Color::White ); material.SetEmission( ON_Color::Black ); material.SetSpecular( ON_Color::White ); material.SetShine( 0.35*ON_Material::MaxShine ); // 0 = flat // MaxShine() = shiney material.SetTransparency( 0.0 ); // 0 = opaque, 1 = transparent material.SetName( L"white material" ); model.AddModelComponent(material); model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); const int surfaces_layer_index = model.AddLayer(L"surfaces", ON_Color::Black); const int lights_layer_index = model.AddLayer(L"lights", ON_Color::Black); // spotlight ON_Light light; light.SetLightIndex(0); light.SetLocation( ON_3dPoint(2.0, 3.0, 10.0) ); light.SetDirection( ON_3dVector(-1.0, -1.0, -10.0) ); light.SetDiffuse( ON_Color::SaturatedBlue ); light.SetAmbient( ON_Color::Black ); light.SetSpecular( ON_Color::SaturatedBlue ); light.SetSpotExponent( 60 ); // 0 = hard, 128 = soft light.SetSpotAngleDegrees( 30.0 ); light.SetStyle(ON::world_spot_light); light.SetLightName( "Blue spot light" ); ON_3dmObjectAttributes light_attributes; light_attributes.Default(); light_attributes.m_layer_index = lights_layer_index; // spotlights layer we defined above light_attributes.m_name = "Blue spot light"; model.AddModelGeometryComponent(&light, &light_attributes); // quick and dirty plane ON_PlaneSurface plane (ON_xy_plane); plane.SetDomain( 0, -10.0, +10.0 ); plane.SetDomain( 1, -10.0, +10.0 ); ON_3dmObjectAttributes plane_attributes; plane_attributes.Default(); plane_attributes.m_layer_index = surfaces_layer_index; // surfaces layer we defined above plane_attributes.m_material_index = material.Index(); // white material we defined above plane_attributes.SetMaterialSource(ON::material_from_object); plane_attributes.m_name = "20x20 plane"; model.AddModelGeometryComponent(&plane, &plane_attributes); return Internal_WriteExampleModel(model, filename, error_log); } static bool write_viewport_example( const wchar_t* filename, ON_TextLog& error_log ) { // create a model with 7 viewports ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); // views will display space inside the sphere ON_Sphere sphere ( ON_origin, 10.0 ); // Writes a 7 viewport layout - 3 along the right side, // 3 along the left side, and 1 big on in the middle // that displays the space inside the sphere. // Viewports have a "target" point inside of the view frustum. // This target is the center of view rotations. // Viewports have a "construction plane". This plane is // (optionally) displayed as a grid. // // OPTIONAL - change values from defaults model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Millimeters; model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.01; model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% // reserve room for 7 views model.m_settings.m_views.Reserve(7); // some values needed to fill in view information const double pos_x[4] = {0.0,0.25,0.75,1.0}; // x: 0 = left, 1 = right const double pos_y[4] = {0.0,1.0/3.0,2.0/3.0,1.0}; // y: 0 = top, 1 = bottom ON_3dVector camDir; double fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far; double target_distance; fr_left = -sphere.radius; fr_right = sphere.radius; fr_bottom = -sphere.radius; fr_top = sphere.radius; fr_near = 2.0*sphere.radius; // Rhino's default target_distance = 3.0*sphere.radius; fr_far = 4.0*sphere.radius; // view number 1 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = -ON_zaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_yaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "+X+Y parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[0]; view.m_position.m_wnd_right = pos_x[1]; view.m_position.m_wnd_top = pos_y[2]; view.m_position.m_wnd_bottom = pos_y[3]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane = ON_xy_plane; } // view number 2 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = ON_yaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_zaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "+X+Z parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[0]; view.m_position.m_wnd_right = pos_x[1]; view.m_position.m_wnd_top = pos_y[1]; view.m_position.m_wnd_bottom = pos_y[2]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane = ON_zx_plane; } // view number 3 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = -ON_xaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_zaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "+Y+Z parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[0]; view.m_position.m_wnd_right = pos_x[1]; view.m_position.m_wnd_top = pos_y[0]; view.m_position.m_wnd_bottom = pos_y[1]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane = ON_yz_plane; } // view number 4 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = ON_zaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_yaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "-X+Y parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[2]; view.m_position.m_wnd_right = pos_x[3]; view.m_position.m_wnd_top = pos_y[2]; view.m_position.m_wnd_bottom = pos_y[3]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_xaxis, ON_yaxis ); } // view number 5 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = -ON_yaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_zaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "-X+Z parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[2]; view.m_position.m_wnd_right = pos_x[3]; view.m_position.m_wnd_top = pos_y[1]; view.m_position.m_wnd_bottom = pos_y[2]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_xaxis, ON_zaxis ); } // view number 6 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first view.m_vp.SetProjection( ON::parallel_view ); camDir = ON_xaxis; view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_zaxis ); view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "-Y+Z parallel"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[2]; view.m_position.m_wnd_right = pos_x[3]; view.m_position.m_wnd_top = pos_y[0]; view.m_position.m_wnd_bottom = pos_y[1]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_yaxis, ON_zaxis ); } // view number 7 { ON_3dmView& view = model.m_settings.m_views.AppendNew(); // set primary view transformation information first target_distance = 10.0*sphere.radius; const double tan_half_angle = sphere.radius / target_distance; view.m_vp.SetProjection( ON::perspective_view ); camDir = ON_3dVector(-40.0,75.0,-50.0); view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); view.m_vp.SetCameraDirection( camDir ); view.m_vp.SetCameraUp( ON_zaxis ); fr_near = (target_distance - sphere.radius)/10.0; fr_far = target_distance + 1.5*sphere.radius; double d = fr_near*tan_half_angle; fr_left = -d; fr_right = d; fr_bottom = -d; fr_top = d; view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); view.SetTargetPoint(sphere.Center()); // secondary view "fluff" view.m_name = "skew perspective"; // position of viewport inside main window view.m_position.m_wnd_left = pos_x[1]; view.m_position.m_wnd_right = pos_x[2]; view.m_position.m_wnd_top = pos_y[0]; view.m_position.m_wnd_bottom = pos_y[3]; // construction plane view.m_cplane.Default(); // default grid settings view.m_cplane.m_plane = ON_xy_plane; } // Add a sphere model.AddDefaultLayer(L"sphere", ON_Color::Black); model.AddManagedModelGeometryComponent( sphere.RevSurfaceForm( true, nullptr ), nullptr ); return Internal_WriteExampleModel(model, filename, error_log); } static void make_trimming_curves( ON_Brep& brep, const ON_2dPoint& A2, // start point in parameter space const ON_2dPoint& B2, // end point in parameter space const ON_3dPoint& A3, // start point in parameter space const ON_3dPoint& B3 // end point in parameter space ) { ON_LineCurve* p2dCurve = new ON_LineCurve( A2, B2 ); ON_LineCurve* p3dCurve = new ON_LineCurve( A3, B3 ); // it is not necessary for the domains of the 2d and 3d curves // to match, but it makes it easier to understand the brep ON_Interval domain = p3dCurve->Domain(); p2dCurve->SetDomain( domain.Min(), domain.Max() ); brep.m_C2.Append(p2dCurve); brep.m_C3.Append(p3dCurve); } static bool write_trimmed_surface_example( const wchar_t* filename, ON_TextLog& error_log ) { // write a trimmed surface ONX_Model model; INTERNAL_INITIALIZE_MODEL(model); model.AddDefaultLayer(L"trimmed surface", ON_Color::Black); // trimmed surfaces are written as a CRhinoBrep that has // a single surface and a single CRhinoBrepFace. // // Trimming loops are simple closed curves and are oriented // so that the active portion of the trimmed surface's // domain lies to the left of the trimming curves. ON_Brep brep; ON_2dPoint q; // Create a 10x10 plane surface at z=3 with domain [0,1]x[0,1] ON_PlaneSurface* pSurface = new ON_PlaneSurface( ON_Plane( ON_3dPoint( 0, 0,3), ON_3dPoint(10,10,3), ON_3dPoint(10, 0,3) ) ); pSurface->SetDomain(0,0.0,10.0); pSurface->SetDomain(1,0.0,10.0); // ~ON_Brep() will delete this surface const int si = brep.m_S.Count(); // index of surface brep.m_S.Append(pSurface); // create simple trimming triangle ON_2dPoint A2(1.0, 2.0); // parameter space locations of 2d trim corners ON_2dPoint B2(9.0, 1.5); ON_2dPoint C2(7.0, 8.0); ON_3dPoint A3 = pSurface->PointAt(A2.x,A2.y); ON_3dPoint B3 = pSurface->PointAt(B2.x,B2.y); ON_3dPoint C3 = pSurface->PointAt(C2.x,C2.y); make_trimming_curves( brep, A2, B2, A3, B3 ); // creates 2d and 3d curve make_trimming_curves( brep, B2, C2, B3, C3 ); make_trimming_curves( brep, C2, A2, C3, A3 ); // there are vertices at the 3 corners brep.NewVertex( pSurface->PointAt( A2.x, A2.y ) ); brep.NewVertex( pSurface->PointAt( B2.x, B2.y ) ); brep.NewVertex( pSurface->PointAt( C2.x, C2.y ) ); // the vertices are exact since we have lines on a plane brep.m_V[0].m_tolerance = 0.0; brep.m_V[1].m_tolerance = 0.0; brep.m_V[2].m_tolerance = 0.0; // there are 3 edges along the sides of the triangle brep.NewEdge( brep.m_V[0], brep.m_V[1], 0 ); // start vertex, end vertex, 3d curve index brep.NewEdge( brep.m_V[1], brep.m_V[2], 1 ); // start vertex, end vertex, 3d curve index brep.NewEdge( brep.m_V[2], brep.m_V[0], 2 ); // start vertex, end vertex, 3d curve index // the edges are exact since we have lines on a plane brep.m_E[0].m_tolerance = 0.0; brep.m_E[1].m_tolerance = 0.0; brep.m_E[2].m_tolerance = 0.0; // there is 1 face ON_BrepFace& face = brep.NewFace( si ); // outer boundary trimming loops ON_BrepLoop& loop = brep.NewLoop( ON_BrepLoop::outer, face ); // geometrically, loops are made from a contiguous list of 2d parameter space // curves that form a simple closed curve. brep.NewTrim( brep.m_E[0], false, loop, 0 ); // A to B brep.NewTrim( brep.m_E[1], false, loop, 1 ); // B to C brep.NewTrim( brep.m_E[2], false, loop, 2 ); // C to A // the trims are exact since we have lines on a plane q = brep.m_C2[0]->PointAtStart(); //brep.m_T[0].m_P[0] = pSurface->PointAt(q.x,q.y); q = brep.m_C2[0]->PointAtEnd(); //brep.m_T[0].m_P[1] = pSurface->PointAt(q.x,q.y); brep.m_T[0].m_type = ON_BrepTrim::boundary; brep.m_T[0].m_tolerance[0] = 0.0; brep.m_T[0].m_tolerance[1] = 0.0; q = brep.m_C2[0]->PointAtStart(); //brep.m_T[1].m_P[0] = pSurface->PointAt(q.x,q.y); q = brep.m_C2[0]->PointAtEnd(); //brep.m_T[1].m_P[1] = pSurface->PointAt(q.x,q.y); brep.m_T[1].m_type = ON_BrepTrim::boundary; brep.m_T[1].m_tolerance[0] = 0.0; brep.m_T[1].m_tolerance[1] = 0.0; q = brep.m_C2[0]->PointAtStart(); //brep.m_T[2].m_P[0] = pSurface->PointAt(q.x,q.y); q = brep.m_C2[0]->PointAtEnd(); //brep.m_T[2].m_P[1] = pSurface->PointAt(q.x,q.y); brep.m_T[2].m_type = ON_BrepTrim::boundary; brep.m_T[2].m_tolerance[0] = 0.0; brep.m_T[2].m_tolerance[1] = 0.0; // when debugging your code, IsValid(), IsSolid(), IsManifold() are useful // to check. model.AddModelGeometryComponent(&brep, nullptr); return Internal_WriteExampleModel(model, filename, error_log); } //int main ( int argc, const char* argv[] ) int main () { bool rc = false; const wchar_t* filename; ON::Begin(); // If you want to learn to write b-rep models, first work through // this example paying close attention to write_trimmed_surface_example(), // then examime example_brep.cpp. // errors printed to stdout ON_TextLog error_log; // messages printed to stdout ON_TextLog message_log; // errors logged in text file //FILE* error_log_fp = ON::OpenFile("error_log.txt","w"); //ON_TextLog error_log(error_log_fp); filename = L"my_points.3dm"; rc = write_points_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_curves.3dm"; rc = write_curves_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_surfaces.3dm"; rc = write_surfaces_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_mesh.3dm"; rc = write_mesh_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_mesh_with_material.3dm"; rc = write_mesh_with_material_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_spot_light.3dm"; rc = write_spot_light_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_viewports.3dm"; rc = write_viewport_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); filename = L"my_trimmed_surface.3dm"; rc = write_trimmed_surface_example( filename, error_log ); if (rc) message_log.Print(L"Successfully wrote %ls.\n",filename); else message_log.Print(L"Errors while writing %ls.\n",filename); ON::End(); return 0; }