why 1pyramid?(3)textLABEL

Need help, or want to share a macro? Post here!
mrrclb48z
Posts: 27
Joined: Wed Feb 07, 2018 11:20 am

why 1pyramid?(3)textLABEL

Postby mrrclb48z » Mon Apr 09, 2018 2:03 pm

I coud understand a little
I can not understand Dwire NO? Text NO?
I need (Dwire and Text) count? (For each function call)
Sorry for weird English

What am I doing wrong with the (new)macro?
output:
Label="myPyramid_x0y5(Text)",Text="myPyramid_x0y0"
Label="Text001,Label" ,Text="myPyramid_x0y5"

Code: Select all

# -*- coding: utf-8 -*-
import FreeCAD
import Part
import DraftTools
import Draft

def myPyramid(x,y,b,h):
       x1=x
       y1=y
       z1=0
       x2=x+b
       y2=y
       z2=0
       x3=x+b
       y3=y+b
       z3=0
       x4=x
       y4=y+b
       z4=0
       x5=x+b*.5
       y5=y+b*.5
       z5=h

       points=[FreeCAD.Vector(x1,y1,z1),FreeCAD.Vector(x2,y2,z2),FreeCAD.Vector(x3,y3,z3),FreeCAD.Vector(x4,y4,z4)]
       w1 = Draft.makeWire(points,closed=True,face=True,support=None)
       points=[FreeCAD.Vector(x1,y1,z1),FreeCAD.Vector(x2,y2,z2),FreeCAD.Vector(x5,y5,z5)]
       w2 = Draft.makeWire(points,closed=True,face=True,support=None)
       points=[FreeCAD.Vector(x2,y2,z2),FreeCAD.Vector(x3,y3,z3),FreeCAD.Vector(x5,y5,z5)]
       w3 = Draft.makeWire(points,closed=True,face=True,support=None)
       points=[FreeCAD.Vector(x3,y3,z3),FreeCAD.Vector(x4,y4,z4),FreeCAD.Vector(x5,y5,z5)]
       w4 = Draft.makeWire(points,closed=True,face=True,support=None)
       points=[FreeCAD.Vector(x4,y4,z4),FreeCAD.Vector(x1,y1,z1),FreeCAD.Vector(x5,y5,z5)]
       w5 = Draft.makeWire(points,closed=True,face=True,support=None)
       myWedgei='myPyramid_x' + str(x) + 'y' + str(y)
       fuse = App.activeDocument().addObject("Part::MultiFuse",myWedgei)
       fuse.Shapes = [w1, w2, w3, w4, w5]
       # https://forum.freecadweb.org/viewtopic.php?f=22&t=27765
       Draft.makeText(myWedgei,FreeCAD.Vector(x5,y5,z5)) 
       mvo = App.ActiveDocument.Text.ViewObject
       FreeCAD.getDocument("MeishoMisette").getObject("Text").Label = myWedgei + "(Text)"
#
b=1
h=1
myPyramid(0,0,b,h)
myPyramid(0,5,b,h)
App.ActiveDocument.recompute()
Gui.activeDocument().activeView().viewAxonometric()
Gui.SendMsgToActiveView("ViewFit")
User avatar
Chris_G
Posts: 1476
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: why 1pyramid?(3)textLABEL

Postby Chris_G » Mon Apr 09, 2018 3:14 pm

Same error as before :
Everytime you use a function like Draft.makeText(...) or Draft.makeWire(...), it will create a document object with automatic naming.
So, in your first call to the pyramid function, Draft.makeText(...) creates a text object in the document that will be called "Text".
On the second call, since there is already an object called "Text", Draft.makeText(...) creates a text object called "Text001"
On third call, it would create "Text002"
But here :

Code: Select all

FreeCAD.getDocument("MeishoMisette").getObject("Text")
You are always accessing the same object called "Text" (the one that was created during the first call of the function).
So, the fix is the same :
use a variable to store your text object and access it :

Code: Select all

myText = Draft.makeText(...)
myText.Label = myWedgei + "(Text)"
full script :

Code: Select all

# -*- coding: utf-8 -*-
import FreeCAD
import Part
import DraftTools
import Draft

