YSMlearnsCode commited on
Commit
f1a45cb
·
1 Parent(s): 3c38be2

added updated vectorstore and added prompts

Browse files
generated/result_script.py CHANGED
@@ -1,88 +1,80 @@
1
  import FreeCAD as App
2
- import Part
 
3
  import math
4
- from FreeCAD import Vector, Placement, Rotation
5
 
6
- doc = App.newDocument()
7
 
8
- outer_diameter = 100.0
9
- inner_diameter = 50.0
10
- thickness = 10.0
11
- bolt_circle_diameter = 75.0
12
- num_bolt_holes = 6
13
- bolt_hole_diameter = 8.0
14
-
15
- overall_placement_vector = Vector(0, 0, 0)
16
- overall_rotation_axis = Vector(0, 0, 1)
17
- overall_rotation_angle = 0.0
18
-
19
- outer_radius = outer_diameter / 2
20
- inner_radius = inner_diameter / 2
21
- bolt_hole_radius = bolt_hole_diameter / 2
22
-
23
- obj_base = doc.addObject("Part::Cylinder", "FlangeBase")
24
- obj_base.Radius = outer_radius
25
- obj_base.Height = thickness
26
- obj_base.Placement = Placement(Vector(0, 0, 0))
27
-
28
- obj_center_hole = doc.addObject("Part::Cylinder", "CenterHole")
29
- obj_center_hole.Radius = inner_radius
30
- obj_center_hole.Height = thickness
31
- obj_center_hole.Placement = Placement(Vector(0, 0, 0))
32
-
33
- bolt_hole_objects = []
34
- for i in range(num_bolt_holes):
35
- angle = 2 * math.pi * i / num_bolt_holes
36
- x = bolt_circle_diameter / 2 * math.cos(angle)
37
- y = bolt_circle_diameter / 2 * math.sin(angle)
38
-
39
- obj_bolt_hole = doc.addObject("Part::Cylinder", f"BoltHole_{i+1}")
40
- obj_bolt_hole.Radius = bolt_hole_radius
41
- obj_bolt_hole.Height = thickness
42
- obj_bolt_hole.Placement = Placement(Vector(x, y, 0))
43
- bolt_hole_objects.append(obj_bolt_hole)
44
-
45
- doc.recompute()
46
-
47
- obj_cut_center = doc.addObject("Part::Cut", "FlangeWithCenterHole")
48
- obj_cut_center.Base = obj_base
49
- obj_cut_center.Tool = obj_center_hole
50
- doc.recompute()
51
-
52
- obj_base.Visibility = False
53
- obj_center_hole.Visibility = False
54
-
55
- obj_fused_holes = doc.addObject("Part::MultiFuse", "FusedBoltHoles")
56
- obj_fused_holes.Objects = bolt_hole_objects
57
- doc.recompute()
58
-
59
- for hole_obj in bolt_hole_objects:
60
- hole_obj.Visibility = False
61
-
62
- obj_final_flange = doc.addObject("Part::Cut", "FinalFlange")
63
- obj_final_flange.Base = obj_cut_center
64
- obj_final_flange.Tool = obj_fused_holes
65
- doc.recompute()
66
-
67
- obj_cut_center.Visibility = False
68
- obj_fused_holes.Visibility = False
69
-
70
- final_placement = Placement(overall_placement_vector, Rotation(overall_rotation_axis, overall_rotation_angle))
71
- obj_final_flange.Placement = final_placement
72
-
73
- rod_length = 200.0
74
- rod_diameter = inner_diameter
75
- rod_radius = rod_diameter / 2
76
-
77
- rod_placement_z = thickness / 2 - rod_length / 2
78
-
79
- obj_rod = doc.addObject("Part::Cylinder", "Rod")
80
- obj_rod.Radius = rod_radius
81
- obj_rod.Height = rod_length
82
- obj_rod.Placement = Placement(Vector(0, 0, rod_placement_z))
83
-
84
- doc.recompute()
85
- App.ActiveDocument.ActiveView.fitAll()
86
 
87
 
88
  import FreeCADGui
 
1
  import FreeCAD as App
2
+ import FreeCADGui as Gui
3
+ from FreeCAD import Vector
4
  import math
 
5
 
 
6
 
