def construct_single_ply_piece(self, fraction=1.0): """Construct a ply piece for shape A (trapezium)""" th_nom = np.radians(self.fiber_angle) # Step 1: Define origin line L0 origin_point = Point2D(self.starting_position, 0.0) L0 = Line2D.from_point_angle(origin_point, th_nom) # Step 2: Define line L2, perpendicular to L0, tangent to circle s4 tangent_point = Point2D.from_polar(self.cg.s4, th_nom) L2 = Line2D.from_point_angle(tangent_point, th_nom + np.pi / 2) P0 = L0.intersection_line(L2) # Step 3: Position P2 and P3 based on max_width and eccentricity P2_dist = self.max_width * self.eccentricity P3_dist = self.max_width * (1 - self.eccentricity) P2 = P0 + Point2D.from_polar(P2_dist, L2.angle()) P3 = P0 + Point2D.from_polar(P3_dist, L2.angle() + np.pi) # Step 4: Calculate the spanned angle (both deltas should be >= 0) T2 = L0.intersection_circle_near(P2.norm(), P0) T3 = L0.intersection_circle_near(P3.norm(), P0) delta_phi_1 = fraction * (P2.angle() - T2.angle()) delta_phi_2 = fraction * (T3.angle() - P3.angle()) # Step 5: Calculate the side lines L1 and L3 L1 = L0.rotate(delta_phi_1) L3 = L0.rotate(-delta_phi_2) near_pt = Point2D(self.cg.s1, 0) P1a = L1.intersection_circle_near(self.cg.s1, near_pt) P4a = L3.intersection_circle_near(self.cg.s1, near_pt) # Redefine P2 and P3 if needed (for rest pieces) if fraction != 1.0: P2 = L2.intersection_line(L1) P3 = L2.intersection_line(L3) # Step 6: Construct L4, parallel to L2, through either P1a or P4a, # whichever is furthest from L2 if L2.distance_point(P1a) > L2.distance_point(P4a): L4_through_point = P1a else: L4_through_point = P4a L4 = Line2D.from_point_angle(L4_through_point, L2.angle()) # now redefine P1 and P4 as the intersection points: P1b = L4.intersection_line(L1) P4b = L4.intersection_line(L3) ip_L1_L3 = L1.intersection_line(L3) if L2.distance_point(ip_L1_L3) < L2.distance_point(P4b): # Line segments L1 and L3 intersect within the polygon, so we have # a 'hourglass' shape. Move P1 and P4 to the intersection point, # effectively forming a triangle. We could just drop P4, if not # for some other code expeccting 4-point polygons. P1, P4 = ip_L1_L3, ip_L1_L3 else: P1, P4 = P1b, P4b # Step 7: Return the final ply piece return PlyPiece(Polygon2D((P1, P2, P3, P4)), 0.0, -delta_phi_2, delta_phi_1)
def _useful_polygon(self, ply_piece=None): # Internal function to get a polygon representing the section of a # ply piece that overlaps with the useful area of the cone (between s2 # and s3). Additionally, return the points on s2 and s3 separately. if ply_piece is None: ply_piece = self.base_piece # Re-use cached value if possible, for performance if ply_piece in self._useful_polygon_cache: return self._useful_polygon_cache[ply_piece] P1, P2, P3, P4 = ply_piece.polygon.points() phi_nom = ply_piece.phi_nom L1 = Line2D.from_points(P1, P2) L3 = Line2D.from_points(P3, P4) points = [] # Define a quick helper function that will be useful later def make_intersection(L, s): return L.intersection_circle_near(s, Point2D.from_polar(s, phi_nom)) # Add the intersection with s2 near P1 if P1.norm() > self.cg.s2: # P1 is inside the useful area, add it as well L4 = Line2D.from_points(P4, P1) points.append(make_intersection(L4, self.cg.s2)) points.append(P1) else: # P1 is outside the useful area points.append(make_intersection(L1, self.cg.s2)) # Add the intersection points with s3 pts_on_s3 = (make_intersection(L1, self.cg.s3), make_intersection(L3, self.cg.s3)) points.extend(pts_on_s3) # Add the intersection with s2 near P4 if P4.norm() > self.cg.s2: # P4 is inside the useful area, add it as well points.append(P4) L4 = Line2D.from_points(P4, P1) points.append(make_intersection(L4, self.cg.s2)) else: # P4 is outside the useful area points.append(make_intersection(L3, self.cg.s2)) pts_on_s2 = (points[0], points[-1]) retval = (Polygon2D(points), pts_on_s2, pts_on_s3) self._useful_polygon_cache[ply_piece] = retval return retval
def construct_single_ply_piece(self, fraction=1.0): """Construct a ply piece for shape C (rectangle)""" th_nom = np.radians(self.fiber_angle) # Define origin line L0 origin_point = Point2D(self.starting_position, 0.0) L0 = Line2D.from_point_angle(origin_point, th_nom) # Define line L4 L4 = Line2D.from_point_angle(Point2D(0.0, 0.0), th_nom + np.pi / 2) ip_L0_L4 = L0.intersection_line(L4) # Construct P1 and P4 P1_dist = self.max_width * self.eccentricity P4_dist = self.max_width * (1 - self.eccentricity) P1 = ip_L0_L4 + Point2D.from_polar(P1_dist, L4.angle()) P4 = ip_L0_L4 + Point2D.from_polar(P4_dist, L4.angle() + np.pi) # Construct side lines L1, L3, parallel to L0 L1 = Line2D.from_point_angle(P1, L0.angle()) L3 = Line2D.from_point_angle(P4, L0.angle()) # Intersection points L0, L1, L3 with circle P0 = L0.intersection_circle_near(self.cg.s4, origin_point) P2 = L1.intersection_circle_near(self.cg.s4, P0) P3 = L3.intersection_circle_near(self.cg.s4, P0) # Handle creation of rest pieces, with fraction < 1.0 delta_phi_1 = (P2.angle() - P0.angle()) delta_phi_2 = (P0.angle() - P3.angle()) if fraction != 1.0: P2 = P2.rotate((fraction - 1.0) * delta_phi_1) P3 = P3.rotate((1.0 - fraction) * delta_phi_2) L1 = Line2D.from_point_angle(P2, L1.angle()) L3 = Line2D.from_point_angle(P3, L3.angle()) P1 = L4.intersection_line(L1) P4 = L4.intersection_line(L3) delta_phi_1 *= fraction delta_phi_2 *= fraction # Construct L2 through P2 or P3, whichever is furthest from L4 # Adjust the other point if L4.distance_point(P2) > L4.distance_point(P3): L2 = Line2D.from_point_angle(P2, L4.angle()) P3 = L2.intersection_line(L3) else: L2 = Line2D.from_point_angle(P3, L4.angle()) P2 = L2.intersection_line(L1) # Return the final ply piece return PlyPiece(Polygon2D((P1, P2, P3, P4)), 0.0, -delta_phi_2, delta_phi_1)
def construct_single_ply_piece(self, fraction=1.0): """Construct a ply piece for shape A (trapezium)""" th_nom = np.radians(self.fiber_angle) # Step 1: Define origin line L0 origin_point = Point2D(self.starting_position, 0.0) L0 = Line2D.from_point_angle(origin_point, th_nom) # Step 2: Define line L2, perpendicular to L0, tangent to circle s4 tangent_point = Point2D.from_polar(self.cg.s4, th_nom) L2 = Line2D.from_point_angle(tangent_point, th_nom + np.pi / 2) P0 = L0.intersection_line(L2) # Step 3: Position P2 and P3 based on max_width and eccentricity P2_dist = self.max_width * self.eccentricity P3_dist = self.max_width * (1 - self.eccentricity) P2 = P0 + Point2D.from_polar(P2_dist, L2.angle()) P3 = P0 + Point2D.from_polar(P3_dist, L2.angle() + np.pi) # Step 4: Calculate a rough estimate of the spanned angle delta_phi_1 = (P2.angle() - P0.angle()) delta_phi_2 = (P0.angle() - P3.angle()) ip_L0_s2 = L0.intersection_circle_near(self.cg.s2, origin_point) ip_L0_s4 = L0.intersection_circle_near(self.cg.s4, origin_point) ratio = 0.0 REQ_RATIO = 2.0 # Step 5: Iterate until the ratio of spanned angles on s4 and s2 is (nearly) correct while abs(ratio - REQ_RATIO) > TOL: delta_phi_on_s2 = REQ_RATIO * (delta_phi_1 + delta_phi_2) ip_L1_s2 = ip_L0_s2.rotate(REQ_RATIO * delta_phi_1) ip_L3_s2 = ip_L0_s2.rotate(-REQ_RATIO * delta_phi_2) L1 = Line2D.from_points(ip_L1_s2, P2) L3 = Line2D.from_points(ip_L3_s2, P3) ip_L1_s4 = L1.intersection_circle_near(self.cg.s4, P0) ip_L3_s4 = L3.intersection_circle_near(self.cg.s4, P0) delta_phi_1 = ip_L1_s4.angle() - ip_L0_s4.angle() delta_phi_2 = ip_L0_s4.angle() - ip_L3_s4.angle() delta_phi_on_s4 = delta_phi_1 + delta_phi_2 ratio = delta_phi_on_s2 / delta_phi_on_s4 # Redefine P2 and P3 if needed (for rest pieces) if fraction != 1.0: ip_L1_s2 = ip_L1_s2.rotate((fraction - 1.0) * delta_phi_1) ip_L3_s2 = ip_L3_s2.rotate((1.0 - fraction) * delta_phi_2) # Apply an additional rotation to avoid having more than one overlap ip_L3_s2 = ip_L3_s2.rotate(delta_phi_1 + delta_phi_2) ip_L1_s4 = ip_L1_s4.rotate((fraction - 1.0) * delta_phi_1) ip_L3_s4 = ip_L3_s4.rotate((1.0 - fraction) * delta_phi_2) L1 = Line2D.from_points(ip_L1_s2, ip_L1_s4) L3 = Line2D.from_points(ip_L3_s2, ip_L3_s4) delta_phi_1 *= fraction delta_phi_2 *= fraction # Step 6: redefine P1 to P4 as the intersection points L4 = Line2D.from_point_angle(Point2D(0.0, 0.0), L2.angle()) P1 = L1.intersection_line(L4) P2 = L2.intersection_line(L1) P3 = L3.intersection_line(L2) P4 = L4.intersection_line(L3) # Step 7: Return the final ply piece return PlyPiece(Polygon2D((P1, P2, P3, P4)), 0.0, -delta_phi_2, delta_phi_1)
def effective_area(self, ply_piece=None, max_angle_dev=2.0): """Get the effective area of a single ply piece. This is the area on useful section of the cone, where the deviation of the fiber angle is less than a given maximum. Parameters ---------- ply_piece : :class:`PlyPiece`, optional Ply piece to get the effective area for. If not set, the base piece is used. max_angle_dev : float, optional Maximum deviation from the nominal fiber angle to consider the material 'effective'. In degrees. Returns ------- out : tuple 2-tuple, where ``out[0]`` is the effective surface area and ``out[1]`` is the corresponding polygon. Notes ----- Note that the polygon has straight edges, while the calculation of the effective area takes into account that some edges of the effective area may be arc sections. """ if ply_piece is None: ply_piece = self.base_piece poly, pts_on_s2, pts_on_s3 = self._useful_polygon(ply_piece) # Construct lines through those corner points of the polygon, # that are on s2/s3. They will be useful later line_s2 = Line2D.from_points(*pts_on_s2) line_s3 = Line2D.from_points(*pts_on_s3) # Lines to cut away the non-effective area cut_line_1 = Line2D.from_point_angle( Point2D(0., 0.), ply_piece.phi_nom - np.radians(max_angle_dev)) cut_line_2 = Line2D.from_point_angle( Point2D(0., 0.), ply_piece.phi_nom + np.pi + np.radians(max_angle_dev)) # Slice the polygon, with some corrections for cut_line in (cut_line_1, cut_line_2): outp = [] for p in poly.slice_line(cut_line).points(): # Polygon intersection can result in points that are not # exactly on circles s2/s3, while they should be. Correct that. if cut_line.distance_point(p) < TOL: if line_s2.distance_point(p) < TOL: p = cut_line.intersection_circle_near(self.cg.s2, p) elif line_s3.distance_point(p) < TOL: p = cut_line.intersection_circle_near(self.cg.s3, p) outp.append(p) poly = Polygon2D(outp) # Calculate area area = poly.area() # Correct polygon area for arc sections s2/s3, if needed pts_on_s2 = [ p for p in poly.points() if abs(p.norm() - self.cg.s2) < TOL ] if len(pts_on_s2) >= 2: angles = [p.angle() for p in pts_on_s2] area -= circle_segment_area(self.cg.s2, max(angles) - min(angles)) pts_on_s3 = [ p for p in poly.points() if abs(p.norm() - self.cg.s3) < TOL ] if len(pts_on_s3) >= 2: angles = [p.angle() for p in pts_on_s3] area += circle_segment_area(self.cg.s3, max(angles) - min(angles)) return area, poly