def myPyramid(x,y,b,h):

       p1 = FreeCAD.Vector(x,y,0)
       p2 = FreeCAD.Vector(x+b,y,0)
       p3 = FreeCAD.Vector(x+b,y+b,0)
       p4 = FreeCAD.Vector(x,y+b,0)
       p5 = FreeCAD.Vector(x+b*.5,y+b*.5,h)

       w1 = Draft.makeWire([p1, p2, p3, p4], closed=True, face=True, support=None)
       w2 = Draft.makeWire([p1, p2, p5], closed=True, face=True, support=None)
       w3 = Draft.makeWire([p2, p3, p5], closed=True, face=True, support=None)
       w4 = Draft.makeWire([p3, p4, p5], closed=True, face=True, support=None)
       w5 = Draft.makeWire([p4, p1, p5], closed=True, face=True, support=None)
       myWedgei='myPyramid_x' + str(x) + 'y' + str(y)
       fuse = App.activeDocument().addObject("Part::MultiFuse",myWedgei)
       fuse.Shapes = [w1, w2, w3, w4, w5]
       # https://forum.freecadweb.org/viewtopic.php?f=22&t=27765
       myText = Draft.makeText(myWedgei,p5) 
       # mvo = App.ActiveDocument.Text.ViewObject
       myText.Label = myWedgei + "(Text)"


b=1
h=1
myPyramid(0,0,b,h)
myPyramid(0,5,b,h)
App.ActiveDocument.recompute()
Gui.activeDocument().activeView().viewAxonometric()
Gui.SendMsgToActiveView("ViewFit")
 
mrrclb48z
Posts: 27
Joined: Wed Feb 07, 2018 11:20 am

Re: why 1pyramid?(3)textLABEL

Postby mrrclb48z » Tue Apr 10, 2018 3:08 pm

Thanks for full script

Code: Select all

# -*- coding: utf-8 -*-
import FreeCAD
import Part
import DraftTools
import Draft
def myPyramid(x,y,b,h):
       p1 = FreeCAD.Vector(x,y,0)
       p2 = FreeCAD.Vector(x+b,y,0)
       p3 = FreeCAD.Vector(x+b,y+b,0)
       p4 = FreeCAD.Vector(x,y+b,0)
       p5 = FreeCAD.Vector(x+b*.5,y+b*.5,h)
       w1 = Draft.makeWire([p1, p2, p3, p4], closed=True, face=True, support=None)
       w2 = Draft.makeWire([p1, p2, p5], closed=True, face=True, support=None)
       w3 = Draft.makeWire([p2, p3, p5], closed=True, face=True, support=None)
       w4 = Draft.makeWire([p3, p4, p5], closed=True, face=True, support=None)
       w5 = Draft.makeWire([p4, p1, p5], closed=True, face=True, support=None)
       myWire =  "(" + w1.Label +"+" + w2.Label +"+" + w3.Label + "+" + w4.Label + "+" + w5.Label + ")"
       myWedgei='myPyramid_x' + str(x) + 'y' + str(y) + myWire
       fuse = App.activeDocument().addObject("Part::MultiFuse",myWedgei)
       fuse.Shapes = [w1, w2, w3, w4, w5]
       myText = Draft.makeText(myWedgei,p5) 
       myText.Label = myWedgei
b=1
h=1
myPyramid(0,0,b,h)
myPyramid(0,5,b,h)
App.ActiveDocument.recompute()
Gui.activeDocument().activeView().viewAxonometric()
Gui.SendMsgToActiveView("ViewFit")
1.convret to solid------->I want to (solid only)
2.delete Dwire*---------->I need Dwire count? (For each function call)
3.dwire delete -->later-->checkmark on icon? what? Please introduce web page

Is there any good way to do it?
TheMarkster
Posts: 2029
Joined: Thu Apr 05, 2018 1:53 am

Re: why 1pyramid?(3)textLABEL

Postby TheMarkster » Mon Apr 23, 2018 5:08 am

You could create a mesh object, create a shape from the mesh object, and then create the solid from the shape. That way you would only have the one final solid object if you don't want all the wires. The mesh object being produced here does have some issues according to the analyze tool in the Mesh Design workbench, but the solid produced from it looks to be okay.

Code: Select all