7
+ def createFlangeAssembly():
8
+ doc = App.newDocument("Flange")
9
+
10
+ FLANGE_OUTER_DIAMETER = 100.0
11
+ FLANGE_THICKNESS = 7.5
12
+ BORE_INNER_DIAMETER = 50.0
13
+ NECK_HEIGHT = 15.0
14
+ NECK_OUTER_DIAMETER = 60.0
15
+ NUM_BOLT_HOLES = 6
16
+ BOLT_HOLE_DIAMETER = 12.0
17
+ PCD = 75.0
18
+
19
+ total_height = FLANGE_THICKNESS + NECK_HEIGHT
20
+
21
+ flange = doc.addObject("Part::Cylinder", "Flange")
22
+ flange.Radius = FLANGE_OUTER_DIAMETER / 2
23
+ flange.Height = FLANGE_THICKNESS
24
+
25
+ bore = doc.addObject("Part::Cylinder", "CentralBore")
26
+ bore.Radius = BORE_INNER_DIAMETER / 2
27
+ bore.Height = FLANGE_THICKNESS
28
+ bore_cut = doc.addObject("Part::Cut", "FlangeWithBore")
29
+ bore_cut.Base = flange
30
+ bore_cut.Tool = bore
31
+
32
+ neck_outer = doc.addObject("Part::Cylinder", "NeckOuter")
33
+ neck_outer.Radius = NECK_OUTER_DIAMETER / 2
34
+ neck_outer.Height = NECK_HEIGHT
35
+ neck_outer.Placement.Base = Vector(0, 0, FLANGE_THICKNESS)
36
+
37
+ neck_inner = doc.addObject("Part::Cylinder", "NeckInner")
38
+ neck_inner.Radius = BORE_INNER_DIAMETER / 2
39
+ neck_inner.Height = NECK_HEIGHT
40
+ neck_inner.Placement.Base = Vector(0, 0, FLANGE_THICKNESS)
41
+
42
+ neck_hollow = doc.addObject("Part::Cut", "HollowNeck")
43
+ neck_hollow.Base = neck_outer
44
+ neck_hollow.Tool = neck_inner
45
+
46
+ fused = doc.addObject("Part::Fuse", "FlangeAndNeck")
47
+ fused.Base = bore_cut
48
+ fused.Tool = neck_hollow
49
+
50
+ current_shape = fused
51
+ bolt_radius = BOLT_HOLE_DIAMETER / 2
52
+ bolt_circle_radius = PCD / 2
53
+
54
+ for i in range(NUM_BOLT_HOLES):
55
+ angle_deg = 360 * i / NUM_BOLT_HOLES
56
+ angle_rad = math.radians(angle_deg)
57
+ x = bolt_circle_radius * math.cos(angle_rad)
58
+ y = bolt_circle_radius * math.sin(angle_rad)
59
+
60
+ hole = doc.addObject("Part::Cylinder", f"BoltHole_{i+1:02d}")
61
+ hole.Radius = bolt_radius
62
+ hole.Height = total_height
63
+ hole.Placement.Base = Vector(x, y, 0)
64
+
65
+ cut = doc.addObject("Part::Cut", f"Cut_Bolt_{i+1:02d}")
66
+ cut.Base = current_shape
67
+ cut.Tool = hole
68
+ current_shape = cut
69
+
70
+ doc.recompute()
71
+ Gui.activeDocument().activeView().viewAxometric()
72
+ Gui.SendMsgToActiveView("ViewFit")
73
+
74
+ return doc
75
+
76
+ if __name__ == "__main__":
77
+ createFlangeAssembly()
 
 
 
 
 
 
 
78
 
79
 
80
  import FreeCADGui
prompts/base_instruction.txt CHANGED
@@ -28,4 +28,405 @@ You are an assistant that generates valid Python code for FreeCAD.
28
  For sweeping profiles along a path, use the makePipeShell() method on the path wire instead of a non-existent makeSweep(). Pass a list of profile wires, and control solid creation with parameters:
29
  pipe_shape = path_wire.makePipeShell([profile1, profile2, profile3], makeSolid=True, isFrenet=True)
30
  - Handling PipeShell Solid Creation Errors
31
- If sweeping results in BRepOffsetAPI_MakePipeShell::MakeSolid errors, try creating the pipe shell without forcing a solid first (set makeSolid=False) to debug geometry issues:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  For sweeping profiles along a path, use the makePipeShell() method on the path wire instead of a non-existent makeSweep(). Pass a list of profile wires, and control solid creation with parameters:
29
  pipe_shape = path_wire.makePipeShell([profile1, profile2, profile3], makeSolid=True, isFrenet=True)
30
  - Handling PipeShell Solid Creation Errors
