From 3e581d38c25934d6d563cbc465dbf6c61eba5a7f Mon Sep 17 00:00:00 2001 From: pgp Date: Wed, 29 Oct 2025 15:14:32 +0100 Subject: [PATCH] maj --- FCHoneycombMaker.FCMacro | 313 ++++++++++++++++++++++++++++++ FCHoneycombMaker.FCMacro.manifest | 4 + FCHoneycombMakerIcon.png | Bin 0 -> 4508 bytes start_WF.FCMacro | 61 ++++++ 4 files changed, 378 insertions(+) create mode 100644 FCHoneycombMaker.FCMacro create mode 100644 FCHoneycombMaker.FCMacro.manifest create mode 100644 FCHoneycombMakerIcon.png create mode 100644 start_WF.FCMacro diff --git a/FCHoneycombMaker.FCMacro b/FCHoneycombMaker.FCMacro new file mode 100644 index 0000000..51b6bfd --- /dev/null +++ b/FCHoneycombMaker.FCMacro @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +""" +*************************************************************************** +* Copyright (c) 2018-2019 * +* * +* This file is a supplement to the FreeCAD CAx development system. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License (LGPL) * +* as published by the Free Software Foundation; either version 2 of * +* the License, or (at your option) any later version. * +* * +* This software is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU Library General Public License at http://www.gnu.org/licenses * +* for more details. * +* * +* For more information about the GNU Library General Public License * +* write to the Free Software Foundation, Inc., 59 Temple Place, * +* Suite 330, Boston, MA 02111-1307 USA * +* * +*************************************************************************** +""" + + + +""" +FCHoneycombMaker + +This is a macro to aid in creation of a honeycomb grid. Parameters, +including hexagon radius, separation between hexagons, etc., can be modified via an included spreadsheet. +""" + +__title__ = "FCHoneycombMaker" +__author__ = "TheMarkster" +__url__ = "https://github.com/mwganson/FCHoneycombMaker" +__Wiki__ = "https://github.com/mwganson/FCHoneycombMaker/blob/master/README.md" +__date__ = "2020.10.30" #year.month.date +__version__ = __date__ + + + + +import FreeCAD +import math +import Part,Draft,DraftTools +from PySide import QtCore, QtGui +import ProfileLib.RegularPolygon +import Sketcher + +RADIUS = 2 +SEPARATION = .25 +PLATE_WIDTH = 20 +PLATE_LENGTH = 55 +PLATE_HEIGHT = 5 + +FreeCAD.Console.PrintMessage ('\nFCHoneycombMaker v'+__version__+'\n') + + +def makeHexagonSketch(sketchName): + #would use the polygon tool, but is buggy and sometimes creates squares instead of hexagons, so the manual way + sketch = App.ActiveDocument.getObject(sketchName) +#we just place the vertices any old where and then constrain them into place later + sketch.addGeometry(Part.LineSegment(App.Vector(-2.000000,1.448548,0),App.Vector(0.500515,2.469759,0)),False) + sketch.addGeometry(Part.LineSegment(App.Vector(0.500515,2.469759,0),App.Vector(2.000000,1.696951,0)),False) + sketch.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) + sketch.addGeometry(Part.LineSegment(App.Vector(2.000000,1.696951,0),App.Vector(2.736140,-0.676675,0)),False) + sketch.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) + sketch.addGeometry(Part.LineSegment(App.Vector(2.736140,-0.676675,0),App.Vector(1.000000,-2.415493,0)),False) + sketch.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) + sketch.addGeometry(Part.LineSegment(App.Vector(1.000000,-2.415493,0),App.Vector(-3.000000,-1.000000,0)),False) + sketch.addConstraint(Sketcher.Constraint('Coincident',3,2,4,1)) + sketch.addGeometry(Part.LineSegment(App.Vector(-3.000000,-1.000000,0),App.Vector(-2.000000,1.365748,0)),False) + sketch.addConstraint(Sketcher.Constraint('Coincident',4,2,5,1)) + sketch.addConstraint(Sketcher.Constraint('Coincident',5,2,0,1)) + sketch.addGeometry(Part.Circle(App.Vector(0.000000,0.000000,0),App.Vector(0,0,1),3.535498),False) #construction circle + sketch.addConstraint(Sketcher.Constraint('Coincident',6,3,-1,1)) + sketch.addConstraint(Sketcher.Constraint('Radius',6,3.535498)) + sketch.setDatum(7,App.Units.Quantity('2.000000 mm')) + sketch.addConstraint(Sketcher.Constraint('PointOnObject',4,2,6)) #constrain all vertices to the circumference + sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,1,6)) + sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,2,6)) + sketch.addConstraint(Sketcher.Constraint('PointOnObject',1,2,6)) + sketch.addConstraint(Sketcher.Constraint('PointOnObject',2,2,6)) + sketch.addConstraint(Sketcher.Constraint('PointOnObject',3,2,6)) + sketch.toggleConstruction(6) + sketch.addConstraint(Sketcher.Constraint('Equal',5,0)) #set all lines equal + sketch.addConstraint(Sketcher.Constraint('Equal',0,1)) + sketch.addConstraint(Sketcher.Constraint('Equal',1,2)) + sketch.addConstraint(Sketcher.Constraint('Equal',2,3)) + sketch.addConstraint(Sketcher.Constraint('Equal',3,4)) + sketch.addConstraint(Sketcher.Constraint('Horizontal',0)) #make one of them horizontal + sketch.setExpression('Constraints[7]', u'EditMe.radius') + + + + + +if not App.ActiveDocument: + App.newDocument() +if hasattr(App.ActiveDocument,"EditMe"): + raise StandardError("FCHoneycombMaker Error: Please run only once per document. Create a new document and try again.\n") + +worksheet = App.ActiveDocument.addObject("Spreadsheet::Sheet", "EditMe") +worksheet.setColumnWidth('A',150) +set = worksheet.set + +worksheet.mergeCells('A11:G25') +msg1 = """Instructions: + +You should only run the macro once unless you want to start again from scratch. + +Edit the values in column B to define your honeycomb. You can also edit the objects (plate, arrays, etc.), but it's +probably better to do all the editing here in the spreadsheet at least until you get it more or less like you want it before +doing some final tweaking directly on the objects. + +Hexagon radius -- the circumradius of the individual hexagons (circle with each vertex on its circumference). + +Hexagon separation -- distance between each hexagon, the thickness of the grid produced after cutting the hexagons from the plate. + +Plate dimensions -- sets the dimensions of the plate out of which the honeycomb can be cut. These values are used to calculate +the countX and countY variables. You can delete the plate object if you wish to apply the hexagon arrays to a different structure. + +Tweak X,Y,Z -- Edit these to move both hexagon arrays independently of the plate object, for example to center the arrays inside +the plate. + +CountX and CountY -- number of hexagons in the 2 arrays. These are calculated based on the plate size, radius of hexagons, and +separation between them, but you will probably want to modify these manually. Just remember if you modify them you are replacing +the formulas in those cells with immediate values, and thus they won't be recalulated for you if other changes are made. + +If you would prefer a round plate, simply delete the plate object and replace it with a cylinder, and then use the Tweak values +and countX and countY variables to arrange the hexagon arrays to your liking. + +""" +set('A13', msg1) + + +aliases={'radius':'B2', 'separation':'B3', 'width':'B4', 'length':'B5', 'height':'B6', 'tweakX':'B8','tweakY':'B9','tweakZ':'B10', +'xInterval':'E2', 'yInterval':'E3', 'firstX':'E4', 'firstY':'E5','countX':'E6', 'countY':'E7','array2XPos':'E8','array2YPos':'E9', + + } + +for k,v in aliases.items(): + worksheet.setAlias(v,k) + + + +set('A1', 'User Variables') +set('D1','Calculated Values') +set('A2', 'Hexagon Radius:') +set(aliases['radius'], str(RADIUS)) +set('A3', 'Hexagon Separation:') +set(aliases['separation'], str(SEPARATION)) +set('A4', 'Grid Width:') +set(aliases['width'], str(PLATE_WIDTH)) +set('A5', 'Grid Length:') +set(aliases['length'], str(PLATE_LENGTH)) +set('A6', 'Grid Height:') +set(aliases['height'], str(PLATE_HEIGHT)) +set('A8', 'Tweak X:') +set(aliases['tweakX'],u'0') +set('A9', 'Tweak Y:') +set(aliases['tweakY'],u'0') +set('A10', 'Tweak Z:') +set(aliases['tweakZ'],u'0') + + + + +set('D2','X Interval:') +set(aliases['xInterval'],'=2*sin(60deg)*(B2*2+(B3-0.267949*B2))') +set('D3', 'Y Interval:') +set(aliases['yInterval'], '=2*B2+(B3-0.267949*B2)' ) +set('D4', 'First X:') +set(aliases['firstX'], '=B2') +set('D5', 'First Y:') +set(aliases['firstY'], '=B2') +set('D6', 'Count X:') +set(aliases['countX'], '=round((B5) / E2) + 2') +set('D7', 'Count Y:') +set(aliases['countY'], '=round((B4) / E3) + 2') +set ('D8', 'Array2 XPos:') +set (aliases['array2XPos'], '=sin(60deg)*(B2*2+B3-0.26794899999999999*B2)') +set ('D9', 'Array2 YPos:') +set (aliases['array2YPos'],'=E3/2') + + + + + +#xInterval = float(worksheet.getContents(aliases['xInterval'])) +xInterval = 2*math.sin(math.pi/180.0*60)*(RADIUS*2+(SEPARATION-0.267949)) +yInterval = 2*RADIUS+(SEPARATION-0.267949) +#yInterval = float(worksheet.getContents(aliases['yInterval'])) +firstX = RADIUS +firstY = RADIUS + +countY = int((PLATE_LENGTH) / yInterval) +countX = int((PLATE_WIDTH) / xInterval) + + + + +App.ActiveDocument.addObject("Part::RegularPolygon","HoneycombHexagon") +App.ActiveDocument.HoneycombHexagon.Polygon=6 +App.ActiveDocument.HoneycombHexagon.setExpression('Circumradius','EditMe.radius') +hexagonObject = App.ActiveDocument.getObject("HoneycombHexagon") +Gui.ActiveDocument.getObject("HoneycombHexagon").Visibility=False + + +extrudedHexagonObject = App.ActiveDocument.addObject('Part::Extrusion', 'ExtrudedHexagon') +extrudedHexagonObject.Base = hexagonObject +extrudedHexagonObject.setExpression('LengthFwd','EditMe.height') +extrudedHexagonObject.Solid=True +#extrudedHexagonObject.Placement=App.Placement(App.Vector(firstX,firstY,0),App.Rotation(0,0,0),App.Vector(0,0,0)) +extrudedHexagonObject.setExpression('Placement.Base.x','EditMe.firstX+EditMe.tweakX') +extrudedHexagonObject.setExpression('Placement.Base.y','EditMe.firstY+EditMe.tweakY') +extrudedHexagonObject.setExpression('Placement.Base.z', 'EditMe.tweakZ') + +xvector = App.Vector(xInterval,0,0) +yvector = App.Vector(0, yInterval,0) +if not hasattr(Draft,"make_ortho_array"): + row1Array = Draft.makeArray(extrudedHexagonObject, xvector,yvector,countX,countY,name="HoneycombArray1") + row2Array = Draft.makeArray(extrudedHexagonObject, xvector,yvector,countX,countY,name="HoneycombArray2") +else: + row1Array = Draft.make_ortho_array(extrudedHexagonObject, v_x=xvector,v_y=yvector,n_x=countX,n_y=countY,use_link=False) + row2Array = Draft.make_ortho_array(extrudedHexagonObject, v_x=xvector,v_y=yvector,n_x=countX,n_y=countY,use_link=False) +App.ActiveDocument.recompute() +row2Array.Placement = App.Placement(App.Vector(math.sin(60*math.pi/180.0)*(RADIUS*2+(SEPARATION-0.267949)),yInterval,0),App.Rotation(0,0,0),App.Vector(0,0,0)) +#row2Array.setExpression('Placement.Base.x','sin(60deg) * (EditMe.radius * 2 + (EditMe.separation-0.267949*EditMe.radius))') +row2Array.setExpression('Placement.Base.x','EditMe.array2XPos') +#row2Array.setExpression('Placement.Base.y','EditMe.yInterval/2.0') +row2Array.setExpression('Placement.Base.y','EditMe.array2YPos') +row1Array.setExpression('IntervalX.x','EditMe.xInterval') +row1Array.setExpression('IntervalY.y','EditMe.yInterval') +row1Array.setExpression('NumberX','EditMe.countX') +row1Array.setExpression('NumberY','EditMe.countY') + +row2Array.setExpression('IntervalX.x','EditMe.xInterval') +row2Array.setExpression('IntervalY.y','EditMe.yInterval') +row2Array.setExpression('NumberX','EditMe.countX') +row2Array.setExpression('NumberY','EditMe.countY') + +App.activeDocument().addObject("Part::MultiFuse","Fused_Arrays") +#App.activeDocument().Fused_Arrays.Shapes = [App.activeDocument().HoneycombArray1,App.activeDocument().HoneycombArray2,] +App.activeDocument().Fused_Arrays.Shapes = [row1Array,row2Array,] +if not hasattr(Draft,"make_ortho_array"): + Gui.activeDocument().HoneycombArray1.Visibility=False + Gui.activeDocument().HoneycombArray2.Visibility=False +else: + row1Array.Visibility=False + row2Array.Visibility=False + + +window = QtGui.QApplication.activeWindow() +items=('Part Design Workbench Body','Part Workbench Object') +item,ok = QtGui.QInputDialog.getItem(window, "Select object type", "Select your object type. It can be a Part Design body or a Part Cut object.", items, 0, False) +if ok: + if item == items[0]: #PD Body + + App.ActiveDocument.addObject("Part::Plane", "HoneycombPlane") + App.ActiveDocument.HoneycombPlane.setExpression('Length', u'EditMe.length') + App.ActiveDocument.HoneycombPlane.setExpression('Width', u'EditMe.width') + App.ActiveDocument.HoneycombPlane.setExpression('Placement.Base.x', u'EditMe.array2XPos + EditMe.radius * 2') + App.ActiveDocument.HoneycombPlane.setExpression('Placement.Base.y', u'EditMe.array2YPos + EditMe.radius * 2') + + App.activeDocument().addObject("Part::Cut","BaseFeatureFace") + App.ActiveDocument.getObject("BaseFeatureFace").ViewObject.Visibility=False + App.activeDocument().BaseFeatureFace.Base = App.activeDocument().HoneycombPlane + App.activeDocument().BaseFeatureFace.Tool = App.activeDocument().Fused_Arrays + Gui.activeDocument().HoneycombPlane.Visibility=False + Gui.activeDocument().Fused_Arrays.Visibility=False + App.ActiveDocument.recompute() + App.activeDocument().addObject('PartDesign::Body','HoneycombBody') + App.activeDocument().HoneycombBody.BaseFeature = App.activeDocument().BaseFeatureFace + Gui.activeView().setActiveObject('pdHoneycombBody', App.activeDocument().HoneycombBody) + App.activeDocument().HoneycombBody.newObject("PartDesign::Pad","HoneycombPad") + App.activeDocument().HoneycombPad.Profile = (App.activeDocument().BaseFeature, ["Face1"]) + App.activeDocument().HoneycombPad.Length = 10.0 + App.ActiveDocument.recompute() + Gui.activeDocument().setEdit('HoneycombPad', 0) + Gui.activeDocument().hide("BaseFeature") + App.ActiveDocument.HoneycombPad.setExpression('Length', u'EditMe.height') + App.ActiveDocument.HoneycombPad.Length2 = 100.000000 + App.ActiveDocument.HoneycombPad.Type = 0 + App.ActiveDocument.HoneycombPad.UpToFace = None + App.ActiveDocument.HoneycombPad.Reversed = 0 + App.ActiveDocument.HoneycombPad.Midplane = 0 + App.ActiveDocument.HoneycombPad.Offset = 0.000000 + App.ActiveDocument.recompute() + Gui.activeDocument().resetEdit() + else: #Part object + #plate = Part.makeBox(PLATE_WIDTH,PLATE_LENGTH,PLATE_HEIGHT) + #plate = Part.makeBox(worksheet.B4, worksheet.B5, worksheet.B6) + App.ActiveDocument.addObject("Part::Box", "Plate") + plateObject = App.ActiveDocument.getObject("Plate") + App.ActiveDocument.Plate.setExpression('Length', u'EditMe.length') + App.ActiveDocument.Plate.setExpression('Width', u'EditMe.width') + App.ActiveDocument.Plate.setExpression('Height', u'EditMe.height') + App.ActiveDocument.Plate.setExpression('Placement.Base.x', u'EditMe.array2XPos + EditMe.radius * 2') + App.ActiveDocument.Plate.setExpression('Placement.Base.y', u'EditMe.array2YPos + EditMe.radius * 2') + + App.activeDocument().addObject("Part::Cut","HoneycombGrid") + App.activeDocument().HoneycombGrid.Base = App.activeDocument().Plate + App.activeDocument().HoneycombGrid.Tool = App.activeDocument().Fused_Arrays + Gui.activeDocument().Plate.Visibility=False + Gui.activeDocument().Fused_Arrays.Visibility=False + + + +App.ActiveDocument.recompute() +Gui.SendMsgToActiveView("ViewFit") \ No newline at end of file diff --git a/FCHoneycombMaker.FCMacro.manifest b/FCHoneycombMaker.FCMacro.manifest new file mode 100644 index 0000000..1441fd5 --- /dev/null +++ b/FCHoneycombMaker.FCMacro.manifest @@ -0,0 +1,4 @@ +[ + "/home/pgp/.var/app/org.freecad.FreeCAD/data/FreeCAD/Macro/MultiExport-FreeCAD/FCHoneycombMaker.FCMacro", + "/home/pgp/.var/app/org.freecad.FreeCAD/data/FreeCAD/Macro/MultiExport-FreeCAD/FCHoneycombMakerIcon.png" +] \ No newline at end of file diff --git a/FCHoneycombMakerIcon.png b/FCHoneycombMakerIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4f43bd3daa6c0bea26836e4655267338cf2e62 GIT binary patch literal 4508 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