{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import cadquery as cq\n", "from jupyter_cadquery import (\n", " show, close_viewer, close_viewers, get_viewer, open_viewer, set_defaults, get_defaults\n", ")\n", "\n", "from jupyter_cadquery.replay import replay, enable_replay, disable_replay, reset_replay" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "set_defaults(axes=False, timeit=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "enable_replay(show_bbox=True, warning=False)\n", "show_object = replay" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex001 Simple Block" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "length = 80.0 # Length of the block\n", "height = 60.0 # Height of the block\n", "thickness = 10.0 # Thickness of the block\n", "\n", "# Create a 3D block based on the dimension variables above.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "result = cq.Workplane(\"XY\").box(length, height, thickness)\n", "\n", "# The following method is now outdated, but can still be used to display the\n", "# results of the script if you want\n", "# from Helpers import show\n", "# show(result) # Render the result of this script\n", "\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex002 Block With Bored Center Hole" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "length = 80.0 # Length of the block\n", "height = 60.0 # Height of the block\n", "thickness = 10.0 # Thickness of the block\n", "center_hole_dia = 22.0 # Diameter of center hole in block\n", "\n", "# Create a block based on the dimensions above and add a 22mm center hole.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "# 2. The highest (max) Z face is selected and a new workplane is created on it.\n", "# 3. The new workplane is used to drill a hole through the block.\n", "# 3a. The hole is automatically centered in the workplane.\n", "result = (\n", " cq.Workplane(\"XY\")\n", " .box(length, height, thickness)\n", " .faces(\">Z\")\n", " .workplane()\n", " .hole(center_hole_dia)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result, show_bbox=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex003 Pillow Block With Counterbored Holes" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "length = 80.0 # Length of the block\n", "width = 100.0 # Width of the block\n", "thickness = 10.0 # Thickness of the block\n", "center_hole_dia = 22.0 # Diameter of center hole in block\n", "cbore_hole_diameter = 2.4 # Bolt shank/threads clearance hole diameter\n", "cbore_inset = 12.0 # How far from the edge the cbored holes are set\n", "cbore_diameter = 4.4 # Bolt head pocket hole diameter\n", "cbore_depth = 2.1 # Bolt head pocket hole depth\n", "\n", "# Create a 3D block based on the dimensions above and add a 22mm center hold\n", "# and 4 counterbored holes for bolts\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "# 2. The highest(max) Z face is selected and a new workplane is created on it.\n", "# 3. The new workplane is used to drill a hole through the block.\n", "# 3a. The hole is automatically centered in the workplane.\n", "# 4. The highest(max) Z face is selected and a new workplane is created on it.\n", "# 5. A for-construction rectangle is created on the workplane based on the\n", "# block's overall dimensions.\n", "# 5a. For-construction objects are used only to place other geometry, they\n", "# do not show up in the final displayed geometry.\n", "# 6. The vertices of the rectangle (corners) are selected, and a counter-bored\n", "# hole is placed at each of the vertices (all 4 of them at once).\n", "result = (\n", " cq.Workplane(\"XY\")\n", " .box(length, width, thickness)\n", " .faces(\">Z\")\n", " .workplane()\n", " .hole(center_hole_dia)\n", " .faces(\">Z\")\n", " .workplane()\n", " .rect(length - cbore_inset, width - cbore_inset, forConstruction=True)\n", " .vertices()\n", " .cboreHole(cbore_hole_diameter, cbore_diameter, cbore_depth)\n", " .edges(\"|Z\")\n", " .fillet(2.0)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result, show_result=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex004 Extruded Cylindrical Plate" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "circle_radius = 50.0 # Radius of the plate\n", "thickness = 13.0 # Thickness of the plate\n", "rectangle_width = 13.0 # Width of rectangular hole in cylindrical plate\n", "rectangle_length = 19.0 # Length of rectangular hole in cylindrical plate\n", "\n", "# Extrude a cylindrical plate with a rectangular hole in the middle of it.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. The 2D geometry for the outer circle is created at the same time as the\n", "# rectangle that will create the hole in the center.\n", "# 2a. The circle and the rectangle will be automatically centered on the\n", "# workplane.\n", "# 2b. Unlike some other functions like the hole(), circle() takes\n", "# a radius and not a diameter.\n", "# 3. The circle and rectangle are extruded together, creating a cylindrical\n", "# plate with a rectangular hole in the center.\n", "# 3a. circle() and rect() could be changed to any other shape to completely\n", "# change the resulting plate and/or the hole in it.\n", "result = (\n", " cq.Workplane(\"front\")\n", " .circle(circle_radius)\n", " .rect(rectangle_width, rectangle_length)\n", " .extrude(thickness)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result, show_bbox=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex005 Extruded Lines and Arcs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "width = 2.0 # Overall width of the plate\n", "thickness = 0.25 # Thickness of the plate\n", "\n", "# Extrude a plate outline made of lines and an arc\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Draws a line from the origin to an X position of the plate's width.\n", "# 2a. The starting point of a 2D drawing like this will be at the center of the\n", "# workplane (0, 0) unless the moveTo() function moves the starting point.\n", "# 3. A line is drawn from the last position straight up in the Y direction\n", "# 1.0 millimeters.\n", "# 4. An arc is drawn from the last point, through point (1.0, 1.5) which is\n", "# half-way back to the origin in the X direction and 0.5 mm above where\n", "# the last line ended at. The arc then ends at (0.0, 1.0), which is 1.0 mm\n", "# above (in the Y direction) where our first line started from.\n", "# 5. An arc is drawn from the last point that ends on (-0.5, 1.0), the sag of\n", "# the curve 0.2 determines that the curve is concave with the midpoint 0.1 mm\n", "# from the arc baseline. If the sag was -0.2 the arc would be convex.\n", "# This convention is valid when the profile is drawn counterclockwise.\n", "# The reverse is true if the profile is drawn clockwise.\n", "# Clockwise: +sag => convex, -sag => concave\n", "# Counterclockwise: +sag => concave, -sag => convex\n", "# 6. An arc is drawn from the last point that ends on (-0.7, -0.2), the arc is\n", "# determined by the radius of -1.5 mm.\n", "# Clockwise: +radius => convex, -radius => concave\n", "# Counterclockwise: +radius => concave, -radius => convex\n", "# 7. close() is called to automatically draw the last line for us and close\n", "# the sketch so that it can be extruded.\n", "# 7a. Without the close(), the 2D sketch will be left open and the extrude\n", "# operation will provide unpredictable results.\n", "# 8. The 2D sketch is extruded into a solid object of the specified thickness.\n", "result = (\n", " cq.Workplane(\"front\")\n", " .lineTo(width, 0)\n", " .lineTo(width, 1.0)\n", " .threePointArc((1.0, 1.5), (0.0, 1.0))\n", " .sagittaArc((-0.5, 1.0), 0.2)\n", " .radiusArc((-0.7, -0.2), -1.5)\n", " .close()\n", " .extrude(thickness)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result, show_result=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex006 Moving the Current Working Point" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "circle_radius = 3.0 # The outside radius of the plate\n", "thickness = 0.25 # The thickness of the plate\n", "\n", "# Make a plate with two cutouts in it by moving the workplane center point\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 1b. The initial workplane center point is the center of the circle, at (0,0).\n", "# 2. A circle is created at the center of the workplane\n", "# 2a. Notice that circle() takes a radius and not a diameter\n", "result = cq.Workplane(\"front\").circle(circle_radius)\n", "\n", "# 3. The work center is movide to (1.5, 0.0) by calling center().\n", "# 3a. The new center is specified relative to the previous center,not\n", "# relative to global coordinates.\n", "# 4. A 0.5mm x 0.5mm 2D square is drawn inside the circle.\n", "# 4a. The plate has not been extruded yet, only 2D geometry is being created.\n", "result = result.center(1.5, 0.0).rect(0.5, 0.5)\n", "\n", "# 5. The work center is moved again, this time to (-1.5, 1.5).\n", "# 6. A 2D circle is created at that new center with a radius of 0.25mm.\n", "result = result.center(-1.5, 1.5).circle(0.25)\n", "\n", "# 7. All 2D geometry is extruded to the specified thickness of the plate.\n", "# 7a. The small circle and the square are enclosed in the outer circle of the\n", "# plate and so it is assumed that we want them to be cut out of the plate.\n", "# A separate cut operation is not needed.\n", "result = result.extrude(thickness)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex007 Using Point Lists" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "plate_radius = 2.0 # The radius of the plate that will be extruded\n", "hole_pattern_radius = 0.25 # Radius of circle where the holes will be placed\n", "thickness = 0.125 # The thickness of the plate that will be extruded\n", "\n", "# Make a plate with 4 holes in it at various points in a polar arrangement from\n", "# the center of the workplane.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. A 2D circle is drawn that will become though outer profile of the plate.\n", "r = cq.Workplane(\"front\").circle(plate_radius)\n", "\n", "# 3. Push 4 points on the stack that will be used as the center points of the\n", "# holes.\n", "r = r.pushPoints([(1.5, 0), (0, 1.5), (-1.5, 0), (0, -1.5)])\n", "\n", "# 4. This circle() call will operate on all four points, putting a circle at\n", "# each one.\n", "r = r.circle(hole_pattern_radius)\n", "\n", "# 5. All 2D geometry is extruded to the specified thickness of the plate.\n", "# 5a. The small hole circles are enclosed in the outer circle of the plate and\n", "# so it is assumed that we want them to be cut out of the plate. A\n", "# separate cut operation is not needed.\n", "result = r.extrude(thickness)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex008 Polygon Creation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "width = 3.0 # The width of the plate\n", "height = 4.0 # The height of the plate\n", "thickness = 0.25 # The thickness of the plate\n", "polygon_sides = 6 # The number of sides that the polygonal holes should have\n", "polygon_dia = 1.0 # The diameter of the circle enclosing the polygon points\n", "\n", "# Create a plate with two polygons cut through it\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. A 3D box is created in one box() operation to represent the plate.\n", "# 2a. The box is centered around the origin, which creates a result that may\n", "# be unituitive when the polygon cuts are made.\n", "# 3. 2 points are pushed onto the stack and will be used as centers for the\n", "# polygonal holes.\n", "# 4. The two polygons are created, on for each point, with one call to\n", "# polygon() using the number of sides and the circle that bounds the\n", "# polygon.\n", "# 5. The polygons are cut thru all objects that are in the line of extrusion.\n", "# 5a. A face was not selected, and so the polygons are created on the\n", "# workplane. Since the box was centered around the origin, the polygons end\n", "# up being in the center of the box. This makes them cut from the center to\n", "# the outside along the normal (positive direction).\n", "# 6. The polygons are cut through all objects, starting at the center of the\n", "# box/plate and going \"downward\" (opposite of normal) direction. Functions\n", "# like cutBlind() assume a positive cut direction, but cutThruAll() assumes\n", "# instead that the cut is made from a max direction and cuts downward from\n", "# that max through all objects.\n", "result = (\n", " cq.Workplane(\"front\")\n", " .box(width, height, thickness)\n", " .pushPoints([(0, 0.75), (0, -0.75)])\n", " .polygon(polygon_sides, polygon_dia)\n", " .cutThruAll()\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex009 Polylines" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These can be modified rather than hardcoding values for each dimension.\n", "# Define up our Length, Height, Width, and thickness of the beam\n", "(L, H, W, t) = (100.0, 20.0, 20.0, 1.0)\n", "\n", "# Define the points that the polyline will be drawn to/thru\n", "pts = [\n", " (0, H / 2.0),\n", " (W / 2.0, H / 2.0),\n", " (W / 2.0, (H / 2.0 - t)),\n", " (t / 2.0, (H / 2.0 - t)),\n", " (t / 2.0, (t - H / 2.0)),\n", " (W / 2.0, (t - H / 2.0)),\n", " (W / 2.0, H / -2.0),\n", " (0, H / -2.0),\n", "]\n", "\n", "# We generate half of the I-beam outline and then mirror it to create the full\n", "# I-beam.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. moveTo() is used to move the first point from the origin (0, 0) to\n", "# (0, 10.0), with 10.0 being half the height (H/2.0). If this is not done\n", "# the first line will start from the origin, creating an extra segment that\n", "# will cause the extrude to have an invalid shape.\n", "# 3. The polyline function takes a list of points and generates the lines\n", "# through all the points at once.\n", "# 3. Only half of the I-beam profile has been drawn so far. That half is\n", "# mirrored around the Y-axis to create the complete I-beam profile.\n", "# 4. The I-beam profile is extruded to the final length of the beam.\n", "result = cq.Workplane(\"front\").polyline(pts).mirrorY().extrude(L)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex010 Defining an Edge with a Spline" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1. Establishes a workplane to create the spline on to extrude.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "s = cq.Workplane(\"XY\")\n", "\n", "# The points that the spline will pass through\n", "sPnts = [\n", " (2.75, 1.5),\n", " (2.5, 1.75),\n", " (2.0, 1.5),\n", " (1.5, 1.0),\n", " (1.0, 1.25),\n", " (0.5, 1.0),\n", " (0, 1.0),\n", "]\n", "\n", "# 2. Generate our plate with the spline feature and make sure it is a\n", "# closed entity\n", "r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts, includeCurrent=True).close()\n", "\n", "# 3. Extrude to turn the wire into a plate\n", "result = r.extrude(0.5)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex011 Mirroring Symmetric Geometry" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. A horizontal line is drawn on the workplane with the hLine function.\n", "# 2a. 1.0 is the distance, not coordinate. hLineTo allows using xCoordinate\n", "# not distance.\n", "r = cq.Workplane(\"front\").hLine(1.0)\n", "\n", "# 3. Draw a series of vertical and horizontal lines with the vLine and hLine\n", "# functions.\n", "r = r.vLine(0.5).hLine(-0.25).vLine(-0.25).hLineTo(0.0)\n", "\n", "# 4. Mirror the geometry about the Y axis and extrude it into a 3D object.\n", "result = r.mirrorY().extrude(0.25)\n", "\n", "# Displays the result of this script\n", "show_object(result, show_bbox=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex012 Creating Workplanes on Faces" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Creates a 3D box that will have a hole placed in it later.\n", "result = cq.Workplane(\"front\").box(2, 3, 0.5)\n", "\n", "# 3. Find the top-most face with the >Z max selector.\n", "# 3a. Establish a new workplane to build geometry on.\n", "# 3b. Create a hole down into the box.\n", "result = result.faces(\">Z\").workplane().hole(0.5)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex013 Locating a Workplane on a Vertex" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Creates a 3D box that will have a hole placed in it later.\n", "result = cq.Workplane(\"front\").box(3, 2, 0.5)\n", "\n", "# 3. Select the lower left vertex and make a workplane.\n", "# 3a. The top-most Z face is selected using the >Z selector.\n", "# 3b. The lower-left vertex of the faces is selected with the Z\").vertices(\"Z\")\n", " .workplane()\n", " .transformed(offset=(0, -1.5, 1.0), rotate=(60, 0, 0))\n", " .rect(1.5, 1.5, forConstruction=True)\n", " .vertices()\n", " .hole(0.25)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex016 Using Construction Geometry" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a block with holes in each corner of a rectangle on that workplane.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects the top-most Z face of the box.\n", "# 4. Creates a new workplane to build new geometry on.\n", "# 5. Creates a for-construction rectangle that only exists to use for placing\n", "# other geometry.\n", "# 6. Selects the vertices of the for-construction rectangle.\n", "# 7. Places holes at the center of each selected vertex.\n", "result = (\n", " cq.Workplane(\"front\")\n", " .box(2, 2, 0.5)\n", " .faces(\">Z\")\n", " .workplane()\n", " .rect(1.5, 1.5, forConstruction=True)\n", " .vertices()\n", " .hole(0.125)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex017 Shelling to Create Thin Features" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a hollow box that's open on both ends with a thin wall.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects faces with normal in +z direction.\n", "# 4. Create a shell by cutting out the top-most Z face.\n", "result = cq.Workplane(\"front\").box(2, 2, 2).faces(\"+Z\").shell(0.05)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex018 Making Lofts" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a lofted section between a rectangle and a circular section.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the named plane orientation \"front\" to define the workplane, meaning\n", "# that the positive Z direction is \"up\", and the negative Z direction\n", "# is \"down\".\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects the top-most Z face of the box.\n", "# 4. Draws a 2D circle at the center of the the top-most face of the box.\n", "# 5. Creates a workplane 3 mm above the face the circle was drawn on.\n", "# 6. Draws a 2D circle on the new, offset workplane.\n", "# 7. Creates a loft between the circle and the rectangle.\n", "result = (\n", " cq.Workplane(\"front\")\n", " .box(4.0, 4.0, 0.25)\n", " .faces(\">Z\")\n", " .circle(1.5)\n", " .workplane(offset=3.0)\n", " .rect(0.75, 0.5)\n", " .loft(combine=True)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex019 Counter Sunk Holes" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a plate with 4 counter-sunk holes in it.\n", "# 1. Establishes a workplane using an XY object instead of a named plane.\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects the top-most face of the box and established a workplane on that.\n", "# 4. Draws a for-construction rectangle on the workplane which only exists for\n", "# placing other geometry.\n", "# 5. Selects the corner vertices of the rectangle and places a counter-sink\n", "# hole, using each vertex as the center of a hole using the cskHole()\n", "# function.\n", "# 5a. When the depth of the counter-sink hole is set to None, the hole will be\n", "# cut through.\n", "result = (\n", " cq.Workplane(cq.Plane.XY())\n", " .box(4, 2, 0.5)\n", " .faces(\">Z\")\n", " .workplane()\n", " .rect(3.5, 1.5, forConstruction=True)\n", " .vertices()\n", " .cskHole(0.125, 0.25, 82.0, depth=None)\n", ")\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex020 Rounding Corners with Fillets" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a plate with 4 rounded corners in the Z-axis.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects all edges that are parallel to the Z axis.\n", "# 4. Creates fillets on each of the selected edges with the specified radius.\n", "result = cq.Workplane(\"XY\").box(3, 3, 0.5).edges(\"|Z\").fillet(0.125)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex021 Splitting an Object" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a simple block with a hole through it that we can split.\n", "# 1. Establishes a workplane that an object can be built on.\n", "# 1a. Uses the X and Y origins to define the workplane, meaning that the\n", "# positive Z direction is \"up\", and the negative Z direction is \"down\".\n", "# 2. Creates a plain box to base future geometry on with the box() function.\n", "# 3. Selects the top-most face of the box and establishes a workplane on it\n", "# that new geometry can be built on.\n", "# 4. Draws a 2D circle on the new workplane and then uses it to cut a hole\n", "# all the way through the box.\n", "c = cq.Workplane(\"XY\").box(1, 1, 1).faces(\">Z\").workplane().circle(0.25).cutThruAll()\n", "\n", "# 5. Selects the face furthest away from the origin in the +Y axis direction.\n", "# 6. Creates an offset workplane that is set in the center of the object.\n", "# 6a. One possible improvement to this script would be to make the dimensions\n", "# of the box variables, and then divide the Y-axis dimension by 2.0 and\n", "# use that to create the offset workplane.\n", "# 7. Uses the embedded workplane to split the object, keeping only the \"top\"\n", "# portion.\n", "result = c.faces(\">Y\").workplane(-0.5).split(keepTop=True)\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex022 Revolution" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The dimensions of the model. These can be modified rather than changing the\n", "# shape's code directly.\n", "rectangle_width = 10.0\n", "rectangle_length = 10.0\n", "angle_degrees = 360.0\n", "\n", "# Revolve a cylinder from a rectangle\n", "# Switch comments around in this section to try the revolve operation with different parameters\n", "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length, False).revolve()\n", "\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = cq.Workplane(\"XY\").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))\n", "# Displays the result of this script\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex023 Sweep" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Points we will use to create spline and polyline paths to sweep over\n", "pts = [(0, 1), (1, 2), (2, 4)]\n", "\n", "# Spline path generated from our list of points (tuples)\n", "path = cq.Workplane(\"XZ\").spline(pts)\n", "\n", "# Sweep a circle with a diameter of 1.0 units along the spline path we just created\n", "defaultSweep = cq.Workplane(\"XY\").circle(1.0).sweep(path)\n", "show_object(defaultSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sweep defaults to making a solid and not generating a Frenet solid. Setting Frenet to True helps prevent creep in\n", "# the orientation of the profile as it is being swept\n", "frenetShell = cq.Workplane(\"XY\").circle(1.0).sweep(path, makeSolid=True, isFrenet=True)\n", "show_object(frenetShell)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# We can sweep shapes other than circles\n", "defaultRect = cq.Workplane(\"XY\").rect(1.0, 1.0).sweep(path)\n", "show_object(defaultRect)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Switch to a polyline path, but have it use the same points as the spline\n", "path = cq.Workplane(\"XZ\").polyline(pts, includeCurrent=True)\n", "\n", "# Using a polyline path leads to the resulting solid having segments rather than a single swept outer face\n", "plineSweep = cq.Workplane(\"XY\").circle(1.0).sweep(path)\n", "show_object(plineSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Switch to an arc for the path\n", "path = cq.Workplane(\"XZ\").threePointArc((1.0, 1.5), (0.0, 1.0))\n", "\n", "# Use a smaller circle section so that the resulting solid looks a little nicer\n", "arcSweep = cq.Workplane(\"XY\").circle(0.5).sweep(path)\n", "show_object(arcSweep)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex024 Sweep With Multiple Sections" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# X axis line length 20.0\n", "path = cq.Workplane(\"XZ\").moveTo(-10, 0).lineTo(10, 0)\n", "\n", "# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0\n", "defaultSweep = (\n", " cq.Workplane(\"YZ\")\n", " .workplane(offset=-10.0)\n", " .circle(2.0)\n", " .workplane(offset=10.0)\n", " .circle(1.0)\n", " .workplane(offset=10.0)\n", " .circle(2.0)\n", " .sweep(path, multisection=True)\n", ")\n", "\n", "# We can sweep thrue different shapes\n", "recttocircleSweep = (\n", " cq.Workplane(\"YZ\")\n", " .workplane(offset=-10.0)\n", " .rect(2.0, 2.0)\n", " .workplane(offset=8.0)\n", " .circle(1.0)\n", " .workplane(offset=4.0)\n", " .circle(1.0)\n", " .workplane(offset=8.0)\n", " .rect(2.0, 2.0)\n", " .sweep(path, multisection=True)\n", ")\n", "\n", "circletorectSweep = (\n", " cq.Workplane(\"YZ\")\n", " .workplane(offset=-10.0)\n", " .circle(1.0)\n", " .workplane(offset=7.0)\n", " .rect(2.0, 2.0)\n", " .workplane(offset=6.0)\n", " .rect(2.0, 2.0)\n", " .workplane(offset=7.0)\n", " .circle(1.0)\n", " .sweep(path, multisection=True)\n", ")\n", "\n", "\n", "# Placement of the Shape is important otherwise could produce unexpected shape\n", "specialSweep = (\n", " cq.Workplane(\"YZ\")\n", " .circle(1.0)\n", " .workplane(offset=10.0)\n", " .rect(2.0, 2.0)\n", " .sweep(path, multisection=True)\n", ")\n", "\n", "# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0\n", "path = (\n", " cq.Workplane(\"XZ\")\n", " .moveTo(-5, 4)\n", " .lineTo(0, 4)\n", " .threePointArc((4, 0), (0, -4))\n", " .lineTo(-5, -4)\n", ")\n", "\n", "# Placement of different shapes should follow the path\n", "# cylinder r=1.5 along first line\n", "# then sweep allong arc from r=1.5 to r=1.0\n", "# then cylinder r=1.0 along last line\n", "arcSweep = (\n", " cq.Workplane(\"YZ\")\n", " .workplane(offset=-5)\n", " .moveTo(0, 4)\n", " .circle(1.5)\n", " .workplane(offset=5, centerOption=\"CenterOfMass\")\n", " .circle(1.5)\n", " .moveTo(0, -8)\n", " .circle(1.0)\n", " .workplane(offset=-5, centerOption=\"CenterOfMass\")\n", " .circle(1.0)\n", " .sweep(path, multisection=True)\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Translate the resulting solids so that they do not overlap and display them left to right\n", "show_object(defaultSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_object(circletorectSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_object(recttocircleSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_object(specialSweep)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_object(arcSweep)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex025 Swept Helix" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "r = 0.5 # Radius of the helix\n", "p = 0.4 # Pitch of the helix - vertical distance between loops\n", "h = 2.4 # Height of the helix - total height\n", "\n", "# Helix\n", "wire = cq.Wire.makeHelix(pitch=p, height=h, radius=r)\n", "helix = cq.Workplane(obj=wire)\n", "\n", "# Final result: A 2D shape swept along a helix.\n", "result = (\n", " cq.Workplane(\"XZ\") # helix is moving up the Z axis\n", " .center(r, 0) # offset isosceles trapezoid\n", " .polyline(((-0.15, 0.1), (0.0, 0.05), (0, 0.35), (-0.15, 0.3)))\n", " .close() # make edges a wire\n", " .sweep(helix, isFrenet=True) # Frenet keeps orientation as expected\n", ")\n", "\n", "show_object(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex100 Lego Brick" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#####\n", "# Inputs\n", "######\n", "lbumps = 4 # number of bumps long\n", "wbumps = 2 # number of bumps wide\n", "thin = True # True for thin, False for thick\n", "\n", "#\n", "# Lego Brick Constants-- these make a lego brick a lego :)\n", "#\n", "pitch = 8.0\n", "clearance = 0.1\n", "bumpDiam = 4.8\n", "bumpHeight = 1.8\n", "if thin:\n", " height = 3.2\n", "else:\n", " height = 9.6\n", "\n", "t = (pitch - (2 * clearance) - bumpDiam) / 2.0\n", "postDiam = pitch - t # works out to 6.5\n", "total_length = lbumps * pitch - 2.0 * clearance\n", "total_width = wbumps * pitch - 2.0 * clearance\n", "\n", "# make the base\n", "s = cq.Workplane(\"XY\").box(total_length, total_width, height)\n", "\n", "# shell inwards not outwards\n", "s = s.faces(\"Z\")\n", " .workplane()\n", " .rarray(pitch, pitch, lbumps, wbumps, True)\n", " .circle(bumpDiam / 2.0)\n", " .extrude(bumpHeight)\n", ")\n", "\n", "# add posts on the bottom. posts are different diameter depending on geometry\n", "# solid studs for 1 bump, tubes for multiple, none for 1x1\n", "tmp = s.faces(\" 1 and wbumps > 1:\n", " tmp = (\n", " tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True)\n", " .circle(postDiam / 2.0)\n", " .circle(bumpDiam / 2.0)\n", " .extrude(height - t)\n", " )\n", "elif lbumps > 1:\n", " tmp = (\n", " tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True)\n", " .circle(t)\n", " .extrude(height - t)\n", " )\n", "elif wbumps > 1:\n", " tmp = (\n", " tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True)\n", " .circle(t)\n", " .extrude(height - t)\n", " )\n", "else:\n", " tmp = s\n", "\n", "# Render the solid\n", "show_object(tmp, show_bbox=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ex101 InterpPlate" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import sin, cos, pi, sqrt\n", "\n", "# TEST_1\n", "# example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface.\n", "thickness = 0\n", "edge_points = [(0.0, 0.0, 0.0), (0.0, 10.0, 0.0), (0.0, 10.0, 10.0), (0.0, 0.0, 10.0)]\n", "surface_points = [(5.0, 5.0, 5.0)]\n", "plate_0 = cq.Workplane(\"XY\").interpPlate(edge_points, surface_points, thickness)\n", "print(\"plate_0.val().Volume() = \", plate_0.val().Volume())\n", "# plate_0 = plate_0.translate((0, 6 * 12, 0))\n", "show_object(plate_0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# EXAMPLE 1\n", "# Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides\n", "thickness = 0.1\n", "edge_points = [\n", " (-7.0, -7.0, 0.0),\n", " (-3.0, -10.0, 3.0),\n", " (7.0, -7.0, 0.0),\n", " (7.0, 7.0, 0.0),\n", " (-7.0, 7.0, 0.0),\n", "]\n", "edge_wire = cq.Workplane(\"XY\").polyline(\n", " [(-7.0, -7.0), (7.0, -7.0), (7.0, 7.0), (-7.0, 7.0)]\n", ")\n", "# edge_wire = edge_wire.add(cq.Workplane(\"YZ\").workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)]))\n", "# In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire.\n", "edge_wire = edge_wire.add(\n", " cq.Workplane(\"YZ\")\n", " .workplane()\n", " .transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0))\n", " .spline([(-7.0, 0.0), (3, -3), (7.0, 0.0)])\n", ")\n", "surface_points = [(-3.0, -3.0, -3.0), (3.0, 3.0, 3.0)]\n", "plate_1 = cq.Workplane(\"XY\").interpPlate(edge_wire, surface_points, thickness)\n", "# plate_1 = cq.Workplane(\"XY\").interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges\n", "print(\"plate_1.val().Volume() = \", plate_1.val().Volume())\n", "show_object(plate_1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# EXAMPLE 2\n", "# Embossed star, need to change optional parameters to obtain nice looking result.\n", "r1 = 3.0\n", "r2 = 10.0\n", "fn = 6\n", "thickness = 0.1\n", "edge_points = [\n", " (r1 * cos(i * pi / fn), r1 * sin(i * pi / fn))\n", " if i % 2 == 0\n", " else (r2 * cos(i * pi / fn), r2 * sin(i * pi / fn))\n", " for i in range(2 * fn + 1)\n", "]\n", "edge_wire = cq.Workplane(\"XY\").polyline(edge_points)\n", "r2 = 4.5\n", "surface_points = [\n", " (r2 * cos(i * pi / fn), r2 * sin(i * pi / fn), 1.0) for i in range(2 * fn)\n", "] + [(0.0, 0.0, -2.0)]\n", "plate_2 = cq.Workplane(\"XY\").interpPlate(\n", " edge_wire,\n", " surface_points,\n", " thickness,\n", " combine=True,\n", " clean=True,\n", " degree=3,\n", " nbPtsOnCur=15,\n", " nbIter=2,\n", " anisotropy=False,\n", " tol2d=0.00001,\n", " tol3d=0.0001,\n", " tolAng=0.01,\n", " tolCurv=0.1,\n", " maxDeg=8,\n", " maxSegments=49,\n", ")\n", "# plate_2 = cq.Workplane(\"XY\").interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges\n", "print(\"plate_2.val().Volume() = \", plate_2.val().Volume())\n", "plate_2 = plate_2\n", "show_object(plate_2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# EXAMPLE 3\n", "# Points on hexagonal pattern coordinates, use of pushpoints.\n", "r1 = 1.0\n", "N = 3\n", "ca = cos(30.0 * pi / 180.0)\n", "sa = sin(30.0 * pi / 180.0)\n", "# EVEN ROWS\n", "pts = [\n", " (-3.0, -3.0),\n", " (-1.267949, -3.0),\n", " (0.464102, -3.0),\n", " (2.196152, -3.0),\n", " (-3.0, 0.0),\n", " (-1.267949, 0.0),\n", " (0.464102, 0.0),\n", " (2.196152, 0.0),\n", " (-2.133974, -1.5),\n", " (-0.401923, -1.5),\n", " (1.330127, -1.5),\n", " (3.062178, -1.5),\n", " (-2.133975, 1.5),\n", " (-0.401924, 1.5),\n", " (1.330127, 1.5),\n", " (3.062178, 1.5),\n", "]\n", "# Spike surface\n", "thickness = 0.1\n", "fn = 6\n", "edge_points = [\n", " (\n", " r1 * cos(i * 2 * pi / fn + 30 * pi / 180),\n", " r1 * sin(i * 2 * pi / fn + 30 * pi / 180),\n", " )\n", " for i in range(fn + 1)\n", "]\n", "surface_points = [\n", " (\n", " r1 / 4 * cos(i * 2 * pi / fn + 30 * pi / 180),\n", " r1 / 4 * sin(i * 2 * pi / fn + 30 * pi / 180),\n", " 0.75,\n", " )\n", " for i in range(fn + 1)\n", "] + [(0, 0, 2)]\n", "edge_wire = cq.Workplane(\"XY\").polyline(edge_points)\n", "plate_3 = (\n", " cq.Workplane(\"XY\")\n", " .pushPoints(pts)\n", " .interpPlate(\n", " edge_wire,\n", " surface_points,\n", " thickness,\n", " combine=False,\n", " clean=False,\n", " degree=2,\n", " nbPtsOnCur=20,\n", " nbIter=2,\n", " anisotropy=False,\n", " tol2d=0.00001,\n", " tol3d=0.0001,\n", " tolAng=0.01,\n", " tolCurv=0.1,\n", " maxDeg=8,\n", " maxSegments=9,\n", " )\n", ")\n", "print(\"plate_3.val().Volume() = \", plate_3.val().Volume())\n", "plate_3 = plate_3\n", "show_object(plate_3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# EXAMPLE 4\n", "# Gyroïd, all edges are splines on different workplanes.\n", "thickness = 0.1\n", "edge_points = [\n", " [[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]],\n", " [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]],\n", " [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]],\n", " [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]],\n", " [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]],\n", " [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]],\n", "]\n", "plane_list = [\"XZ\", \"XY\", \"YZ\", \"XZ\", \"YZ\", \"XY\"]\n", "offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54]\n", "edge_wire = (\n", " cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0])\n", ")\n", "for i in range(len(edge_points) - 1):\n", " edge_wire = edge_wire.add(\n", " cq.Workplane(plane_list[i + 1])\n", " .workplane(offset=-offset_list[i + 1])\n", " .spline(edge_points[i + 1])\n", " )\n", "surface_points = [(0, 0, 0)]\n", "plate_4 = cq.Workplane(\"XY\").interpPlate(edge_wire, surface_points, thickness)\n", "print(\"plate_4.val().Volume() = \", plate_4.val().Volume())\n", "plate_4 = plate_4\n", "show_object(plate_4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 4 }