31
+ If sweeping results in BRepOffsetAPI_MakePipeShell::MakeSolid errors, try creating the pipe shell without forcing a solid first (set makeSolid=False) to debug geometry issues:
32
+
33
+ -Use the following functions to make simple solids:
34
+ 1. makeBox(length,width,height,[pnt,dir]) #Description: Makes a box located at pnt with the dimensions (length,width,height). By default pnt is Vector(0,0,0) and dir is Vector(0,0,1)
35
+ 2. makeCircle(radius,[pnt,dir,angle1,angle2]) #Description: Makes a circle with a given radius. By default pnt is Vector(0,0,0), dir is Vector(0,0,1), angle1 is 0 and angle2 is 360.
36
+ 3. makeCone(radius1,radius2,height,[pnt,dir,angle]) #Description: Makes a cone with given radii and height. By default pnt is Vector(0,0,0), dir is Vector(0,0,1) and angle is 360
37
+ 4. makeCylinder(radius,height,[pnt,dir,angle]) #Description: Makes a cylinder with a given radius and height. By default pnt is Vector(0,0,0),dir is Vector(0,0,1) and angle is 360
38
+ 5. makeHelix(pitch,height,radius,[angle,lefthand,heightstyle]) #Description: Makes a helix shape with a given pitch, height and radius. Defaults to right-handed cylindrical helix. Non-zero angle parameter produces a conical helix. Lefthand True produces left handed helix. Heightstyle applies only to conical helices. Heightstyle False (default) will cause the height parameter to be interpreted as the length of the side of the underlying frustum. Heightstyle True will cause the height parameter to be interpreted as the vertical height of the helix. Pitch is "metric pitch" (advance/revolution). For conical helix, radius is the minor radius.
39
+ 6. makeLine((x1,y1,z1),(x2,y2,z2)) #Description: Makes a line of two points
40
+ 7. makeLoft(shapelist<profiles>,[boolean<solid>,boolean<ruled>]) #Description: Creates a loft shape using the list of profiles. Optionally make result a solid (vs surface/shell) or make result a ruled surface.
41
+ 8. makePlane(length,width,[pnt,dir]) #Description: Makes a plane. By default pnt is Vector(0,0,0) and dir is Vector(0,0,1)
42
+ 9. makePolygon(list) #Description: Makes a polygon of a list of Vectors
43
+ 10. makeRevolution(Curve,[vmin,vmax,angle,pnt,dir]) #Description: Makes a revolved shape by rotating the curve or a portion of it around an axis given by (pnt,dir). By default vmin/vmax are set to bounds of the curve,angle is 360,pnt is Vector(0,0,0) and dir is Vector(0,0,1)
44
+ 11. makeShell(list) #Description: Creates a shell out of a list of faces. Note: Resulting shell should be manifold. Non-manifold shells are not well supported.
45
+ 12. makeSolid(Part.Shape) #Description: Creates a solid out of the shells inside a shape.
46
+ 13. makeSphere(radius,[center_pnt, axis_dir, V_startAngle, V_endAngle, U_angle]) #Description: Makes a sphere (or partial sphere) with a given radius. By default center_pnt is Vector(0,0,0), axis_dir is Vector(0,0,1), V_startAngle is 0, V_endAngle is 90 and U_angle is 360
47
+ 14. makeTorus(radius1,radius2,[pnt,dir,angle1,angle2,angle]) #Description: Makes a torus with a given radii and angles. By default pnt is Vector(0,0,0),dir is Vector(0,0,1),angle1 is 0,angle2 is 360 and angle is 360
48
+ 15. makeTube(edge,float) #Description: Creates a tube.
49
+ - ensure all dimensions are correct and parts dont intersect each other
50
+ - Always use from FreeCAD import Vector instead of import FreeCAD.Vector when importing the Vector class in FreeCAD. This is the correct and preferred method.
51
+ - Always use from FreeCAD import Placement instead of import FreeCAD.Placement when importing the Placement class in FreeCAD. This is the correct and preferred method.
52
+ - Always use from FreeCAD import Rotation instead of import FreeCAD.Rotation when importing the Rotation class in FreeCAD. This is the correct and preferred method.
53
+ - use the fuse() function instead of Part.Union(). Use it only where necessary. Only when it is necessary to combine parts
54
+ - make the design tree as well for all parts. In the design tree, I must be able to change the dimension and placement of parts.
55
+ - dont make several features and then copy and make one feature at the end.
56
+ - Make the generated object visible
57
+ - try to make the design as modular as possible. for example, if i need to generate a cup, make the cup body as one feature in design tree and handle as other feature. in this way, i can edit the dimensions of both separately.
58
+ - There is no built-in Part.makeTube function in FreeCAD. You're trying to create a hollow cylinder (tube) directly with parameters like outer_radius, inner_radius, height, but FreeCAD expects shapes, not numbers.
59
+ - dont write comments
60
+ - When converting a Part.BSplineCurve to a wire, do not use .toWire() directly (it does not exist). Instead, convert the spline to an edge with .toShape() and wrap it inside Part.Wire([ ... ]).
61
+ Example:
62
+ wire = Part.Wire([bspline.toShape()])
63
+ - To create ellipse profiles, do not use Part.makeEllipse(). Instead, create a Part.Ellipse(), set the MajorRadius and MinorRadius, then convert it to a wire:
64
+ ellipse = Part.Ellipse()
65
+ ellipse.MajorRadius = width / 2
66
+ ellipse.MinorRadius = height / 2
67
+ wire = Part.Wire([ellipse.toShape()])
68
+ - Sweeping Profiles Along Paths
69
+ For sweeping profiles along a path, use the makePipeShell() method on the path wire instead of a non-existent makeSweep(). Pass a list of profile wires, and control solid creation with parameters:
70
+ pipe_shape = path_wire.makePipeShell([profile1, profile2, profile3], makeSolid=True, isFrenet=True)
71
+ - Handling PipeShell Solid Creation Errors
72
+ If sweeping results in BRepOffsetAPI_MakePipeShell::MakeSolid errors, try creating the pipe shell without forcing a solid first (set makeSolid=False) to debug geometry issues:
73
+ - Dont write comments in the generated code. reduce the number of used tokens
74
+
75
+ - Correct Usage of fuse() in FreeCAD-
76
+ When performing a union (boolean fuse) of multiple shapes in FreeCAD, always use the iterative .fuse() method on Part objects instead of Part.Union().
77
+
78
+ Correct Approach:
79
+
80
+ fan_final_shape = all_parts_to_fuse[0] # Start with the first shape
81
+ for shape in all_parts_to_fuse[1:]: # Iterate over remaining shapes
82
+ fan_final_shape = fan_final_shape.fuse(shape) # Fuse one by one
83
+ Avoid:
84
+
85
+ fan_final_shape = Part.Union(all_parts_to_fuse) # Incorrect method
86
+
87
+ - when asked to "make a flange of OD 100mm, bore size as 50mm, thickness 7.5mm. the height of the middle hollow neck must be 15mm. make 6 m12 holes at PCD 75mm", make the following code:
88
+ import FreeCAD as App
89
+ import FreeCADGui as Gui
90
+ from FreeCAD import Vector
91
+ import math
92
+
93
+
94
+ def createFlangeAssembly():
95
+ doc = App.newDocument("Flange")
96
+
97
+ # === Parameters ===
98
+ FLANGE_OUTER_DIAMETER = 100.0
99
+ FLANGE_THICKNESS = 7.5
100
+ BORE_INNER_DIAMETER = 50.0
101
+ NECK_HEIGHT = 15.0
102
+ NECK_OUTER_DIAMETER = 60.0
103
+ NUM_BOLT_HOLES = 6
104
+ BOLT_HOLE_DIAMETER = 12.0
105
+ PCD = 75.0
106
+
107
+ total_height = FLANGE_THICKNESS + NECK_HEIGHT
108
+
109
+ # === 1. Create flange base ===
110
+ flange = doc.addObject("Part::Cylinder", "Flange")
111
+ flange.Radius = FLANGE_OUTER_DIAMETER / 2
112
+ flange.Height = FLANGE_THICKNESS
113
+
114
+ # === 2. Cut central bore from flange ===
115
+ bore = doc.addObject("Part::Cylinder", "CentralBore")
116
+ bore.Radius = BORE_INNER_DIAMETER / 2
117
+ bore.Height = FLANGE_THICKNESS
118
+ bore_cut = doc.addObject("Part::Cut", "FlangeWithBore")
119
+ bore_cut.Base = flange
120
+ bore_cut.Tool = bore
121
+
122
+ # === 3. Create neck ===
123
+ neck_outer = doc.addObject("Part::Cylinder", "NeckOuter")
124
+ neck_outer.Radius = NECK_OUTER_DIAMETER / 2
125
+ neck_outer.Height = NECK_HEIGHT
126
+ neck_outer.Placement.Base = Vector(0, 0, FLANGE_THICKNESS)
127
+
128
+ neck_inner = doc.addObject("Part::Cylinder", "NeckInner")
129
+ neck_inner.Radius = BORE_INNER_DIAMETER / 2
130
+ neck_inner.Height = NECK_HEIGHT
131
+ neck_inner.Placement.Base = Vector(0, 0, FLANGE_THICKNESS)
132
+
133
+ neck_hollow = doc.addObject("Part::Cut", "HollowNeck")
134
+ neck_hollow.Base = neck_outer
135
+ neck_hollow.Tool = neck_inner
136
+
137
+ # === 4. Fuse flange (with central hole) and neck ===
138
+ fused = doc.addObject("Part::Fuse", "FlangeAndNeck")
139
+ fused.Base = bore_cut
140
+ fused.Tool = neck_hollow
141
+
142
+ # === 5. Cut bolt holes sequentially ===
143
+ current_shape = fused
144
+ bolt_radius = BOLT_HOLE_DIAMETER / 2
145
+ bolt_circle_radius = PCD / 2
146
+
147
+ for i in range(NUM_BOLT_HOLES):
148
+ angle_deg = 360 * i / NUM_BOLT_HOLES
149
+ angle_rad = math.radians(angle_deg)
150
+ x = bolt_circle_radius * math.cos(angle_rad)
151
+ y = bolt_circle_radius * math.sin(angle_rad)
152
+
153
+ hole = doc.addObject("Part::Cylinder", f"BoltHole_{i+1:02d}")
154
+ hole.Radius = bolt_radius
155
+ hole.Height = total_height
156
+ hole.Placement.Base = Vector(x, y, 0)
157
+
158
+ cut = doc.addObject("Part::Cut", f"Cut_Bolt_{i+1:02d}")
159
+ cut.Base = current_shape
160
+ cut.Tool = hole
161
+ current_shape = cut # update for next iteration
162
+
163
+ # === 6. Final result ===
164
+
165
+
166
+ # Recompute and fit view
167
+ doc.recompute()
168
+ Gui.activeDocument().activeView().viewAxometric()
169
+ Gui.SendMsgToActiveView("ViewFit")
170
+
171
+ return doc
172
+
173
+ if __name__ == "__main__":
174
+ createFlangeAssembly()
175
+
176
+ use this template whenever asked to make a flange
177
+
178
+ This is a good example for a teapot. Whenever asked to generate a teapot, make something similar:
179
+ import FreeCAD as App
180
+ import FreeCADGui as Gui
181
+ from FreeCAD import Vector, Placement, Rotation
182
+ import Part
183
+
184
+ # Teapot dimensions
185
+ BODY_BOTTOM_RADIUS = 50.0
186
+ BODY_MAX_RADIUS = 80.0
187
+ BODY_HEIGHT = 100.0
188
+ LID_OPENING_RADIUS = 35.0
189
+
190
+ # Spout parameters
191
+ SPOUT_ATTACH_HEIGHT = BODY_HEIGHT * 0.5 # 50.0
192
+ SPOUT_OFFSET_Y = BODY_MAX_RADIUS * 0.7 # 56.0
193
+ SPOUT_LENGTH_HORIZONTAL = 60.0
194
+ SPOUT_LENGTH_VERTICAL = 30.0
195
+ SPOUT_RADIUS = 7.0
196
+
197
+ # Handle parameters
198
+ HANDLE_ATTACH_TOP_HEIGHT = BODY_HEIGHT * 0.7 # 70.0
199
+ HANDLE_ATTACH_BOTTOM_HEIGHT = BODY_HEIGHT * 0.3 # 30.0
200
+ HANDLE_OFFSET_Y = -BODY_MAX_RADIUS * 0.7 # -56.0
201
+ HANDLE_RADIUS = 6.0
202
+
203
+ def createTeapot():
204
+ doc = App.newDocument("Teapot")
205
+
206
+ # --- 1. Body ---
207
+ body_profile_pts = [
208
+ Vector(BODY_BOTTOM_RADIUS, 0, 0),
209
+ Vector(BODY_MAX_RADIUS, 0, BODY_HEIGHT * 0.4),
210
+ Vector(BODY_MAX_RADIUS * 0.8, 0, BODY_HEIGHT * 0.7),
211
+ Vector(LID_OPENING_RADIUS, 0, BODY_HEIGHT)
212
+ ]
213
+ body_spline = Part.BSplineCurve(body_profile_pts)
214
+ body_edge = body_spline.toShape()
215
+ line1 = Part.LineSegment(Vector(LID_OPENING_RADIUS, 0, BODY_HEIGHT), Vector(0, 0, BODY_HEIGHT)).toShape()
216
+ line2 = Part.LineSegment(Vector(0, 0, BODY_HEIGHT), Vector(0, 0, 0)).toShape()
217
+ line3 = Part.LineSegment(Vector(0, 0, 0), Vector(BODY_BOTTOM_RADIUS, 0, 0)).toShape()
218
+ wire = Part.Wire([body_edge, line1, line2, line3])
219
+ face = Part.Face(wire)
220
+ body_solid = face.revolve(Vector(0, 0, 0), Vector(0, 0, 1), 360)
221
+
222
+ obj_body = doc.addObject("Part::Feature", "Body")
223
+ obj_body.Shape = body_solid
224
+ obj_body.ViewObject.ShapeColor = (0.9, 0.7, 0.7)
225
+
226
+ # --- 2. Lid ---
227
+ lid_profile_pts = [
228
+ Vector(36.0, 0, 0),
229
+ Vector(36.0, 0, 3.0),
230
+ Vector(35.0, 0, 3.0 + 20.0 * 0.2),
231
+ Vector(17.5, 0, 3.0 + 20.0 * 0.7),
232
+ Vector(10.0, 0, 3.0 + 20.0),
233
+ Vector(5.0, 0, 3.0 + 20.0 + 15.0 * 0.8),
234
+ Vector(0, 0, 3.0 + 20.0 + 15.0)
235
+ ]
236
+ lid_spline = Part.BSplineCurve(lid_profile_pts)
237
+ lid_edge = lid_spline.toShape()
238
+ line1 = Part.LineSegment(Vector(0, 0, 3.0 + 20.0 + 15.0), Vector(0, 0, 0)).toShape()
239
+ line2 = Part.LineSegment(Vector(0, 0, 0), Vector(36.0, 0, 0)).toShape()
240
+ wire_lid = Part.Wire([lid_edge, line1, line2])
241
+ face_lid = Part.Face(wire_lid)
242
+ lid_solid = face_lid.revolve(Vector(0, 0, 0), Vector(0, 0, 1), 360)
243
+
244
+ obj_lid = doc.addObject("Part::Feature", "Lid")
245
+ obj_lid.Shape = lid_solid
246
+ obj_lid.Placement = Placement(Vector(0, 0, BODY_HEIGHT), Rotation())
247
+ obj_lid.ViewObject.ShapeColor = (0.9, 0.7, 0.7)
248
+
249
+ # --- 3. Spout (Precomputed final positions) ---
250
+ spout_path_pts = [
251
+ Vector(0, -121, 66), # Original: (0, -56, 50) -> transformed
252
+ Vector(0, -91, 51), # Original: (0, -26, 65) -> transformed
253
+ Vector(0, -61, 36) # Original: (0, 4, 80) -> transformed
254
+ ]
255
+
256
+ spout_curve = Part.BSplineCurve(spout_path_pts)
257
+ spout_wire = Part.Wire(spout_curve.toShape())
258
+
259
+ tangent_spout = spout_curve.tangent(spout_curve.FirstParameter)[0]
260
+ tangent_spout.normalize()
261
+
262
+ spout_circle = Part.Circle()
263
+ spout_circle.Center = spout_path_pts[0]
264
+ spout_circle.Axis = tangent_spout
265
+ spout_circle.Radius = SPOUT_RADIUS
266
+ spout_profile = Part.Wire(spout_circle.toShape())
267
+
268
+ spout_solid = spout_wire.makePipe(spout_profile)
269
+ obj_spout = doc.addObject("Part::Feature", "Spout")
270
+ obj_spout.Shape = spout_solid
271
+ obj_spout.ViewObject.ShapeColor = (0.9, 0.7, 0.7)
272
+
273
+ # --- 4. Handle (Precomputed final positions) ---
274
+ handle_path_pts = [
275
+ Vector(0, 56, 31), # Original: (0, 56, 70) -> transformed
276
+ Vector(0, 78, 43), # Original: (0, 78, 58) -> transformed
277
+ Vector(0, 78, 79), # Original: (0, 78, 22) -> transformed
278
+ Vector(0, 56, 71) # Original: (0, 56, 30) -> transformed
279
+ ]
280
+
281
+ handle_curve = Part.BSplineCurve(handle_path_pts)
282
+ handle_wire = Part.Wire(handle_curve.toShape())
283
+
284
+ tangent_handle = handle_curve.tangent(handle_curve.FirstParameter)[0]
285
+ tangent_handle.normalize()
286
+
287
+ handle_circle = Part.Circle()
288
+ handle_circle.Center = handle_path_pts[0]
289
+ handle_circle.Axis = tangent_handle
290
+ handle_circle.Radius = HANDLE_RADIUS
291
+ handle_profile = Part.Wire(handle_circle.toShape())
292
+
293
+ handle_solid = handle_wire.makePipe(handle_profile)
294
+ obj_handle = doc.addObject("Part::Feature", "Handle")
295
+ obj_handle.Shape = handle_solid
296
+ obj_handle.ViewObject.ShapeColor = (0.9, 0.7, 0.7)
297
+
298
+ # --- 5. Fuse all parts ---
299
+ fused = obj_body.Shape.fuse(obj_lid.Shape)
300
+ fused = fused.fuse(obj_spout.Shape)
301
+ fused = fused.fuse(obj_handle.Shape)
302
+
303
+ obj_final = doc.addObject("Part::Feature", "Teapot_Complete")
304
+ obj_final.Shape = fused
305
+ obj_final.ViewObject.ShapeColor = (0.9, 0.6, 0.6)
306
+
307
+ # Hide individual parts for clarity
308
+ obj_body.ViewObject.Visibility = False
309
+ obj_lid.ViewObject.Visibility = False
310
+ obj_spout.ViewObject.Visibility = False
311
+ obj_handle.ViewObject.Visibility = False
312
+
313
+ doc.recompute()
314
+
315
+ Gui.activeDocument().activeView().viewAxometric()
316
+ Gui.SendMsgToActiveView("ViewFit")
317
+
318
+ return doc
319
+
320
+ if __name__ == "__main__":
321
+ createTeapot()
322
+
323
+ - This is a good example for a herringbone gear. If asked to make a herringbone gear, generate similar:
324
+ #Herringbone gear
325
+
326
+ import FreeCAD as App
327
+ import FreeCADGui as Gui
328
+ import Part
329
+ import math
330
+ from FreeCAD import Vector, Placement, Rotation
331
+
332
+ def createHerringboneGear(
333
+ num_teeth=20,
334
+ module=5.0,
335
+ pressure_angle_deg=20.0,
336
+ helix_angle_deg=25.0,
337
+ face_width=50.0,
338
+ central_bore_diameter=20.0,
339
+ num_loft_sections=50,
340
+ addendum_factor=1.0,
341
+ dedendum_factor=1.25,
342
+ tooth_radial_offset=1.5 # Teeth pushed radially outward
343
+ ):
344
+ doc = App.newDocument("HerringboneGear")
345
+
346
+ pressure_angle_rad = math.radians(pressure_angle_deg)
347
+ helix_angle_rad = math.radians(helix_angle_deg)
348
+
349
+ pitch_diameter = module * num_teeth
350
+ pitch_radius = pitch_diameter / 2
351
+ addendum = addendum_factor * module
352
+ dedendum = dedendum_factor * module
353
+ root_radius = pitch_radius - dedendum + tooth_radial_offset
354
+ outer_radius = pitch_radius + addendum + tooth_radial_offset
355
+
356
+ gear_total_height = face_width
357
+ half_gear_height = gear_total_height / 2
358
+
359
+ total_angular_twist_rad = (face_width * math.tan(helix_angle_rad)) / pitch_radius
360
+
361
+ gear_hub = doc.addObject("Part::Cylinder", "GearHub")
362
+ gear_hub.Radius = outer_radius - tooth_radial_offset
363
+ gear_hub.Height = gear_total_height
364
+ gear_hub.Placement.Base = Vector(0, 0, 0)
365
+
366
+ if central_bore_diameter > 0:
367
+ bore_radius = central_bore_diameter / 2
368
+ central_bore = doc.addObject("Part::Cylinder", "CentralBore")
369
+ central_bore.Radius = bore_radius
370
+ central_bore.Height = gear_total_height
371
+ central_bore.Placement.Base = Vector(0, 0, 0)
372
+
373
+ hub_base = doc.addObject("Part::Cut", "Hub_With_Bore")
374
+ hub_base.Base = gear_hub
375
+ hub_base.Tool = central_bore
376
+ else:
377
+ hub_base = gear_hub
378
+
379
+ angle_per_tooth = 360.0 / num_teeth
380
+
381
+ effective_half_angle_for_flank_base = (math.pi / num_teeth) / 2
382
+ effective_half_angle_for_flank_tip = effective_half_angle_for_flank_base * 0.7
383
+
384
+ P_A = Vector(root_radius * math.sin(effective_half_angle_for_flank_base), root_radius * math.cos(effective_half_angle_for_flank_base), 0)
385
+ P_B = Vector(root_radius * math.sin(-effective_half_angle_for_flank_base), root_radius * math.cos(-effective_half_angle_for_flank_base), 0)
386
+
387
+ P_C = Vector(outer_radius * math.sin(effective_half_angle_for_flank_tip), outer_radius * math.cos(effective_half_angle_for_flank_tip), 0)
388
+ P_D = Vector(outer_radius * math.sin(-effective_half_angle_for_flank_tip), outer_radius * math.cos(-effective_half_angle_for_flank_tip), 0)
389
+
390
+ e_flank1 = Part.LineSegment(P_B, P_D).toShape()
391
+ e_flank2 = Part.LineSegment(P_A, P_C).toShape()
392
+
393
+ def offset_midpoint(p1, p2, offset=0.1):
394
+ mid = (p1 + p2).multiply(0.5)
395
+ vec = p2.sub(p1)
396
+ perp = Vector(-vec.y, vec.x, 0)
397
+ perp.normalize()
398
+ return mid.add(perp.multiply(offset))
399
+
400
+ tip_midpoint = offset_midpoint(P_D, P_C, 0.1)
401
+ e_tip_arc = Part.ArcOfCircle(P_D, tip_midpoint, P_C).toShape()
402
+
403
+ root_midpoint = offset_midpoint(P_A, P_B, 0.1)
404
+ e_root_arc = Part.ArcOfCircle(P_A, root_midpoint, P_B).toShape()
405
+
406
+ try:
407
+ tooth_profile_wire = Part.Wire([e_root_arc, e_flank1, e_tip_arc, e_flank2])
408
+ except Exception as e:
409
+ App.Console.PrintError(f"Error creating tooth profile wire: {e}. Using fallback wire.\n")
410
+ fallback_edges = [
411
+ Part.LineSegment(P_A, P_B).toShape(),
412
+ Part.LineSegment(P_B, P_D).toShape(),
413
+ Part.LineSegment(P_D, P_C).toShape(),
414
+ Part.LineSegment(P_C, P_A).toShape()
415
+ ]
416
+ tooth_profile_wire = Part.Wire(fallback_edges)
417
+
418
+ tooth_profile_face = Part.Face(tooth_profile_wire)
419
+
420
+ helical_teeth_LH_fused = None
421
+ lh_z_start = 0
422
+ lh_z_end = half_gear_height
423
+ lh_twist_start = 0
424
+ lh_twist_end = total_angular_twist_rad / 2
425
+
426
+ for tooth_idx in range(num_teeth):
427
+ current_tooth_LH_profiles = []
428
+ initial_tooth_rotation_deg = tooth_idx * angle_per_tooth
429
+
430
+ for i in range(num_loft_sections + 1):
431
+ z_pos_current = lh_z_start + (lh_z_end - lh_z_start) * (i / num_loft_sections)
432
+ current_slice_twist_angle_rad = lh_twist_start + (lh_twist_end - lh_twist_start) * (i / num_loft_sections)
src/llm_client.py CHANGED
@@ -20,6 +20,12 @@ REPO_ID = "Yas1n/CADomatic_vectorstore"
20
  FILENAME_FAISS = "index_oss120b.faiss"
