I have writen a macro (see at the end) based on the work of Microelly on Import_image for the geodata WB. I was helped by Mario which helped to implement a search file function. I am sorry for the professional and advanced coders whose will read my codes... yes it is awfull. but works. but awfull but works but....
You need to run A) an open document B) having the partWorkBench open C) a png picture (no other format sorry)
AIM: creating a pin art like object from a PNG picture using the alpha value of pixel to define height of single pins. My first approach was reading the picture a) getting the size x,y of the picture b) creating a support part named SOCKET of size x*y c) creating a pin at each pixel location. d) use the luminance (or alpha value) as a height value
for each pin added I made a fusion object with the socket (so a boolean yeahhh hum ok but I cannot find better ATM) then make a copy of it with the name fusion copy. remove the socket. copy the fusion_copy as socket. simplify socket. remove the fusion copy. (you follow? I try at each fusion to have it as socket and getting it simpler).
it worked for a 10x20 png picture and for pins that touched each others. (In that it is then like a heightmap)
If I do not implement the fusion it works for bigger pictures (up to 6000 single Box Shapes). It then worked for a 60x100 pixels
Which I would like to "get" together but it freeze when doing make compound or fuse. So in the macro I deactivate it ATM. but you can run it to see it in action on a very small picture (like a 10x10 pixel PNG image)
I could achieve without fusion step also to do the pin art with small needles like Cylinders (using makecylinders with a Radius 0.2 instead of makeBox)
But I am not satisfyied. I am looking for a better solution that can manage a uge amount of shapes.
My second thought:
Basically How do work a pin art? you have a given support part and many pins. Then you push the pins in one direction. (instead of inserting each pins at the right height one by one)
So Here what I thought: a)Read a PNG picture b)Get the size of a PNG picture (x,y) c) Create a array of shapes of dimension (1x1x11) with arrayx=x and arrayy=y.
until this it looks promissing and is feasible with scripting and modifyin my macro.
d) displace each single pins at the right height following the luminance as guide for the displacement. e) make a simple copy of the array and apply simplify. I bet this would be the best way to go.
Why? because I tried to manipulate an array of 100 x 100 of a box it basically works whereas try to do so with 100x60 box - freecad freezed.
However this part from d) I cannot do it as I cannot enter the information of subobject placement or Subshape dimensions.
By the way, Blender another 3D program has a heightmap option where you can make something similar to my approach here.
If anybody as an insight or a recommendation I would love to try.
A further point about my macro: I would love to implement a lot of things to use --> through a GUI : decreasing the resolution - proposing the choice of the shape (as well as the width-lenght or radius value). Also an option using a closed sketch which would be extruded can be an option which has been discussed with Mario52.
PS: If you wanna help and modify further(make the code better also is an option) please contact me if implemented your collaboration work will be added at the beginning below Mario52.
Thanks for your feedback.
Code: Select all
# -*- coding: utf-8 -*-
##Pin Art from Picture v01from date 24 sept 2018
##Made By Dr_Froggy_rD based on very helpful work of Microelly - Geodata WB geodat import image to nurbs/pcl
##Openfile tool provided by Mario52
##As for the Geodat WB - I prefer to follow the same as used by Microelly: GNU Lesser General Public License (LGPL)
import PySide
from PySide import QtGui ,QtCore
from PySide.QtGui import *
from PySide.QtCore import *
##path###########################################################################
global path
#path = FreeCAD.ConfigGet("AppHomePath") # path FreeCAD installation
path = FreeCAD.ConfigGet("UserAppData") # path FreeCAD User data
#path = "your path" # your directory path
#param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")# macro path
#path = param.GetString("MacroPath","") + "/" # macro path
path = path.replace("\\","/") # convert the "\" to "/"
#################################################################################
OpenName = ""
OpenName, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Read a file png", path, "*.png") #PySide
# "here the text displayed on windows" "here the filter (extension)"
#####
# exemple pour plusieurs extensions séparées ouverture fichier
# OpenName, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open a file", path, "FCInfo (*.FCInfo);;Csv (*.csv);;Ascii (*.asc);;TXT (*.txt)") # PySide
# OpenName, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open a file", path, "STEP with colors (*.step *.stp);;"
# "Iges format (*.iges *.igs);;") # PySide
# "here the text displayed on windows" "here the filter (extension)"
#####
if OpenName == "": # if the name file are not selected then Abord process
App.Console.PrintMessage("Process aborted"+"\n")
else:
App.Console.PrintMessage("Read "+OpenName+"\n") # text displayed to Report view (Menu > View > Report view checked)
try: # detect error to read file
file = open(OpenName, "r") # open the file selected to read (r) # (rb is binary)
try: # detect error ...
# here your code mettre votre code ici
print "here your code"
op = OpenName.split("/") # decode the path
op2 = op[-1].split(".") # decode the file name
nomF = op2[0] # the file name are isolated
App.Console.PrintMessage(str(nomF)+"\n") # the file name are displayed
for ligne in file: # read the file
X = ligne.rstrip('\n\r') #.split() # decode the line
print X # print the line in report view other method
# (Menu > Edit > preferences... > Output window > Redirect internal Python output (and errors) to report view checked)
except Exception: # if error detected to read
App.Console.PrintError("Error read file "+"\n") # detect error ... display the text in red (PrintError)
finally: # if error detected to read ... or not error the file is closed
file.close() # if error detected to read ... or not error the file is closed
except Exception: # if one error detected to read file
App.Console.PrintError("Error in Open the file "+OpenName+"\n") # if one error detected ... display the text in red (PrintError)
##############################################
import urllib2
from geodat.say import *
import Points
from FreeCAD import Base
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
#f=urllib2.urlopen("https://matplotlib.org/_static/logo2.png")
#f='C:/Temp/KleinMuster2.png'
f=OpenName
f=OpenName
socket=mpimg.imread(f)
h = np.shape(socket)[0]
w = np.shape(socket)[1]
print (h)
print (w)
socket = FreeCAD.ActiveDocument.addObject("Part::Feature","Socket")
socketBox = Part.makeBox(w+2,h+2,10,Base.Vector(-1,-1,-10))
socket.Shape = socketBox
#mytestShape = FreeCAD.ActiveDocument.addObject("Part::Feature","TestShape")
#testShape = Part.makeBox(w*0.02,h*0.01,30,Base.Vector(00,00,00))
#mytestShape.Shape = testShape
#f=urllib2.urlopen("https://matplotlib.org/_static/logo2.png")
img=mpimg.imread(f)
if len(img.shape) == 2:
lum_img = img
else:
lum_img=img[:,:,2]
lum_img=0.3*img[:,:,0]+0.3*img[:,:,1]+0.3*img[:,:,2]
plt.imshow(lum_img)
plt.show(img)
(lu,lv)=lum_img.shape
print("OK")
App.ActiveDocument.recompute()
pinart=[]
uu=[]
for u in range(lu):
ul=[]
for v in range(lv):
# p=FreeCAD.Vector(ky*v,-kx*u,bz-kz*lum_img[u,v])
r=0.001
#p=Part.makeBox(1,1,21-20*lum_img[u,v],Base.Vector(v,u,00))
#''' choose one between inverse 21-20* lum_img on the line right above
# or below for the straight one'''
p=Part.makeBox(1,1,1+20*lum_img[u,v],Base.Vector(v,u,00))
Part.show(p)
#remove the ''' below to activate the iterative fusion and at the end of this paragraph
'''App.activeDocument().addObject("Part::MultiFuse","Fusion_socket")
App.activeDocument().Fusion_socket.Shapes = [App.activeDocument().Socket,App.activeDocument().Shape,]
App.activeDocument().recompute()
App.ActiveDocument.addObject('Part::Feature','Copy_fusion_socket').Shape=App.ActiveDocument.Fusion_socket.Shape
App.ActiveDocument.ActiveObject.Label=App.ActiveDocument.Fusion_socket.Label
App.activeDocument().removeObject("Socket")
App.activeDocument().removeObject("Fusion_socket")
App.activeDocument().removeObject("Shape")
App.activeDocument().recompute()
App.ActiveDocument.addObject('Part::Feature','Socket').Shape=App.ActiveDocument.Copy_fusion_socket.Shape.removeSplitter()
App.ActiveDocument.ActiveObject.Label=App.ActiveDocument.Socket.Label
App.activeDocument().recompute()
App.activeDocument().removeObject("Copy_fusion_socket")
App.activeDocument().recompute()'''
#remove the ''' above to activate the iterative fusion and at the beginning of this paragraph
# show the points
App.ActiveDocument.ActiveObject.ViewObject.ShapeColor=(0.9,0.6,0.80)
Gui.updateGui()