import Mesh
def myPyramidFromMesh(xIn,yIn,bIn,hIn):
        x = float(xIn)
        y = float(yIn)
        b = float(bIn)
        h = float(hIn)
        x2 = x+b*0.5
        y2 = y+b*0.5
        x3 = x+b
        y3 = y+b
        z = 0
        z2 = h


        m1 = Mesh.Mesh([[x,y3,z],[x2,y2,z2],[x3,y3,z]]) #sides
        m2 = Mesh.Mesh([[x3,y,z],[x2,y2,z2],[x,y,z]])
        m3 = Mesh.Mesh([[x,y,z],[x2,y2,z2],[x,y3,z]])
        m4 = Mesh.Mesh([[x3,y,z],[x3,y3,z],[x2,y2,z2]])
        m5 = Mesh.Mesh([[x,y,z],[x,y3,z],[x3,y3,z],[x,y,z],[x3,y3,z],[x3,y,z]])#base

        mesh=Mesh.Mesh()
        mesh.addMesh(m1)
        mesh.addMesh(m2)
        mesh.addMesh(m3)
        mesh.addMesh(m4)
        mesh.addMesh(m5)

        shape = Part.Shape()        
        shape.makeShapeFromMesh(mesh.Topology,0.05)
        
        solid = Part.makeSolid(shape).removeSplitter()
        myWedgei='myPyramid_x' + str(x) + 'y' + str(y)
        
        f = App.activeDocument().addObject("Part::Feature",myWedgei)
        f.Shape = solid
You could also create a single wire object for the base and then extrude that at an angle so that the points meet together at the top, but you would still be left with at least the one wire base object because if you deleted it you would break the dependent extruded object. You would also need to calculate the correct angle to use for the taper, which would be different if b and h are not equal to each other. For b = h the taper should be around -26.565 degrees.
My FreeCAD video series on youtube: https://www.youtube.com/c/mwganson
TheMarkster
Posts: 2029
Joined: Thu Apr 05, 2018 1:53 am

Re: why 1pyramid?(3)textLABEL

Postby TheMarkster » Mon Apr 23, 2018 3:58 pm

It occurs to me we have a primitive object ideally suited for creating pyramids in part workbench, the versatile wedge object.

Code: Select all

def myPyramidFromWedge(x,y,b,h):

    name = "Pyramidx"+str(x)+"y"+str(y)
    name = name.replace('-','_') #- sign gets replaced with underscore
    App.activeDocument().addObject("Part::Wedge", name)
    obj = App.activeDocument().getObject(name)
    obj.Xmin= str(x) #build it sideways, and then stand it up afterwards 
    obj.Ymin =str(y)
    obj.Zmin = str(0)
    obj.Xmax = str(x+b)
    obj.X2max = str(x+b/2.0)
    obj.X2min = str(x+b/2.0)
    obj.Ymax = str(y+h)
    obj.Zmax = str(b)
    obj.Z2min = str(b/2.0)
    obj.Z2max=str(b/2.0)
    placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(1,0,0),90),App.Vector(0,0,0))
    obj.Placement = placement
My FreeCAD video series on youtube: https://www.youtube.com/c/mwganson
TheMarkster
Posts: 2029
Joined: Thu Apr 05, 2018 1:53 am

Re: why 1pyramid?(3)textLABEL

Postby TheMarkster » Mon Apr 23, 2018 5:43 pm

At the risk of beating a dead horse, the extruded version:

Code: Select all

def myPyramidExtruded(x,y,b,h):

       p1 = FreeCAD.Vector(x,y,0)
       p2 = FreeCAD.Vector(x+b,y,0)
       p3 = FreeCAD.Vector(x+b,y+b,0)
       p4 = FreeCAD.Vector(x,y+b,0)

       e1 = Part.Edge(Part.makeLine(p1,p2))
       e2 = Part.Edge(Part.makeLine(p2,p3))
       e3 = Part.Edge(Part.makeLine(p3,p4))
       e4 = Part.Edge(Part.makeLine(p4,p1))
       pw = Part.Wire([e1,e2,e3,e4])
       name = "Pyramidx"+str(x)+"y"+str(y).replace('-','_')

       w = App.activeDocument().addObject("Part::Feature",name )
       w.Shape = pw
       f = App.activeDocument().addObject("Part::Extrusion", "Extruded"+name)
       base = App.activeDocument().getObject(name)

       f.Base = base
       f.DirMode = "Normal"
       f.DirLink = None
       f.LengthFwd = h
       f.LengthRev = 0.0
       f.Solid = True
       f.Reversed = False
       f.Symmetric = False
       f.TaperAngle = -90. + 57.29578*math.atan((2.0*float(h))/float(b))
       f.TaperAngleRev = 0.0
       f.Base.ViewObject.hide()
        #actual number is 57.29577951308232087679815481410517033218 to 25 digits
Note the math involved for calculating the taper angle, which I found to be an interesting problem in geometry/trigonometry. We take the arctangent of 2h/b, which would be 1.10715 for h = b, multiply this value with the constant 57.29578, and subtract 90 degrees from that product since the taper is measured against the vertical. If we're going to miss the angle we want to undershoot it, preferring a very tiny plateau atop the pyramid versus too much of an angle and having the extrusion converge before reaching the top of the pyramid, which results in an error and produces no pyramid at all, hence the rounding up of the constant. (Also, python float probably can't handle such a long number.)

