55import matplotlib .pyplot as plt
66from mpl_toolkits .mplot3d import Axes3D
77
8+ from .intepolatedface import InterpolatedFace
9+
810class Blade (object ):
911 """
1012 Bottom-up parametrized blade construction.
@@ -158,16 +160,7 @@ def __init__(self, sections, radii, chord_lengths, pitch, rake,
158160 self .skew_angles = skew_angles
159161 self ._check_params ()
160162
161- self .pitch_angles = self ._compute_pitch_angle ()
162- self .induced_rake = self ._induced_rake_from_skew ()
163-
164- self .blade_coordinates_up = []
165- self .blade_coordinates_down = []
166-
167- self .generated_upper_face = None
168- self .generated_lower_face = None
169- self .generated_tip = None
170- self .generated_root = None
163+ self .reset ()
171164
172165 def reset (self ):
173166 """
@@ -176,12 +169,12 @@ def reset(self):
176169 self .blade_coordinates_up = []
177170 self .blade_coordinates_down = []
178171
179- self .generated_upper_face = None
180- self .generated_lower_face = None
181- self .generated_tip = None
182- self .generated_root = None
172+ self .upper_face = None
173+ self .lower_face = None
174+ self .tip_face = None
175+ self .root_face = None
183176
184- def build (self ):
177+ def build (self , reflect = True ):
185178 """
186179 Generate a bottom-up constructed propeller blade without applying any
187180 transformations on the airfoils.
@@ -190,11 +183,17 @@ def build(self):
190183 the given 3D coordinates of the blade sections.
191184
192185 """
193- from . import InterpolatedFace
194- upper_face = InterpolatedFace (self .blade_coordinates_up )
195- lower_face = InterpolatedFace (self .blade_coordinates_down )
196- tip_face = InterpolatedFace (np .stack
197-
186+ self .apply_transformations (reflect = reflect )
187+ self .upper_face = InterpolatedFace (self .blade_coordinates_up ).face
188+ self .lower_face = InterpolatedFace (self .blade_coordinates_down ).face
189+ self .tip_face = InterpolatedFace (np .stack ([
190+ self .blade_coordinates_up [- 1 ],
191+ self .blade_coordinates_down [- 1 ]
192+ ])).face
193+ self .root_face = InterpolatedFace (np .stack ([
194+ self .blade_coordinates_up [0 ],
195+ self .blade_coordinates_down [0 ]
196+ ])).face
198197
199198 def _check_params (self ):
200199 """
@@ -221,7 +220,7 @@ def _check_params(self):
221220 'rake, skew_angles} do not have the same shape.' )
222221
223222 @property
224- def pitch_angle (self ):
223+ def pitch_angles (self ):
225224 """
226225 Return the pitch angle from the linear pitch for all blade sections.
227226
@@ -385,7 +384,7 @@ def rotate(self, deg_angle=None, rad_angle=None, axis='x'):
385384 or if neither is inserted
386385
387386 """
388- if not self .blade_coordinates_up :
387+ if len ( self .blade_coordinates_up ) == 0 :
389388 raise ValueError ('You must apply transformations before rotation.' )
390389
391390 # Check rotation angle
@@ -431,10 +430,6 @@ def scale(self, factor):
431430
432431 :param float factor: scaling factor
433432 """
434-
435- scaling_matrix = np .array ([factor , 0 , 0 , 0 , factor ,
436- 0 , 0 , 0 , factor ]).reshape ((3 , 3 ))
437-
438433 self .blade_coordinates_up *= factor
439434 self .blade_coordinates_down *= factor
440435
@@ -633,112 +628,6 @@ def _write_blade_errors(self, upper_face, lower_face, errors):
633628 output_string += '\n '
634629 f .write (output_string )
635630
636- def generate_iges (self ,
637- upper_face = None ,
638- lower_face = None ,
639- tip = None ,
640- root = None ,
641- max_deg = 1 ,
642- display = False ,
643- errors = None ):
644- """
645- Generate and export the .iges CAD for the blade upper face, lower face,
646- tip and root. This method requires PythonOCC (7.4.0) to be installed.
647-
648- :param string upper_face: if string is passed then the method generates
649- the blade upper surface using the BRepOffsetAPI_ThruSections
650- algorithm, then exports the generated CAD into .iges file holding
651- the name <upper_face_string>.iges. Default value is None
652- :param string lower_face: if string is passed then the method generates
653- the blade lower surface using the BRepOffsetAPI_ThruSections
654- algorithm, then exports the generated CAD into .iges file holding
655- the name <lower_face_string>.iges. Default value is None
656- :param string tip: if string is passed then the method generates
657- the blade tip using the BRepOffsetAPI_ThruSections algorithm
658- in order to close the blade at the tip, then exports the generated
659- CAD into .iges file holding the name <tip_string>.iges.
660- Default value is None
661- :param string root: if string is passed then the method generates
662- the blade root using the BRepOffsetAPI_ThruSections algorithm
663- in order to close the blade at the root, then exports the generated
664- CAD into .iges file holding the name <tip_string>.iges.
665- Default value is None
666- :param int max_deg: Define the maximal U degree of generated surface.
667- Default value is 1
668- :param bool display: if True, then display the generated CAD. Default
669- value is False
670- :param string errors: if string is passed then the method writes out
671- the distances between each discrete point used to construct the
672- blade and the nearest point on the CAD that is perpendicular to
673- that point. Default value is None
674-
675- We note that the blade object must have its radial sections be arranged
676- in order from the blade root to the blade tip, so that generate_iges
677- method can build the CAD surface that passes through the corresponding
678- airfoils. Also to be able to identify and close the blade tip and root.
679- """
680-
681- from OCC .Core .IGESControl import IGESControl_Writer
682- from OCC .Display .SimpleGui import init_display
683-
684- if max_deg <= 0 :
685- raise ValueError ('max_deg argument must be a positive integer.' )
686-
687- if upper_face :
688- self ._check_string (filename = upper_face )
689- self ._generate_upper_face (max_deg = max_deg )
690- # Write IGES
691- iges_writer = IGESControl_Writer ()
692- iges_writer .AddShape (self .generated_upper_face )
693- iges_writer .Write (upper_face + '.iges' )
694-
695- if lower_face :
696- self ._check_string (filename = lower_face )
697- self ._generate_lower_face (max_deg = max_deg )
698- # Write IGES
699- iges_writer = IGESControl_Writer ()
700- iges_writer .AddShape (self .generated_lower_face )
701- iges_writer .Write (lower_face + '.iges' )
702-
703- if tip :
704- self ._check_string (filename = tip )
705- self ._generate_tip (max_deg = max_deg )
706- # Write IGES
707- iges_writer = IGESControl_Writer ()
708- iges_writer .AddShape (self .generated_tip )
709- iges_writer .Write (tip + '.iges' )
710-
711- if root :
712- self ._check_string (filename = root )
713- self ._generate_root (max_deg = max_deg )
714- # Write IGES
715- iges_writer = IGESControl_Writer ()
716- iges_writer .AddShape (self .generated_root )
717- iges_writer .Write (root + '.iges' )
718-
719- if errors :
720- # Write out errors between discrete points and constructed faces
721- self ._check_string (filename = errors )
722- self ._check_errors (upper_face = upper_face , lower_face = lower_face )
723-
724- self ._write_blade_errors (
725- upper_face = upper_face , lower_face = lower_face , errors = errors )
726-
727- if display :
728- display , start_display , add_menu , add_function_to_menu = init_display (
729- )
730-
731- ## DISPLAY FACES
732- if upper_face :
733- display .DisplayShape (self .generated_upper_face , update = True )
734- if lower_face :
735- display .DisplayShape (self .generated_lower_face , update = True )
736- if tip :
737- display .DisplayShape (self .generated_tip , update = True )
738- if root :
739- display .DisplayShape (self .generated_root , update = True )
740- start_display ()
741-
742631 def generate_solid (self ,
743632 max_deg = 1 ,
744633 display = False ,
@@ -766,145 +655,24 @@ def generate_solid(self,
766655 if max_deg <= 0 :
767656 raise ValueError ('max_deg argument must be a positive integer.' )
768657
769- self ._generate_upper_face (max_deg = max_deg )
770-
771- print (self .generated_upper_face )
772- from . import InterpolatedFace
773- upper_face = InterpolatedFace (self .blade_coordinates_up )
774- lower_face = InterpolatedFace (self .blade_coordinates_down )
775- tip_face = InterpolatedFace (np .stack ([
776- self .blade_coordinates_up [- 1 ],
777- self .blade_coordinates_down [- 1 ]
778- ]))
779- root_face = InterpolatedFace (np .stack ([
780- self .blade_coordinates_up [0 ],
781- self .blade_coordinates_down [0 ]
782- ]))
783- # print(upper_face.face)
784- # fff
785- # self._generate_lower_face(max_deg=max_deg)
786- # self._generate_tip(max_deg=max_deg)
787- # self._generate_root(max_deg=max_deg)
788-
789- if errors :
790- # Write out errors between discrete points and constructed faces
791- self ._check_string (filename = errors )
792- self ._check_errors (upper_face = upper_face , lower_face = lower_face )
793-
794- self ._write_blade_errors (
795- upper_face = upper_face , lower_face = lower_face , errors = errors )
796-
658+ faces = [
659+ self .upper_face , self .lower_face , self .tip_face , self .root_face
660+ ]
797661
798662 sewer = BRepBuilderAPI_Sewing (1e-2 )
799- for face in [upper_face .face , lower_face .face , tip_face .face ,
800- root_face .face ]:
663+ for face in faces :
801664 sewer .Add (face )
802665 sewer .Perform ()
803666
804667 result_shell = sewer .SewedShape ()
805668 solid_maker = BRepBuilderAPI_MakeSolid ()
806669 solid_maker .Add (OCC .Core .TopoDS .topods .Shell (result_shell ))
670+
807671 if not solid_maker .IsDone ():
808672 raise RuntimeError ('Unsuccessful assembling of solid blade' )
809673 result_solid = solid_maker .Solid ()
810- return result_solid
811-
812- def generate_stl (self , upper_face = None ,
813- lower_face = None ,
814- tip = None ,
815- root = None ,
816- max_deg = 1 ,
817- display = False ,
818- errors = None ):
819- """
820- Generate and export the .STL files for upper face, lower face, tip
821- and root. This method requires PythonOCC (7.4.0) to be installed.
822-
823- :param string upper_face: if string is passed then the method generates
824- the blade upper surface using the BRepOffsetAPI_ThruSections
825- algorithm, then exports the generated CAD into .stl file holding
826- the name <upper_face_string>.stl. Default value is None
827- :param string lower_face: if string is passed then the method generates
828- the blade lower surface using the BRepOffsetAPI_ThruSections
829- algorithm, then exports the generated CAD into .stl file holding
830- the name <lower_face_string>.stl. Default value is None
831- :param string tip: if string is passed then the method generates
832- the blade tip using the BRepOffsetAPI_ThruSections algorithm
833- in order to close the blade at the tip, then exports the generated
834- CAD into .stl file holding the name <tip_string>.stl.
835- Default value is None
836- :param string root: if string is passed then the method generates
837- the blade root using the BRepOffsetAPI_ThruSections algorithm
838- in order to close the blade at the root, then exports the generated
839- CAD into .stl file holding the name <tip_string>.stl.
840- Default value is None
841- :param int max_deg: Define the maximal U degree of generated surface.
842- Default value is 1
843- :param bool display: if True, then display the generated CAD. Default
844- value is False
845- :param string errors: if string is passed then the method writes out
846- the distances between each discrete point used to construct the
847- blade and the nearest point on the CAD that is perpendicular to
848- that point. Default value is None
849-
850- We note that the blade object must have its radial sections be arranged
851- in order from the blade root to the blade tip, so that generate_stl
852- method can build the CAD surface that passes through the corresponding
853- airfoils. Also to be able to identify and close the blade tip and root.
854- """
855-
856- from OCC .Extend .DataExchange import write_stl_file
857- from OCC .Display .SimpleGui import init_display
858-
859- if max_deg <= 0 :
860- raise ValueError ('max_deg argument must be a positive integer.' )
861674
862- if upper_face :
863- self ._check_string (filename = upper_face )
864- self ._generate_upper_face (max_deg = max_deg )
865- # Write STL
866- write_stl_file (self .generated_upper_face , upper_face + '.stl' )
867-
868- if lower_face :
869- self ._check_string (filename = lower_face )
870- self ._generate_lower_face (max_deg = max_deg )
871- # Write STL
872- write_stl_file (self .generated_lower_face , lower_face + '.stl' )
873-
874- if tip :
875- self ._check_string (filename = tip )
876- self ._generate_tip (max_deg = max_deg )
877- # Write STL
878- write_stl_file (self .generated_tip , tip + '.stl' )
879-
880- if root :
881- self ._check_string (filename = root )
882- self ._generate_root (max_deg = max_deg )
883- # Write STL
884- write_stl_file (self .generated_root , root + '.stl' )
885-
886- if errors :
887- # Write out errors between discrete points and constructed faces
888- self ._check_string (filename = errors )
889- self ._check_errors (upper_face = upper_face , lower_face = lower_face )
890-
891- self ._write_blade_errors (
892- upper_face = upper_face , lower_face = lower_face , errors = errors )
893-
894- if display :
895- display , start_display , add_menu , add_function_to_menu = init_display (
896- )
897-
898- ## DISPLAY FACES
899- if upper_face :
900- display .DisplayShape (self .generated_upper_face , update = True )
901- if lower_face :
902- display .DisplayShape (self .generated_lower_face , update = True )
903- if tip :
904- display .DisplayShape (self .generated_tip , update = True )
905- if root :
906- display .DisplayShape (self .generated_root , update = True )
907- start_display ()
675+ return result_solid
908676
909677 def generate_stl_blade (self , filename ):
910678 """
0 commit comments