21
  FILENAME_PKL = "index_oss120b.pkl"
22
 
 
 
 
 
 
 
23
  faiss_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME_FAISS)
24
  pkl_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME_PKL)
25
  download_dir = Path(faiss_path).parent
@@ -29,9 +35,9 @@ vectorstore = FAISS.load_local(
29
  str(download_dir),
30
  embeddings=embedding,
31
  allow_dangerous_deserialization=True,
32
- index_name="index_oss120b"
33
  )
34
- retriever = vectorstore.as_retriever(search_kwargs={"k": 8})
35
 
36
  # Conversation memory
37
  memory = ConversationBufferMemory(return_messages=True)
@@ -39,7 +45,7 @@ memory = ConversationBufferMemory(return_messages=True)
39
  # Gemini 2.5 Flash (LangChain wrapper, no genai import needed)
40
  llm = ChatGoogleGenerativeAI(
41
  model="gemini-2.5-flash",
42
- temperature=1.2,
43
  api_key=GEMINI_API_KEY # Use the key directly
44
  )
45
 
@@ -61,6 +67,9 @@ Use the following FreeCAD wiki documentation as context:
61
 
62
  {context}
63
 
 
 
 
64
  Here is the conversation so far:
65
  {history_text}
66
 
 
20
  FILENAME_FAISS = "index_oss120b.faiss"
