def execute(self, fp): if not fp.Base or not fp.Tools: return fp.Proxy = None if fp.CutDirection == Vector(0.0, 0.0, 0.0): bbox = self.extractCompounds([fp.Base])[0].Shape.BoundBox v = Vector(1, 1, 1) if bbox.XLength < bbox.YLength and bbox.XLength < bbox.ZLength: v.x = 0 elif bbox.YLength <= bbox.XLength and bbox.YLength < bbox.ZLength: v.y = 0 else: v.z = 0 bbox = self.extractCompounds(fp.Tools)[0].Shape.BoundBox if bbox.XLength < bbox.YLength and bbox.XLength < bbox.ZLength: v.x = 0 elif bbox.YLength <= bbox.XLength and bbox.YLength < bbox.ZLength: v.y = 0 else: v.z = 0 fp.CutDirection = v * fp.CutDepth / 50 fp.Proxy = self self.cutNotches(fp)
def cutNotches(self, fp): shapes = [] halfsize = fp.CutDirection / 2 for obj in self.extractCompounds([fp.Base]): useSubPart = obj.TypeId == 'Part::Extrusion' and len( obj.Base.Shape.Faces) > 0 if useSubPart: bShapes = obj.Base else: bShapes = obj for bShape in self.extractShapes([bShapes]): cutcubes = [] for tool in self.extractShapes(fp.Tools): tbox = tool.optimalBoundingBox() common = tool.common(bShape) cbox = common.BoundBox if cbox.XLength + cbox.YLength + cbox.ZLength > epsilon: cbox = common.optimalBoundingBox() vSize = Vector(cbox.XLength, cbox.YLength, cbox.ZLength) vPlace = Vector(cbox.XMin, cbox.YMin, cbox.ZMin) if vSize.x < epsilon or vSize.x > tbox.XLength: vSize.x = tbox.XLength vPlace.x = tbox.XMin if vSize.y < epsilon or vSize.y > tbox.YLength: vSize.y = tbox.YLength vPlace.y = tbox.YMin if vSize.z < epsilon or vSize.z > tbox.ZLength: vSize.z = tbox.ZLength vPlace.z = tbox.ZMin cutcube = Part.makeBox(vSize.x, vSize.y, vSize.z) cutcube.Placement.Base = vPlace cutcube.Placement.Base.x += cbox.XLength * halfsize.x cutcube.Placement.Base.y += cbox.YLength * halfsize.y cutcube.Placement.Base.z += cbox.ZLength * halfsize.z cutcubes.append(cutcube) if len(cutcubes) > 0: try: cutted = bShape.cut(cutcubes) except: cutted = bShape else: cutted = bShape if useSubPart: cutted.Placement.Base -= obj.Dir * float(obj.LengthRev) ext = cutted.extrude(obj.Dir * float(obj.LengthFwd + obj.LengthRev)) shapes.append(ext) else: shapes.append(cutted) fp.Shape = Part.makeCompound(shapes)
def cutNotches(self, fp): shapes = [] halfsize = fp.CutDirection / 2 for obj in self.extractCompounds([fp.Base]): isExtrude = hasattr(obj, "LengthFwd") and hasattr(obj, "Base") if isExtrude: bShapes = obj.Base else: bShapes = obj for bShape in self.extractShapes([bShapes]): cutcubes = [] for tool in self.extractShapes(fp.Tools): tbox = tool.BoundBox common = tool.common(bShape) cbox = common.BoundBox if cbox.XLength + cbox.YLength + cbox.ZLength > epsilon: vSize = Vector(cbox.XLength, cbox.YLength, cbox.ZLength) vPlace = Vector(cbox.XMin, cbox.YMin, cbox.ZMin) if vSize.x < epsilon or vSize.x > tbox.XLength: vSize.x = tbox.XLength vPlace.x = tbox.XMin if vSize.y < epsilon or vSize.y > tbox.YLength: vSize.y = tbox.YLength vPlace.y = tbox.YMin if vSize.z < epsilon or vSize.z > tbox.ZLength: vSize.z = tbox.ZLength vPlace.z = tbox.ZMin cutcube = Part.makeBox(vSize.x, vSize.y, vSize.z) cutcube.Placement.Base = vPlace cutcube.Placement.Base.x += cbox.XLength * halfsize.x cutcube.Placement.Base.y += cbox.YLength * halfsize.y cutcube.Placement.Base.z += cbox.ZLength * halfsize.z cutcubes.append(cutcube) if len(cutcubes) > 0: cutted = bShape.cut(cutcubes) else: cutted = bShape if isExtrude: cutted.Placement.Base -= obj.Dir * float(obj.LengthRev) ext = cutted.extrude(obj.Dir * float(obj.LengthFwd + obj.LengthRev)) shapes.append(ext) else: shapes.append(cutted) if len(shapes) == 1: fp.Shape = shapes[0] elif len(shapes) > 1: fp.Shape = Part.makeCompound(shapes)
def __vol_cog(shape): vol = 0.0 cog = Vector() for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol return cog, vol
def scaleByBoundbox(shape, boundbox, doScaleXYZ, copy=True): basebbox = shape.BoundBox #basebbox=[1.0,1.0,1.0] scalevec = Vector(1, 1, 1) if doScaleXYZ[0] and basebbox.XLength > epsilon: scalevec.x = boundbox.XLength / basebbox.XLength if doScaleXYZ[1] and basebbox.YLength > epsilon: scalevec.y = boundbox.YLength / basebbox.YLength if doScaleXYZ[2] and basebbox.ZLength > epsilon: scalevec.z = boundbox.ZLength / basebbox.ZLength scalevec.x = boundbox.XLength scalevec.y = boundbox.YLength scalevec.z = boundbox.ZLength if scalevec.x < epsilon: if doScaleXYZ[0]: scalevec.x = epsilon else: scalevec.x = 1 if scalevec.y < epsilon: if doScaleXYZ[1]: scalevec.y = epsilon else: scalevec.y = 1 if scalevec.z < epsilon: if doScaleXYZ[2]: scalevec.z = epsilon else: scalevec.z = 1 _rib = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Rib00") print("Scale in scaleByBoundbox") print(scalevec) WingRib( _rib, "/Users/fredericnivoix/Library/Preferences/FreeCAD/Mod/AirPlaneDesign/wingribprofil/naca/naca2412.dat", False, 0, scalevec.x, 0, 0, 0, 0, 0, 0) ViewProviderWingRib(_rib.ViewObject) _rib.Placement = shape.Placement #dolly = scale(shape, scalevec, basebbox.Center, copy) #dolly.Placement = shape.Placement if doScaleXYZ[0]: _rib.Placement.Base.x += boundbox.XMin - basebbox.XMin * scalevec.x if doScaleXYZ[1]: _rib.Placement.Base.y += boundbox.YMin - basebbox.YMin * scalevec.y if doScaleXYZ[2]: _rib.Placement.Base.z += boundbox.ZMin - basebbox.ZMin * scalevec.z return _rib #dolly
def getCoG(self, fp, vol, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Return the fluid volume center of gravity, provided the volume of fluid inside the tank. The returned center of gravity is referred to the untransformed ship. Keyword arguments: fp -- Part::FeaturePython object affected. vol -- Volume of fluid. roll -- Ship roll angle. trim -- Ship trim angle. If the fluid volume is bigger than the total tank one, it will be conveniently clamped. """ if vol <= 0.0: return Vector() if vol >= fp.Shape.Volume: vol = 0.0 cog = Vector() for solid in fp.Shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol return cog shape = self.getFluidShape(fp, vol, roll, trim) # Get the center of gravity vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol return cog
def pointInversion(circle, point): """Circle inversion of a point. pointInversion(Circle, Vector) Will calculate the inversed point an return it. If the given point is equal to the center of the circle "None" will be returned. See also: http://en.wikipedia.org/wiki/Inversive_geometry """ if (geomType(circle) == "Circle") and isinstance(point, FreeCAD.Vector): cen = circle.Curve.Center rad = circle.Curve.Radius if DraftVecUtils.equals(cen, point): return None # Inverse the distance of the point # dist(cen -> P) = r^2 / dist(cen -> invP) dist = DraftVecUtils.dist(point, cen) invDist = rad**2 / d invPoint = Vector(0, 0, point.z) invPoint.x = cen.x + (point.x - cen.x) * invDist / dist invPoint.y = cen.y + (point.y - cen.y) * invDist / dist return invPoint else: FreeCAD.Console.PrintMessage("debug: pointInversion bad parameters!\n") return None
def checkLimit(v: FreeCAD.Vector, PressureAngleOffset, minrad, maxrad): """ if x,y outside limit return x,y as at limit, else return x,y :type v: FreeCAD.Vector """ r, a = toPolar(v.x, v.y) if (r > maxrad) or (r < minrad): r = r - PressureAngleOffset v.x, v.y = toRect(r, a) return v
def check_limit(v: FreeCAD.Vector, pressure_angle_offset, minrad, maxrad): """ if x,y outside limit return x,y as at limit, else return x,y :type v: FreeCAD.Vector """ r, a = to_polar(v.x, v.y) if (r > maxrad) or (r < minrad): r = r - pressure_angle_offset v.x, v.y = to_rect(r, a) return v
def scaleByBoundbox2(shape, boundbox, doScaleXYZ): basebbox = shape.BoundBox scalevec = Vector(1, 1, 1) x = shape.Placement.Base.x y = shape.Placement.Base.y z = shape.Placement.Base.z if doScaleXYZ[0] and basebbox.XLength > epsilon: scalevec.x = boundbox.XLength / basebbox.XLength if doScaleXYZ[1] and basebbox.YLength > epsilon: scalevec.y = boundbox.YLength / basebbox.YLength if doScaleXYZ[2] and basebbox.ZLength > epsilon: scalevec.z = boundbox.ZLength / basebbox.ZLength scalevec.x = boundbox.XLength scalevec.y = boundbox.YLength scalevec.z = boundbox.ZLength if scalevec.x < epsilon: if doScaleXYZ[0]: scalevec.x = epsilon else: scalevec.x = 1 if scalevec.y < epsilon: if doScaleXYZ[1]: scalevec.y = epsilon else: scalevec.y = 1 if scalevec.z < epsilon: if doScaleXYZ[2]: scalevec.z = epsilon else: scalevec.z = 1 if doScaleXYZ[0]: x += boundbox.XMin - basebbox.XMin * scalevec.x if doScaleXYZ[1]: y += boundbox.YMin - basebbox.YMin * scalevec.y if doScaleXYZ[2]: z += boundbox.ZMin - basebbox.ZMin * scalevec.z return x, y, z, scalevec.x, scalevec.y, scalevec.z
def scaleByBoundbox(shape, boundbox, doScaleXYZ, copy=True): basebbox = shape.BoundBox scalevec = Vector(1, 1, 1) if basebbox.XLength > epsilon: scalevec.x = boundbox.XLength / basebbox.XLength if basebbox.YLength > epsilon: scalevec.y = boundbox.YLength / basebbox.YLength if basebbox.ZLength > epsilon: scalevec.z = boundbox.ZLength / basebbox.ZLength if scalevec.x < epsilon: if doScaleXYZ[0]: scalevec.x = epsilon else: scalevec.x = 1 if scalevec.y < epsilon: if doScaleXYZ[1]: scalevec.y = epsilon else: scalevec.y = 1 if scalevec.z < epsilon: if doScaleXYZ[2]: scalevec.z = epsilon else: scalevec.z = 1 dolly = scale(shape, scalevec, basebbox.Center, copy) dolly.Placement = shape.Placement if doScaleXYZ[0]: dolly.Placement.Base.x += boundbox.XMin - basebbox.XMin * scalevec.x if doScaleXYZ[1]: dolly.Placement.Base.y += boundbox.YMin - basebbox.YMin * scalevec.y if doScaleXYZ[2]: dolly.Placement.Base.z += boundbox.ZMin - basebbox.ZMin * scalevec.z return dolly
def makeRibs(self, obj): pl = obj.Placement ribs = [] curvebox = FreeCAD.BoundBox(float("-inf"), float("-inf"), float("-inf"), float("inf"), float("inf"), float("inf")) for n in range(0, len(obj.Hullcurves)): cbbx = obj.Hullcurves[n].Shape.BoundBox if self.doScaleXYZ[n][0]: if cbbx.XMin > curvebox.XMin: curvebox.XMin = cbbx.XMin if cbbx.XMax < curvebox.XMax: curvebox.XMax = cbbx.XMax if self.doScaleXYZ[n][1]: if cbbx.YMin > curvebox.YMin: curvebox.YMin = cbbx.YMin if cbbx.YMax < curvebox.YMax: curvebox.YMax = cbbx.YMax if self.doScaleXYZ[n][2]: if cbbx.ZMin > curvebox.ZMin: curvebox.ZMin = cbbx.ZMin if cbbx.ZMax < curvebox.ZMax: curvebox.ZMax = cbbx.ZMax if curvebox.XMin == float("-inf"): curvebox.XMin = obj.Hullcurves[0].Shape.BoundBox.XMin if curvebox.XMax == float("inf"): curvebox.XMax = obj.Hullcurves[0].Shape.BoundBox.XMax if curvebox.YMin == float("-inf"): curvebox.YMin = obj.Hullcurves[0].Shape.BoundBox.YMin if curvebox.YMax == float("inf"): curvebox.YMax = obj.Hullcurves[0].Shape.BoundBox.YMax if curvebox.ZMin == float("-inf"): curvebox.ZMin = obj.Hullcurves[0].Shape.BoundBox.ZMin if curvebox.ZMax == float("inf"): curvebox.ZMax = obj.Hullcurves[0].Shape.BoundBox.ZMax areavec = Vector(curvebox.XLength, curvebox.YLength, curvebox.ZLength) deltavec = areavec.scale( obj.Axis.x, obj.Axis.y, obj.Axis.z) - (obj.OffsetStart + obj.OffsetEnd) * obj.Axis sections = int(obj.Items) startvec = Vector(curvebox.XMin, curvebox.YMin, curvebox.ZMin) if obj.Axis.x < 0: startvec.x = curvebox.XMax if obj.Axis.y < 0: startvec.y = curvebox.YMax if obj.Axis.z < 0: startvec.z = curvebox.ZMax pos0 = startvec + (obj.OffsetStart * obj.Axis) if (not hasattr(obj, "Positions") or len(obj.Positions) == 0): for x in range(0, sections): if sections > 1: d = CurvedShapes.distribute(x / (sections - 1), obj.Distribution, obj.DistributionReverse) posvec = pos0 + (deltavec * d) else: posvec = pos0 dolly = self.makeRib(obj, posvec) if dolly: if not obj.Twist == 0: dolly.rotate( dolly.BoundBox.Center, obj.Axis, obj.Twist * posvec.Length / areavec.Length) ribs.append(dolly) else: for p in obj.Positions: posvec = pos0 + (deltavec * p) dolly = self.makeRib(obj, posvec) if dolly: if not obj.Twist == 0: dolly.rotate( dolly.BoundBox.Center, obj.Axis, obj.Twist * posvec.Length / areavec.Length) ribs.append(dolly) if (obj.Surface or obj.Solid) and obj.Items > 1: obj.Shape = CurvedShapes.makeSurfaceSolid(ribs, obj.Solid) else: obj.Shape = Part.makeCompound(ribs) obj.Placement = pl if self.extract: CompoundTools.Explode.explodeCompound(obj) obj.ViewObject.hide()
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0): """ Compute the ship displacement. @param ship Ship instance. @param draft Ship draft. @param roll Ship roll angle. @param trim Ship trim angle. @param yaw Ship yaw angle. Ussually you don't want to use this value. @return [disp, B, Cb], \n - disp = Ship displacement [ton]. - B = Bouyance center [m]. - Cb = Block coefficient. @note Bouyance center will returned as a FreeCAD.Vector instance. @note Returned Bouyance center is in the non modified ship coordinates """ # We will take a duplicate of ship shape in order to conviniently # manipulate it shape = ship.Shape.copy() shape.translate(Vector(0.0, 0.0, -draft * Units.Metre.Value)) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw) bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax # Create the "sea" box to intersect the ship L = xmax - xmin B = bbox.YMax - bbox.YMin p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0) try: box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p) except: return [0.0, Vector(), 0.0] vol = 0.0 cog = Vector() for solid in shape.Solids: # Compute the common part of the "sea" with the ship try: common = box.common(solid) except: continue # Get the data vol = vol + common.Volume / Units.Metre.Value**3 for s in common.Solids: sCoG = s.CenterOfMass cog.x = cog.x + sCoG.x * s.Volume / Units.Metre.Value**4 cog.y = cog.y + sCoG.y * s.Volume / Units.Metre.Value**4 cog.z = cog.z + sCoG.z * s.Volume / Units.Metre.Value**4 cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol Vol = L * B * abs(bbox.ZMin) / Units.Metre.Value**3 # Undo the transformations B = Vector() B.x = cog.x * math.cos(math.radians(-yaw)) - \ cog.y * math.sin(math.radians(-yaw)) B.y = cog.x * math.sin(math.radians(-yaw)) + \ cog.y * math.cos(math.radians(-yaw)) B.z = cog.z cog.x = B.x * math.cos(math.radians(-trim)) - \ B.z * math.sin(math.radians(-trim)) cog.y = B.y cog.z = B.x * math.sin(math.radians(-trim)) + \ B.z * math.cos(math.radians(-trim)) B.x = cog.x B.y = cog.y * math.cos(math.radians(-roll)) - \ cog.z * math.sin(math.radians(-roll)) B.z = cog.y * math.sin(math.radians(-roll)) + \ cog.z * math.cos(math.radians(-roll)) B.z = B.z + draft # Return the computed data dens = 1.025 # [tons/m3], salt water return [dens*vol, B, vol/Vol]
def getCoG(self, fp, vol, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Return the fluid volume center of gravity, provided the volume of fluid inside the tank. The returned center of gravity is refered to the untransformed ship. Keyword arguments: fp -- Part::FeaturePython object affected. vol -- Volume of fluid. roll -- Ship roll angle. trim -- Ship trim angle. If the fluid volume is bigger than the total tank one, it will be conveniently clamped. """ # Change the units of the volume, and clamp the value if vol <= 0.0: return Vector() if vol >= fp.Shape.Volume: vol = 0.0 for solid in fp.Shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol return cog # Get a first estimation of the level level = vol.Value / fp.Shape.Volume # Transform the tank shape current_placement = fp.Placement m = current_placement.toMatrix() m.rotateX(roll.getValueAs("rad")) m.rotateY(-trim.getValueAs("rad")) fp.Placement = Placement(m) # Iterate to find the fluid shape for i in range(COMMON_BOOLEAN_ITERATIONS): shape = self.getVolume(fp, level, return_shape=True) error = (vol.Value - shape.Volume) / fp.Shape.Volume if abs(error) < 0.01: break level += error # Get the center of gravity vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol # Untransform the object to retrieve the original position fp.Placement = current_placement p = Part.Point(cog) m = Matrix() m.rotateY(trim.getValueAs("rad")) m.rotateX(-roll.getValueAs("rad")) p.rotate(Placement(m)) return Vector(p.X, p.Y, p.Z)
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0): """ Compute the ship displacement. @param ship Ship instance. @param draft Ship draft. @param roll Ship roll angle. @param trim Ship trim angle. @param yaw Ship yaw angle. Ussually you don't want to use this value. @return [disp, B, Cb], \n - disp = Ship displacement [ton]. - B = Bouyance center [m]. - Cb = Block coefficient. @note Bouyance center will returned as a FreeCAD.Vector instance. @note Returned Bouyance center is in the non modified ship coordinates """ # We will take a duplicate of ship shape in order to conviniently # manipulate it shape = ship.Shape.copy() shape.translate(Vector(0.0, 0.0, -draft * Units.Metre.Value)) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw) bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax # Create the "sea" box to intersect the ship L = xmax - xmin B = bbox.YMax - bbox.YMin p = Vector(-1.5 * L, -1.5 * B, bbox.ZMin - 1.0) try: box = Part.makeBox(3.0 * L, 3.0 * B, -bbox.ZMin + 1.0, p) except: return [0.0, Vector(), 0.0] vol = 0.0 cog = Vector() for solid in shape.Solids: # Compute the common part of the "sea" with the ship try: common = box.common(solid) except: continue # Get the data vol = vol + common.Volume / Units.Metre.Value**3 for s in common.Solids: sCoG = s.CenterOfMass cog.x = cog.x + sCoG.x * s.Volume / Units.Metre.Value**4 cog.y = cog.y + sCoG.y * s.Volume / Units.Metre.Value**4 cog.z = cog.z + sCoG.z * s.Volume / Units.Metre.Value**4 cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol Vol = L * B * abs(bbox.ZMin) / Units.Metre.Value**3 # Undo the transformations B = Vector() B.x = cog.x * math.cos(math.radians(-yaw)) - \ cog.y * math.sin(math.radians(-yaw)) B.y = cog.x * math.sin(math.radians(-yaw)) + \ cog.y * math.cos(math.radians(-yaw)) B.z = cog.z cog.x = B.x * math.cos(math.radians(-trim)) - \ B.z * math.sin(math.radians(-trim)) cog.y = B.y cog.z = B.x * math.sin(math.radians(-trim)) + \ B.z * math.cos(math.radians(-trim)) B.x = cog.x B.y = cog.y * math.cos(math.radians(-roll)) - \ cog.z * math.sin(math.radians(-roll)) B.z = cog.y * math.sin(math.radians(-roll)) + \ cog.z * math.cos(math.radians(-roll)) B.z = B.z + draft # Return the computed data dens = 1.025 # [tons/m3], salt water return [dens * vol, B, vol / Vol]
def getCoG(self, fp, vol, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Return the fluid volume center of gravity, provided the volume of fluid inside the tank. The returned center of gravity is referred to the untransformed ship. Keyword arguments: fp -- Part::FeaturePython object affected. vol -- Volume of fluid. roll -- Ship roll angle. trim -- Ship trim angle. If the fluid volume is bigger than the total tank one, it will be conveniently clamped. """ # Change the units of the volume, and clamp the value if vol <= 0.0: return Vector() if vol >= fp.Shape.Volume: vol = 0.0 for solid in fp.Shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol return cog # Get a first estimation of the level level = vol.Value / fp.Shape.Volume # Transform the tank shape current_placement = fp.Placement m = current_placement.toMatrix() m.rotateX(roll.getValueAs("rad")) m.rotateY(-trim.getValueAs("rad")) fp.Placement = Placement(m) # Iterate to find the fluid shape for i in range(COMMON_BOOLEAN_ITERATIONS): shape = self.getVolume(fp, level, return_shape=True) error = (vol.Value - shape.Volume) / fp.Shape.Volume if abs(error) < 0.01: break level += error # Get the center of gravity vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol # Untransform the object to retrieve the original position fp.Placement = current_placement p = Part.Point(cog) m = Matrix() m.rotateY(trim.getValueAs("rad")) m.rotateX(-roll.getValueAs("rad")) p.rotate(Placement(m)) return Vector(p.X, p.Y, p.Z)
def create_compartment(box, direction, offset, materialWidth, notchWidth, drawSides=[True, True, True, True, True, True], doc = app.activeDocument()): cpos = direction * offset mybox = None if len(box) == 1 and hasattr(box[0], 'Links'): parts = box[0].Links mybox = box[0] elif isinstance(box, list): parts = box else: parts = [box] boxsize = Vector(0, 0, 0) for side in parts: if hasattr(side, 'Shape'): bbox = side.Shape.BoundBox if bbox.XLength > boxsize.x: boxsize.x = bbox.XLength if bbox.YLength > boxsize.y: boxsize.y = bbox.YLength if bbox.ZLength > boxsize.z: boxsize.z = bbox.ZLength holes = [] if direction == Vector(1, 0, 0): if boxsize.z == 0 or boxsize.y == 0: app.Console.PrintError("select a box first !\n") return compartment = draw_left(doc, 'compartmentX' + str(offset), materialWidth, boxsize.z, boxsize.y, notchWidth, drawSides) if drawSides[4] or drawSides[5]: holes += draw_holes(boxsize.y, notchWidth, materialWidth, Vector(0, 0, 90)) if drawSides[2] or drawSides[3]: holes += draw_holes(boxsize.z, notchWidth, materialWidth, Vector(90, 0, 90)) for h in holes: h.Placement.Base.x += offset + materialWidth elif direction == Vector(0, 1, 0): if boxsize.z == 0 or boxsize.x == 0: app.Console.PrintError("select a box first !\n") return sides = [drawSides[0], drawSides[1], drawSides[4], drawSides[5], drawSides[2], drawSides[3]] compartment = draw_left(doc, 'compartmentY' + str(offset), materialWidth, boxsize.z, boxsize.x, notchWidth, sides) doc.recompute() Draft.rotate([compartment], 270.0, Vector(0, 0, 0), axis=Vector(0.0, 0.0, 1.0), copy=False) doc.recompute() Draft.move([compartment], Vector(0, materialWidth, 0), copy=False) doc.recompute() if drawSides[0] or drawSides[1]: holes += draw_holes(boxsize.x, notchWidth, materialWidth, Vector(0, 0, 0)) if drawSides[2] or drawSides[3]: holes += draw_holes(boxsize.z, notchWidth, materialWidth, Vector(0, 270, 0)) for h in holes: h.Placement.Base.y += offset elif direction == Vector(0, 0, 1): if boxsize.x == 0 or boxsize.y == 0: app.Console.PrintError("select a box first !\n") return sides = [drawSides[2], drawSides[3], drawSides[0], drawSides[1], drawSides[4], drawSides[5]] compartment = draw_left(doc, 'compartmentZ' + str(offset), materialWidth, boxsize.x, boxsize.y, notchWidth, sides) doc.recompute() Draft.rotate([compartment], 270.0, Vector(boxsize.x, 0, 0), axis=Vector(0.0, 1.0, 0.0), copy=False) doc.recompute() Draft.move([compartment], Vector(0, 0, boxsize.x), copy=False) doc.recompute() if drawSides[0] or drawSides[1]: holes += draw_holes(boxsize.x, notchWidth, materialWidth, Vector(270, 0, 0)) if drawSides[4] or drawSides[5]: holes += draw_holes(boxsize.y, notchWidth, materialWidth, Vector(0, 270, 90)) for h in holes: h.Placement.Base.z += offset + materialWidth else: return None Draft.move([compartment], cpos, copy=False) doc.recompute() addLinesToBoxSide(parts, holes) if mybox: compartment.adjustRelativeLinks(mybox) mybox.ViewObject.dropObject(compartment, None, '', []) doc.recompute() return mybox doc.recompute() return compartment
def displacement(ship, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship displacement Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned values: disp -- The ship displacement (a density of the water of 1025 kg/m^3 is assumed) B -- Bouyance application point, i.e. Center of mass of the underwater side Cb -- Block coefficient The Bouyance center is refered to the original ship position. """ if draft is None: draft = ship.Draft shape, base_z = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol bbox = shape.BoundBox Vol = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin) * abs(bbox.ZMin) # Undo the transformations on the bouyance point B = Part.Point(Vector(cog.x, cog.y, cog.z)) m = Matrix() m.move(Vector(0.0, 0.0, draft)) m.move(Vector(-draft * math.sin(trim.getValueAs("rad")), 0.0, 0.0)) m.rotateY(trim.getValueAs("rad")) m.move(Vector(0.0, -draft * math.sin(roll.getValueAs("rad")), base_z)) m.rotateX(-roll.getValueAs("rad")) B.transform(m) try: cb = vol / Vol except ZeroDivisionError: msg = QtGui.QApplication.translate( "ship_console", "ZeroDivisionError: Null volume found during the displacement" " computation!", None, QtGui.QApplication.UnicodeUTF8) App.Console.PrintError(msg + '\n') cb = 0.0 # Return the computed data return (DENS * Units.Quantity(vol, Units.Volume), Vector(B.X, B.Y, B.Z), cb)
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0): """ Compute ship displacement. @param ship Ship instance. @param draft Ship draft. @param roll Ship roll angle. @param trim Ship trim angle. @param yaw Ship yaw angle. Ussually you don't want to use this value. @return [disp, B, Cb], \n disp = Ship displacement [ton]. B = Bouyance center [m]. Cb = Block coefficient. @note Bouyance center will returned as FreeCAD.Vector class. @note Returned Bouyance center is in non modified ship coordinates """ # We will take a duplicate of ship shape in order to place it shape = ship.Shape.copy() shape.translate(Vector(0.0,0.0,-draft)) # Rotations composition is Roll->Trim->Yaw shape.rotate(Vector(0.0,0.0,0.0), Vector(1.0,0.0,0.0), roll) shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim) shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,0.0,1.0), yaw) # Now we need to know box dimensions bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax # Create the box L = xmax - xmin B = bbox.YMax - bbox.YMin p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0) box = Part.makeBox(3.0*L, 3.0*B, -bbox.ZMin + 1.0, p) vol = 0.0 cog = Vector() for solid in shape.Solids: # Compute common part with ship try: common = box.common(solid) except: continue # Get data vol = vol + common.Volume for s in common.Solids: sCoG = s.CenterOfMass cog.x = cog.x + sCoG.x*s.Volume cog.y = cog.y + sCoG.y*s.Volume cog.z = cog.z + sCoG.z*s.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol Vol = L*B*abs(bbox.ZMin) # Undo transformations B = Vector() B.x = cog.x*math.cos(math.radians(-yaw)) - cog.y*math.sin(math.radians(-yaw)) B.y = cog.x*math.sin(math.radians(-yaw)) + cog.y*math.cos(math.radians(-yaw)) B.z = cog.z cog.x = B.x*math.cos(math.radians(-trim)) - B.z*math.sin(math.radians(-trim)) cog.y = B.y cog.z = B.x*math.sin(math.radians(-trim)) + B.z*math.cos(math.radians(-trim)) B.x = cog.x B.y = cog.y*math.cos(math.radians(-roll)) - cog.z*math.sin(math.radians(-roll)) B.z = cog.y*math.sin(math.radians(-roll)) + cog.z*math.cos(math.radians(-roll)) B.z = B.z + draft # Return data dens = 1.025 # [tons/m3], salt water return [dens*vol, B, vol/Vol]
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0): """ Compute ship displacement. @param ship Ship instance. @param draft Ship draft. @param roll Ship roll angle. @param trim Ship trim angle. @param yaw Ship yaw angle. Ussually you don't want to use this value. @return [disp, B, Cb], \n disp = Ship displacement [ton]. B = Bouyance center [m]. Cb = Block coefficient. @note Bouyance center will returned as FreeCAD.Vector class. @note Returned Bouyance center is in non modified ship coordinates """ # We will take a duplicate of ship shape in order to place it shape = ship.Shape.copy() shape.translate(Vector(0.0, 0.0, -draft)) # Rotations composition is Roll->Trim->Yaw shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim) shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw) # Now we need to know box dimensions bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax # Create the box L = xmax - xmin B = bbox.YMax - bbox.YMin p = Vector(-1.5 * L, -1.5 * B, bbox.ZMin - 1.0) box = Part.makeBox(3.0 * L, 3.0 * B, -bbox.ZMin + 1.0, p) vol = 0.0 cog = Vector() for solid in shape.Solids: # Compute common part with ship try: common = box.common(solid) except: continue # Get data vol = vol + common.Volume for s in common.Solids: sCoG = s.CenterOfMass cog.x = cog.x + sCoG.x * s.Volume cog.y = cog.y + sCoG.y * s.Volume cog.z = cog.z + sCoG.z * s.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol Vol = L * B * abs(bbox.ZMin) # Undo transformations B = Vector() B.x = cog.x * math.cos(math.radians(-yaw)) - cog.y * math.sin( math.radians(-yaw)) B.y = cog.x * math.sin(math.radians(-yaw)) + cog.y * math.cos( math.radians(-yaw)) B.z = cog.z cog.x = B.x * math.cos(math.radians(-trim)) - B.z * math.sin( math.radians(-trim)) cog.y = B.y cog.z = B.x * math.sin(math.radians(-trim)) + B.z * math.cos( math.radians(-trim)) B.x = cog.x B.y = cog.y * math.cos(math.radians(-roll)) - cog.z * math.sin( math.radians(-roll)) B.z = cog.y * math.sin(math.radians(-roll)) + cog.z * math.cos( math.radians(-roll)) B.z = B.z + draft # Return data dens = 1.025 # [tons/m3], salt water return [dens * vol, B, vol / Vol]
def displacement(ship, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship displacement Position arguments: ship -- Ship object (see createShip) Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned values: disp -- The ship displacement (a density of the water of 1025 kg/m^3 is assumed) B -- Bouyance application point, i.e. Center of mass of the underwater side Cb -- Block coefficient The Bouyance center is referred to the original ship position. """ if draft is None: draft = ship.Draft shape, base_z = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) vol = 0.0 cog = Vector() if len(shape.Solids) > 0: for solid in shape.Solids: vol += solid.Volume sCoG = solid.CenterOfMass cog.x = cog.x + sCoG.x * solid.Volume cog.y = cog.y + sCoG.y * solid.Volume cog.z = cog.z + sCoG.z * solid.Volume cog.x = cog.x / vol cog.y = cog.y / vol cog.z = cog.z / vol bbox = shape.BoundBox Vol = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin) * abs(bbox.ZMin) # Undo the transformations on the bouyance point B = Part.Point(Vector(cog.x, cog.y, cog.z)) m = Matrix() m.move(Vector(0.0, 0.0, draft)) m.move(Vector(-draft * math.sin(trim.getValueAs("rad")), 0.0, 0.0)) m.rotateY(trim.getValueAs("rad")) m.move(Vector(0.0, -draft * math.sin(roll.getValueAs("rad")), base_z)) m.rotateX(-roll.getValueAs("rad")) B.transform(m) try: cb = vol / Vol except ZeroDivisionError: msg = QtGui.QApplication.translate( "ship_console", "ZeroDivisionError: Null volume found during the displacement" " computation!", None) App.Console.PrintError(msg + '\n') cb = 0.0 # Return the computed data return (DENS * Units.Quantity(vol, Units.Volume), Vector(B.X, B.Y, B.Z), cb)