FreeCAD-macros
471 строка · 22.4 Кб
1# -*- coding: utf-8 -*-
2# (c) 18Turbo, 2022
3
4__Name__ = 'Generador3D'
5__Comment__ = 'Create a solid with 2 or 3 sketches (views)'
6__Author__ = '18Turbo'
7__Version__ = '0.1.1'
8__Date__ = '2022-09-20'
9__License__ = 'LGPL-2.0-or-later'
10__Web__ = 'https://github.com/18turbo/Generador3DFreeCAD'
11__Wiki__ = 'https://github.com/18turbo/Generador3DFreeCAD/wiki'
12__Icon__ = 'G3d.svg'
13__Help__ = ''
14__Status__ = 'beta'
15__Requires__ = 'FreeCAD >= v0.20'
16__Contact__ = 'https://github.com/18turbo'
17__Communication__ = 'https://github.com/18turbo/Generador3DFreeCAD/issues'
18__Files__ = 'G3d.svg,g3d/ui/g3d15.ui'
19
20
21# (En español:)
22# Funcionalidad: Genera una pieza 3D a partir de 2 o 3 sketch, cada uno en una vista distinta (planta, frontal o lateral)
23#
24# Descripción: Una vez ejecutada la macro, habrá que asociar cada sketch a cada vista, y pulsar el botón de OK.
25#
26#
27# Notas de versiones:
28# v.0.1.0: Adaptación del código para varios idiomas
29# Corrección de error en la deselección de vistas
30# Cambio de nombres de ficheros
31# v.0.0.16: Corregido errores en la selección de vistas
32# Evita el error de introducir la longitud menor o igual al offset
33# v.0.0.15: Cambiada la interfaz
34# Añadido Offset y Longitud a cada vista
35# v.0.0.14: Adaptación al Addon Manager
36# v.0.0.13: Depuración de código
37# v.0.0.12: Depuración de código
38# Posibilidad de crear una carpeta automáticamente donde meter las vistas y ocultarlas
39# v.0.0.11: Incluida tolerancia 3D para evitar problemas en los límites.
40# v.0.0.10: Corrección de errores en sketch negativos
41# v.0.0.9: Cambio de interfaz y corrección de errores. Beta funcional.
42# v.0.0.8: Corrección de errores
43# v.0.0.7: Corrección de errores
44# v.0.0.6: Primera versión BETA
45# v.0.0.5 ALFA: Genera la pieza 3D con 3 vistas
46# v.0.0.4 ALFA: Arregla la selección en las vistas en la pestaña Task (Tarea)
47#
48#
49# BetaTester: Rafael García Rodríguez
50# BetaTester: 18Turbo
51
52# Traducción inglés: Rocco Vicedomini
53
54
55import FreeCAD as app
56import FreeCADGui as gui
57import Part
58
59from PySide import QtCore, QtGui
60from FreeCAD import Base
61
62
63strLang = [
64[ # Español
65"Generador 3D",
66"Seleccione un sketch (boceto) en 2 o 3 vistas:",
67"Frontal -Alzado- (Front/Rear):",
68"Marque esta casilla si quiere activar la vista frontal y asígnele un sketch (boceto)",
69"Lateral -Perfil- (Right/Left):",
70"Marque esta casilla si quiere activar la vista lateral y asígnele un sketch (boceto)",
71"Planta -Sup/Inf- (Top/Bott):",
72"Marque esta casilla si quiere activar la vista en planta y asígnele un sketch (boceto)",
73"Tol. 3D (mm.):",
74"Es la distancia en mm. que extruirá de más. No debe influir en el objeto final. Si no desea tolerancia, ponga este valor a 0",
75"Crear un Grupo (Carpeta) para las vistas (sketches)",
76"Ocultar las vistas al generar el sólido",
77"Fusión (interesección)",
78"Versión:",
79"Información Breve de Uso:",
80"Cree un boceto (sketch) para cada vista con la que desee generar un sólido 3D. Necesitará al menos 2 bocetos.",
81"Luego pinche en el icono del script y seleccione un boceto para cada vista.",
82"Finalmente, pulse OK para generar el sólido.",
83"Documentación:",
84"Offset",
85"Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)",
86"Longitud",
87"Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal",
88"Offset",
89"Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)",
90"Longitud",
91"Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral",
92"Offset",
93"Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)",
94"Longitud",
95"Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta",
96"Pieza3D",
97"VistasPieza3D"
98],
99[ # English
100## Strings for translation into any language (English Strings)
101
102"3D Generator",
103"Select a sketch in 2 or 3 views:",
104"Front view - (Front/Rear):",
105"Check this box if you want to activate the front view and assign it a sketch",
106"Side view - (Right/Left):",
107"Check this box if you want to activate the side view and assign it a sketch",
108"Plan view - (Top/Bott):",
109"Check this box if you want to activate the plan view and assign it a sketch",
110"Tol. 3D (mm):",
111"Length in millimetres. This should not influence the final object. If you do not need this tolerance, set this value to 0",
112"Make a new group (folder) with views (sketches)",
113"Hide views when generating the solid",
114"Fusion (intersection)",
115"Version:",
116"Brief guide to use the script:",
117"Create a sketch for each view required to generate your 3D solid. At least two sketches will be required.",
118"Then click on the script icon and select a sketch for each view.",
119"Finally, press the OK button to generate the solid.",
120"Documentation:",
121"Offset",
122"Offset from the sketch plane from which the extrusion along the Y axis will start",
123"Length",
124"Total length of the frontal extrusion along the Y axis (it must be always larger than the frontal offset)",
125"Offset",
126"Offset from the sketch plane from which the extrusion along the X axis will start",
127"Length",
128"Total length of the lateral extrusion along the X axis (it must be always larger than the lateral offset)",
129"Offset",
130"Offset from the sketch plane from which the extrusion along the Z axis will start",
131"Length",
132"Total length of the vertical extrusion along the Z axis (it must be always larger than the vertical offset)",
133"3D-Part",
134"3D-Part_Views"
135],
136[ ## Stringhe per la traduzione in qualsiasi lingua (stringhe in italiano)
137
138"Generatore 3D",
139"Seleziona uno schizzo in 2 o 3 viste:",
140"Vista frontale - (Fronte/Retro):",
141"Spunta questa casella se desideri utilizzare la vista frontale ed assegnarle uno schizzo",
142"Vista laterale - (Destra/Sinistra):",
143"Spunta questa casella se desideri utilizzare la vista laterale ed assegnarle uno schizzo",
144"Vista in pianta - (Alto/Basso):",
145"Spunta questa casella se desideri utilizzare la vista in pianta ed assegnarle uno schizzo",
146"Tol. 3D (mm):",
147"Lunghezza in mm di sovraestrusione. Non dovrebbe influenzare l'oggetto finale. Se non si necessita questa tolleranza impostare il valore relativo a 0 mm",
148"Crea un nuovo gruppo (cartella) con le viste (sketch)",
149"Nasconde le viste quando viene generato il solido",
150"Fusione (intersezione)",
151"Versione:",
152"Breve guida all'uso dello script:",
153"Crea uno sketch per ogni vista necessaria a generare il solido. Sono necessari almeno due sketch.",
154"Quindi clicca sull'icona dello script e seleziona uno schizzo per ciascuna vista necessaria.",
155"Infine, premi il pulsante OK per generare il solido.",
156"Documentazione:",
157"Offset",
158"Distanza rispetto al piano dello sketch frontale da cui inizierà l'estrusione lungo l'asse Y (spostamento in Y)",
159"Lunghezza",
160"Lunghezza totale dell'estrusione frontale lungo l'asse Y (dovrà essere sempre più grande dell'offset frontale)",
161"Offset",
162"Distanza rispetto al piano dello sketch laterale da cui inizierà l'estrusione lungo l'asse X (spostamento in X)",
163"Lunghezza",
164"Lunghezza totale dell'estrusione laterale lungo l'asse X (dovrà essere sempre più grande dell'offset laterale)",
165"Offset",
166"Distanza rispetto al piano dello sketch in pianta da cui inizierà l'estrusione lungo l'asse Z (spostamento in Z)",
167"Lunghezza",
168"Lunghezza totale dell'estrusione verticale lungo l'asse Z (dovrà essere sempre più grande dell'offset in pianta)",
169"Pezzo3D",
170"Pezzo3D_Viste"
171]
172]
173
174
175minimoExtrusion = app.Units.Quantity('0.01 mm')
176
177
178class Generator3d:
179
180def __init__(self):
181self.doc = app.activeDocument()
182if not self.doc:
183app.Console.PrintWarning('No active document\n')
184return
185
186ui_path = app.getUserMacroDir(True) + '/g3d/ui/g3d15.ui'
187self.form = gui.PySideUic.loadUi(ui_path)
188
189# Whether views are active.
190self.frontal = False
191self.lateral = False
192self.planta = False
193
194# Find all sketches and add to the widgets.
195for candidate_sketch in app.activeDocument().Objects:
196if candidate_sketch.TypeId == 'Sketcher::SketchObject':
197self.form.listWidgetFrontal.addItem(candidate_sketch.Label)
198self.form.listWidgetLateral.addItem(candidate_sketch.Label)
199self.form.listWidgetPlanta.addItem(candidate_sketch.Label)
200
201self.form.listWidgetFrontal.itemSelectionChanged.connect(self.changeSelectionFront)
202self.form.listWidgetLateral.itemSelectionChanged.connect(self.changeSelectionLat)
203self.form.listWidgetPlanta.itemSelectionChanged.connect(self.changeSelectionTop)
204
205self.form.offsetF.valueChanged.connect(self.changeValueFront)
206self.form.offsetL.valueChanged.connect(self.changeValueLat)
207self.form.offsetP.valueChanged.connect(self.changeValueTop)
208
209self.form.etFrontal.clicked.connect(self.changeCheckedFront)
210self.form.etLateral.clicked.connect(self.changeCheckedLat)
211self.form.etPlanta.clicked.connect(self.changeCheckedTop)
212
213self.initStrings()
214
215def initStrings(self):
216tr = self.translate
217if g_num_lang != 0:
218title = "(" + strLang[g_num_lang][0] + ")"
219else:
220title = ""
221self.form.setWindowTitle(__Name__ + " " + title)
222self.form.etInfo.setText(tr('Seleccione un sketch (boceto) en 2 o 3 vistas:'))
223self.form.etFrontal.setTitle(tr('Frontal -Alzado- (Front/Rear):'))
224self.form.etFrontal.setToolTip(tr('Marque esta casilla si quiere activar la vista frontal y asígnele un sketch (boceto)'))
225self.form.etLateral.setTitle(tr('Lateral -Perfil- (Right/Left):'))
226self.form.etLateral.setToolTip(tr('Marque esta casilla si quiere activar la vista lateral y asígnele un sketch (boceto)'))
227self.form.etPlanta.setTitle(tr('Planta -Sup/Inf- (Top/Bott):'))
228self.form.etPlanta.setToolTip(tr('Marque esta casilla si quiere activar la vista en planta y asígnele un sketch (boceto)'))
229self.form.etTolerancia.setText(tr('Tol. 3D (mm.):'))
230self.form.etTolerancia.setToolTip(tr('Es la distancia en mm. que extruirá de más. No debe influir en el objeto final. Si no desea tolerancia, ponga este valor a 0'))
231self.form.meterVistasCarpeta.setText(tr('Crear un Grupo (Carpeta) para las vistas (sketches)'))
232self.form.ocultarVistas.setText(tr('Ocultar las vistas al generar el sólido'))
233self.form.radioInterseccion.setText(tr('Fusión (interesección)'))
234self.form.etVersion.setText(tr('Versión:'))
235self.form.etNumVersion.setText(__Version__ + " " + __Status__)
236self.form.etInfoUso.setText(tr('Información Breve de Uso:'))
237descripcionInfoUso = (tr('Cree un boceto (sketch) para cada vista con la que desee generar un sólido 3D. Necesitará al menos 2 bocetos.')
238+ " " + tr('Luego pinche en el icono del script y seleccione un boceto para cada vista.')
239+ " " + tr('Finalmente, pulse OK para generar el sólido.'))
240self.form.descInfoUso.setText(descripcionInfoUso)
241self.form.etDoc.setText(tr('Documentación:'))
242self.form.etOffsetF.setText(tr('Offset'))
243self.form.etOffsetF.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)'))
244self.form.offsetF.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)'))
245self.form.etOffsetL.setText(tr('Offset'))
246self.form.etOffsetL.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)'))
247self.form.offsetL.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)'))
248self.form.etOffsetP.setText(tr('Offset'))
249self.form.etOffsetP.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)'))
250self.form.offsetP.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)'))
251self.form.etLongitudF.setText(tr('Longitud'))
252self.form.etLongitudF.setToolTip(tr('Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal'))
253self.form.longF.setToolTip(tr('Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal'))
254self.form.etLongitudL.setText(tr('Longitud'))
255self.form.etLongitudL.setToolTip(tr('Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral'))
256self.form.longL.setToolTip(tr('Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral'))
257self.form.etLongitudP.setText(tr('Longitud'))
258self.form.etLongitudP.setToolTip(tr('Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta'))
259self.form.longP.setToolTip(tr('Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta'))
260
261def translate(self, text):
262if (text not in strLang[0]) or (g_num_lang < 0) or (g_num_lang >= len(strLang)):
263return text
264primerIdioma = strLang[0]
265indice = primerIdioma.index(text)
266if (indice == -1) or (indice >= len(strLang[g_num_lang])):
267return text
268return strLang[g_num_lang][indice]
269
270def changeValueFront(self):
271self.form.longF.setMinimum(self.form.offsetF.value() + minimoExtrusion)
272
273def changeValueLat(self):
274self.form.longL.setMinimum(self.form.offsetL.value() + minimoExtrusion)
275
276def changeValueTop(self):
277self.form.longP.setMinimum(self.form.offsetP.value() + minimoExtrusion)
278
279def changeCheckedFront(self):
280if not self.form.etFrontal.isChecked():
281self.frontal = False
282else:
283self.frontal = True
284
285def changeCheckedLat(self):
286if not self.form.etLateral.isChecked():
287self.lateral = False
288else:
289self.lateral = True
290
291def changeCheckedTop(self):
292if not self.form.etPlanta.isChecked():
293self.planta = False
294else:
295self.planta = True
296
297def changeSelectionFront(self):
298label = str(self.form.listWidgetFrontal.selectedItems()[0].text())
299self.front_sketch = self.doc.getObjectsByLabel(label)[0]
300
301def changeSelectionLat(self):
302label = str(self.form.listWidgetLateral.selectedItems()[0].text())
303self.lat_sketch = self.doc.getObjectsByLabel(label)[0]
304
305def changeSelectionTop(self):
306label = str(self.form.listWidgetPlanta.selectedItems()[0].text())
307self.top_sketch = self.doc.getObjectsByLabel(label)[0]
308
309def active_view_count(self):
310return sum([
311self.form.etFrontal.isChecked(),
312self.form.etLateral.isChecked(),
313self.form.etPlanta.isChecked()])
314
315def extrusion(self, nombreExtrusion, nombreEnVista, vista):
316doc = app.activeDocument()
317doc.addObject('Part::Extrusion', nombreExtrusion)
318f = doc.getObject(nombreExtrusion)
319objf = doc.getObjectsByLabel(nombreEnVista)
320f.Base = doc.getObject(objf[0].Name)
321f.DirMode = "Custom"
322if vista == "planta":
323offset = self.form.offsetP.value()
324longitud = self.form.longP.value()
325f.Dir = app.Vector(0.0, 0.0, 1.0)
326if self.lateral:
327maximoLZ = self.lat_sketch.Shape.BoundBox.ZMax
328minimoLZ = self.lat_sketch.Shape.BoundBox.ZMin
329if abs(maximoLZ) > abs(minimoLZ):
330f.LengthFwd = (abs(maximoLZ) + self.form.tolerancia.value()) * 2
331else:
332f.LengthFwd = (abs(minimoLZ) + self.form.tolerancia.value()) * 2
333else:
334maximoFZ = self.front_sketch.Shape.BoundBox.ZMax
335minimoFZ = self.front_sketch.Shape.BoundBox.ZMin
336if abs(maximoFZ) > abs(minimoFZ):
337f.LengthFwd = (abs(maximoFZ) + self.form.tolerancia.value()) * 2
338else:
339f.LengthFwd = (abs(minimoFZ) + self.form.tolerancia.value()) * 2
340
341elif vista == "frontal":
342offset = self.form.offsetF.value()
343longitud = self.form.longF.value()
344f.Dir = app.Vector(0.0, 1.0, 0.0)
345if self.lateral:
346maximoLY = self.lat_sketch.Shape.BoundBox.YMax
347minimoLY = self.lat_sketch.Shape.BoundBox.YMin
348if abs(maximoLY) > abs(minimoLY):
349f.LengthFwd = (abs(maximoLY) + self.form.tolerancia.value()) *2
350else:
351f.LengthFwd = (abs(minimoLY) + self.form.tolerancia.value()) *2
352else:
353maximoPY = self.top_sketch.Shape.BoundBox.YMax
354minimoPY = self.top_sketch.Shape.BoundBox.YMin
355if abs(maximoPY) > abs(minimoPY):
356f.LengthFwd = (abs(maximoPY) + self.form.tolerancia.value()) *2
357else:
358f.LengthFwd = (abs(minimoPY) + self.form.tolerancia.value()) *2
359
360elif vista == "lateral":
361offset = self.form.offsetL.value()
362longitud = self.form.longL.value()
363f.Dir = app.Vector(1.0, 0.0, 0.0)
364if self.planta:
365maximoPX = self.top_sketch.Shape.BoundBox.XMax
366minimoPX = self.top_sketch.Shape.BoundBox.XMin
367if abs(maximoPX) > abs(minimoPX):
368f.LengthFwd = (abs(maximoPX) + self.form.tolerancia.value()) *2
369else:
370f.LengthFwd = (abs(minimoPX) + self.form.tolerancia.value()) *2
371else:
372maximoFX = self.front_sketch.Shape.BoundBox.XMax
373minimoFX = self.front_sketch.Shape.BoundBox.XMin
374if abs(maximoFX) > abs(minimoFX):
375f.LengthFwd = (abs(maximoFX) + self.form.tolerancia.value()) *2
376else:
377f.LengthFwd = (abs(minimoFX) + self.form.tolerancia.value()) *2
378
379f.DirLink = None
380f.Solid = True
381f.Reversed = False
382if (offset > 0) or (longitud > 0):
383f.Symmetric = False
384f.LengthRev = offset * (-1)
385if (longitud > 0):
386f.LengthFwd = longitud
387else:
388f.LengthRev = 0.0
389f.Symmetric = True
390f.TaperAngle = 0.0
391f.TaperAngleRev = 0.0
392doc.recompute()
393return f
394
395def accept(self):
396#Comprobar que no existe ya una extrusión con este nombre
397numeroCoincidencias = 0
398encontradoCoincidencia = False
399cadenaAdicional = ""
400doc = app.activeDocument()
401for obj in doc.Objects:
402if (('ExtrPlanta18Turbo' in obj.Name) or
403('ExtrFrontal18Turbo' in obj.Name) or
404('ExtrLateral18Turbo' in obj.Name)):
405numeroCoincidencias = numeroCoincidencias + 1
406encontradoCoincidencia = True
407if encontradoCoincidencia:
408cadenaAdicional = cadenaAdicional + str(numeroCoincidencias)
409
410if self.active_view_count() >= 2:
411if self.planta:
412# Sketch en Z (Planta XY)
413planta = self.extrusion("ExtrPlanta18Turbo" + cadenaAdicional,
414self.top_sketch.Label, "planta")
415if self.frontal:
416# Sketch en Y (Frontal XZ)
417frontal = self.extrusion("ExtrFrontal18Turbo" + cadenaAdicional,
418self.front_sketch.Label, "frontal")
419if self.lateral:
420# Sketch en X (Lateral YZ)
421lateral = self.extrusion("ExtrLateral18Turbo" + cadenaAdicional,
422self.lat_sketch.Label, "lateral")
423
424# Intersección
425nombreInterseccion = self.translate('Pieza3D') + cadenaAdicional
426if self.active_view_count() == 3:
427common = doc.addObject("Part::MultiCommon", nombreInterseccion)
428common.Shapes = [planta, frontal, lateral]
429else:
430common = doc.addObject("Part::MultiCommon", nombreInterseccion)
431if (self.planta) and (self.frontal):
432common.Shapes = [planta, frontal]
433if (self.planta) and (self.lateral):
434common.Shapes = [planta, lateral]
435if (self.frontal) and (self.lateral):
436common.Shapes = [frontal, lateral]
437doc.recompute()
438
439# Meter vistas en Grupo (Carpeta)
440if self.form.meterVistasCarpeta.checkState():
441nombreGrupoVistas = self.translate('VistasPieza3D') + cadenaAdicional
442group = doc.addObject('App::DocumentObjectGroup', nombreGrupoVistas)
443if self.planta:
444group.addObject(self.top_sketch)
445if self.frontal:
446group.addObject(self.front_sketch)
447if self.lateral:
448group.addObject(self.lat_sketch)
449# Ocultar Vistas
450if self.form.ocultarVistas.isChecked():
451if self.planta:
452self.top_sketch.Visibility = False
453if self.frontal:
454self.front_sketch.Visibility = False
455if self.lateral:
456self.lat_sketch.Visibility = False
457
458gui.Control.closeDialog()
459
460
461# Se define el g_num_lang con variable global
462lang = gui.getLocale()
463# Default to English.
464g_num_lang = 1
465if 'Spanish' in lang:
466g_num_lang = 0
467elif 'Italian' in lang:
468g_num_lang = 2
469
470panel = Generator3d()
471gui.Control.showDialog(panel)
472