To see how I did the math go to https://lab.open.wolframcloud.com/app select the create a new notebook option and copy/paste:

Solve[Tan[deg*Pi/180]*(b/2) == h, {deg}]

Solve[] is a wonderful tool. It takes an equation as the first parameter and the variable or list of variables {variables_list} you wish to solve for. In this case, {deg}, but could just as easily simply been deg since I only wanted to solve for the one variable. Tan[] takes radians instead of degrees, so we use Pi/180 to convert to degrees. We know tan(deg) * (b/2) == h because in general tan(deg) * run = rise, and in this case b/2 is our run and h is our rise.

The output from that first line is a bit cryptic:

"{{deg -> ConditionalExpression[(180*(ArcTan[(2*h)/b] + Pi*C[1]))/Pi, \n Element[C[1], Integers]]}}"

But not so bad because we can ignore the ConditionalExpression[] bit and the +Pi*C[1] bit, along with the Elmenent[] stuff and just focus on the (180*(ArcTan[(2*h)/b] + Pi*C[1]))/Pi bit to generate a new function.

I used the output from that to produce a function called getDeg[h,b]:

getDeg[h_, b_] := (180*ArcTan[(2*h)/b])/Pi

And then applied N[,25] to that function to get 25 digits of precision:

N[-1*(90 - getDeg[h, b]), 25] // InputForm

out = "-90.`25. + 57.2957795130823208767981548141051703052`25.*\n ArcTan[(2.`25.30102999566398*h)/b]"

The `25. stuff just means this is 25 digits, and can be ignored to get the final formula:

f.TaperAngle = -90. + 57.29578*math.atan((2.0*float(h))/float(b)) #expressly converting h and b to floats so we don't get any integer division operations. The formula works, but I only tested 2 or 3 different h and b combinations. If it fails, try increasing the 57.29578 slightly so as to get slightly less of an angle, but I doubt this will be necessary (not that anybody will actually use this anyway). So now you know more than you ever wanted to know about using Wolfram language.
My FreeCAD video series on youtube: https://www.youtube.com/c/mwganson
mrrclb48z
Posts: 27
Joined: Wed Feb 07, 2018 11:20 am

Re: why 1pyramid?(3)textLABEL

Postby mrrclb48z » Wed Apr 25, 2018 2:56 pm

Thank you. I coud understand a little.

Code: Select all

 
import FreeCAD
import Part
import DraftTools
import Draft
import Mesh
def myPyramidFromMesh2Solid(xIn,yIn,bIn,hIn):
        x = float(xIn)
        y = float(yIn)
        b = float(bIn)
        h = float(hIn)
        x2 = x+b*0.5
        y2 = y+b*0.5
        x3 = x+b
        y3 = y+b
        z = 0
        z2 = h  
        a =[[x,y,z], #dummy
            [x,y,z],
            [x,y3,z],
            [x3,y3,z],
            [x3,y,z],
            [x2,y2,z2]]
        m1 = Mesh.Mesh([a[2],a[5],a[3]])
        m2 = Mesh.Mesh([a[4],a[5],a[1]])
        m3 = Mesh.Mesh([a[1],a[5],a[4]])
        m4 = Mesh.Mesh([a[4],a[3],a[5]])
        m5 = Mesh.Mesh([a[1],a[2],a[3],a[1],a[3],a[4]])
        
        mesh=Mesh.Mesh()
        mesh.addMesh(m1)
        mesh.addMesh(m2)
        mesh.addMesh(m3)
        mesh.addMesh(m4)
        mesh.addMesh(m5)
        shape = Part.Shape()        
        shape.makeShapeFromMesh(mesh.Topology,0.05)
        solid = Part.makeSolid(shape).removeSplitter()
        myWedgei='x' + str(x) + 'y' + str(y)
        f = App.activeDocument().addObject("Part::Feature",myWedgei)
        f.Shape = solid
        p5 = FreeCAD.Vector(x+b*.5,y+b*.5,h)
        myText = Draft.makeText(myWedgei,p5) 
        myText.Label = myWedgei

b=1
h=1
myPyramidFromMesh2Solid(0,0,b,h)
myPyramidFromMesh2Solid(0,5,b,h)
App.ActiveDocument.recompute()
Gui.activeDocument().activeView().viewAxonometric()
Gui.SendMsgToActiveView("ViewFit")
#x0_0y0_0
#x0.0y0.0
#x0_0y5_0
#x0.0y5.0