21
  FILENAME_PKL = "index_oss120b.pkl"
22
 
23
+ BASE_PROMPT_PATH = Path(__file__).parent.parent / "prompts" / "base_instruction.txt"
24
+ if not BASE_PROMPT_PATH.exists():
25
+ raise FileNotFoundError(f"Base instruction file not found: {BASE_PROMPT_PATH}")
26
+ with open(BASE_PROMPT_PATH, "r", encoding="utf-8") as f:
27
+ BASE_INSTRUCTIONS = f.read().strip()
28
+
29
  faiss_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME_FAISS)
30
  pkl_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME_PKL)
31
  download_dir = Path(faiss_path).parent
 
35
  str(download_dir),
36
  embeddings=embedding,
37
  allow_dangerous_deserialization=True,
38
+ index_name="index"
39
  )
40
+ retriever = vectorstore.as_retriever(search_kwargs={"k": 15})
41
 
42
  # Conversation memory
43
  memory = ConversationBufferMemory(return_messages=True)
 
45
  # Gemini 2.5 Flash (LangChain wrapper, no genai import needed)
46
  llm = ChatGoogleGenerativeAI(
47
  model="gemini-2.5-flash",
48
+ temperature=0.7,
49
  api_key=GEMINI_API_KEY # Use the key directly
50
  )
51
 
 
67
 
68
  {context}
69
 
70
+ Here are a few important instructions for you to follow
71
+ {BASE_INSTRUCTIONS}
72
+
73
  Here is the conversation so far:
74
  {history_text}
75