859 lines
35 KiB
Python
859 lines
35 KiB
Python
# /**********************************************************************
|
|
# * *
|
|
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com> *
|
|
# * *
|
|
# * 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. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program 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 for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307*
|
|
# * USA *
|
|
# * *
|
|
# ***********************************************************************
|
|
|
|
import Arch
|
|
import Draft
|
|
import FreeCAD
|
|
import Mesh
|
|
import MeshPart
|
|
import Part
|
|
import numpy
|
|
import os
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from PySide import QtCore
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
from DraftTools import translate
|
|
else:
|
|
def translate(ctxt, txt):
|
|
return txt
|
|
|
|
try:
|
|
_fromUtf8 = QtCore.QString.fromUtf8
|
|
except AttributeError:
|
|
def _fromUtf8(s):
|
|
return s
|
|
|
|
from PVPlantResources import DirIcons as DirIcons
|
|
|
|
|
|
__title__ = "FreeCAD PVSyst importer"
|
|
__author__ = "Javier"
|
|
#__url__ = "http://www.freecadweb.org"
|
|
|
|
try:
|
|
# Python 2 forward compatibility
|
|
range = xrange
|
|
except NameError:
|
|
pass
|
|
|
|
|
|
def checkCollada():
|
|
"checks if collada if available"
|
|
|
|
global collada
|
|
COLLADA = None
|
|
try:
|
|
import collada
|
|
except ImportError:
|
|
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n")
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
# from ARCH:
|
|
def triangulate(shape):
|
|
"triangulates the given face"
|
|
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
|
mesher = p.GetInt("ColladaMesher", 0)
|
|
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
|
grading = p.GetFloat("ColladaGrading", 0.3)
|
|
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
|
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
|
secondorder = p.GetBool("ColladaSecondOrder", False)
|
|
optimize = p.GetBool("ColladaOptimize", True)
|
|
allowquads = p.GetBool("ColladaAllowQuads", False)
|
|
|
|
if mesher == 0:
|
|
return shape.tessellate(tessellation)
|
|
elif mesher == 1:
|
|
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
|
else:
|
|
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge,
|
|
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize,
|
|
AllowQuad=allowquads).Topology
|
|
|
|
def export(exportList, filename, tessellation=1, colors=None):
|
|
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
|
|
colors is an optional dictionary of objName:shapeColorTuple or objName:diffuseColorList elements
|
|
to be used in non-GUI mode if you want to be able to export colors. Tessellation is used when breaking
|
|
curved surfaces into triangles."""
|
|
|
|
if not checkCollada(): return
|
|
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
|
scale = p.GetFloat("ColladaScalingFactor", 1.0)
|
|
scale = scale * 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
|
|
c = p.GetUnsigned("DefaultShapeColor", 4294967295)
|
|
defaultcolor = (float((c >> 24) & 0xFF) / 255.0, float((c >> 16) & 0xFF) / 255.0, float((c >> 8) & 0xFF) / 255.0)
|
|
colmesh = collada.Collada()
|
|
colmesh.assetInfo.upaxis = collada.asset.UP_AXIS.Z_UP
|
|
|
|
# authoring info
|
|
cont = collada.asset.Contributor()
|
|
try:
|
|
author = FreeCAD.ActiveDocument.CreatedBy
|
|
except UnicodeEncodeError:
|
|
author = FreeCAD.ActiveDocument.CreatedBy.encode("utf8")
|
|
author = author.replace("<", "")
|
|
author = author.replace(">", "")
|
|
cont.author = author
|
|
ver = FreeCAD.Version()
|
|
appli = "PVPlant for FreeCAD" + ver[0] + "." + ver[1] + " build" + ver[2] + "\n"
|
|
cont.authoring_tool = appli
|
|
|
|
colmesh.assetInfo.contributors.append(cont)
|
|
colmesh.assetInfo.unitname = "meter"
|
|
colmesh.assetInfo.unitmeter = 1.0
|
|
|
|
defaultmat = None
|
|
objind = 0
|
|
scenenodes = []
|
|
|
|
# TODO: cambiar lo de objeclist. Buscar los elementos que se necesitan exportar
|
|
objectlist = Draft.get_group_contents(exportList, walls=True, addgroups=True)
|
|
objectlist = Arch.pruneIncluded(objectlist)
|
|
for obj in objectlist:
|
|
findex = numpy.array([])
|
|
m = None
|
|
if obj.isDerivedFrom("Part::Feature"):
|
|
print("exporting object ", obj.Name, obj.Shape)
|
|
new_shape = obj.Shape.copy()
|
|
new_shape.Placement = obj.getGlobalPlacement()
|
|
m = Mesh.Mesh(triangulate(new_shape))
|
|
elif obj.isDerivedFrom("Mesh::Feature"):
|
|
print("exporting object ", obj.Name, obj.Mesh)
|
|
m = obj.Mesh
|
|
elif obj.isDerivedFrom("App::Part"):
|
|
for child in obj.OutList:
|
|
objectlist.append(child)
|
|
continue
|
|
else:
|
|
continue
|
|
|
|
if m:
|
|
Topology = m.Topology
|
|
Facets = m.Facets
|
|
|
|
# vertex indices
|
|
vindex = numpy.empty(len(Topology[0]) * 3)
|
|
for i in range(len(Topology[0])):
|
|
v = Topology[0][i]
|
|
vindex[list(range(i * 3, i * 3 + 3))] = (v.x * scale, v.y * scale, v.z * scale)
|
|
|
|
# normals
|
|
nindex = numpy.empty(len(Facets) * 3)
|
|
for i in range(len(Facets)):
|
|
n = Facets[i].Normal
|
|
nindex[list(range(i * 3, i * 3 + 3))] = (n.x, n.y, n.z)
|
|
|
|
# face indices
|
|
findex = numpy.empty(len(Topology[1]) * 6, numpy.int64)
|
|
for i in range(len(Topology[1])):
|
|
f = Topology[1][i]
|
|
findex[list(range(i * 6, i * 6 + 6))] = (f[0], i, f[1], i, f[2], i)
|
|
|
|
print(len(vindex), " vert indices, ", len(nindex), " norm indices, ", len(findex), " face indices.")
|
|
vert_src = collada.source.FloatSource("cubeverts-array" + str(objind), vindex, ('X', 'Y', 'Z'))
|
|
normal_src = collada.source.FloatSource("cubenormals-array" + str(objind), nindex, ('X', 'Y', 'Z'))
|
|
geom = collada.geometry.Geometry(colmesh, "geometry" + str(objind), obj.Name, [vert_src, normal_src])
|
|
input_list = collada.source.InputList()
|
|
input_list.addInput(0, 'VERTEX', "#cubeverts-array" + str(objind))
|
|
input_list.addInput(1, 'NORMAL', "#cubenormals-array" + str(objind))
|
|
matnode = None
|
|
matref = "materialref"
|
|
if hasattr(obj, "Material"):
|
|
if obj.Material:
|
|
if hasattr(obj.Material, "Material"):
|
|
if "DiffuseColor" in obj.Material.Material:
|
|
kd = tuple([float(k) for k in obj.Material.Material["DiffuseColor"].strip("()").split(",")])
|
|
effect = collada.material.Effect("effect_" + obj.Material.Name, [], "phong", diffuse=kd,
|
|
specular=(1, 1, 1))
|
|
mat = collada.material.Material("mat_" + obj.Material.Name, obj.Material.Name, effect)
|
|
colmesh.effects.append(effect)
|
|
colmesh.materials.append(mat)
|
|
matref = "ref_" + obj.Material.Name
|
|
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
|
|
|
if not matnode:
|
|
if colors:
|
|
if obj.Name in colors:
|
|
color = colors[obj.Name]
|
|
if color:
|
|
if isinstance(color[0], tuple):
|
|
# this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors
|
|
color = color[0]
|
|
# print("found color for obj",obj.Name,":",color)
|
|
kd = color[:3]
|
|
effect = collada.material.Effect("effect_" + obj.Name, [], "phong", diffuse=kd,
|
|
specular=(1, 1, 1))
|
|
mat = collada.material.Material("mat_" + obj.Name, obj.Name, effect)
|
|
colmesh.effects.append(effect)
|
|
colmesh.materials.append(mat)
|
|
matref = "ref_" + obj.Name
|
|
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
|
elif FreeCAD.GuiUp:
|
|
if hasattr(obj.ViewObject, "ShapeColor"):
|
|
kd = obj.ViewObject.ShapeColor[:3]
|
|
effect = collada.material.Effect("effect_" + obj.Name, [], "phong", diffuse=kd, specular=(1, 1, 1))
|
|
mat = collada.material.Material("mat_" + obj.Name, obj.Name, effect)
|
|
colmesh.effects.append(effect)
|
|
colmesh.materials.append(mat)
|
|
matref = "ref_" + obj.Name
|
|
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
|
|
|
if not matnode:
|
|
if not defaultmat:
|
|
effect = collada.material.Effect("effect_default", [], "phong", diffuse=defaultcolor,
|
|
specular=(1, 1, 1))
|
|
defaultmat = collada.material.Material("mat_default", "default_material", effect)
|
|
colmesh.effects.append(effect)
|
|
colmesh.materials.append(defaultmat)
|
|
matnode = collada.scene.MaterialNode(matref, defaultmat, inputs=[])
|
|
|
|
triset = geom.createTriangleSet(findex, input_list, matref)
|
|
geom.primitives.append(triset)
|
|
colmesh.geometries.append(geom)
|
|
geomnode = collada.scene.GeometryNode(geom, [matnode])
|
|
node = collada.scene.Node("node" + str(objind), children=[geomnode])
|
|
scenenodes.append(node)
|
|
objind += 1
|
|
|
|
myscene = collada.scene.Scene("PVScene", scenenodes)
|
|
colmesh.scenes.append(myscene)
|
|
colmesh.scene = myscene
|
|
colmesh.write(filename)
|
|
|
|
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
|
|
|
|
def exportToDAE(path):
|
|
filename = path + ".dae"
|
|
|
|
def exportToPVC(path, exportTerrain = False):
|
|
filename = path + ".pvc"
|
|
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
|
|
|
from xml.etree.ElementTree import Element, SubElement
|
|
import datetime
|
|
|
|
generated_on = str(datetime.datetime.now())
|
|
|
|
site = None
|
|
tmp = FreeCAD.ActiveDocument.getObjectsByLabel('Site')
|
|
for obj in tmp:
|
|
if obj.Name.startswith("Site"):
|
|
site = obj
|
|
break
|
|
terrain = None
|
|
center = None
|
|
if not(site.Terrain is None):
|
|
terrain = site.Terrain.Mesh
|
|
center = terrain.BoundBox.Center
|
|
else:
|
|
center = FreeCAD.Vector()
|
|
|
|
try:
|
|
author = FreeCAD.ActiveDocument.CreatedBy
|
|
except UnicodeEncodeError:
|
|
author = FreeCAD.ActiveDocument.CreatedBy.encode("utf8")
|
|
|
|
author = author.replace("<", "")
|
|
author = author.replace(">", "")
|
|
ver = FreeCAD.Version()
|
|
appli = "PVPlant for FreeCAD" + ver[0] + "." + ver[1] + " build" + ver[2] + "\n"
|
|
|
|
# xml: Configure one attribute with set()
|
|
root = Element('COLLADA')
|
|
root.set('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema')
|
|
root.set('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance')
|
|
root.set('version', '1.4.1')
|
|
root.set('xmlns', 'http://www.collada.org/2005/11/COLLADASchema')
|
|
#root.append(Comment('Generated by ElementTree_csv_to_xml.py for PyMOTW'))
|
|
|
|
# xml: 1. Asset:
|
|
asset = SubElement(root, 'asset')
|
|
asset_contributor = SubElement(asset, 'contributor')
|
|
asset_contributor_autor = SubElement(asset_contributor, 'autor')
|
|
#asset_contributor_autor.text = author
|
|
asset_contributor_authoring_tool = SubElement(asset_contributor, 'authoring_tool')
|
|
#asset_contributor_authoring_tool.text = appli
|
|
asset_contributor_comments = SubElement(asset_contributor, 'comments')
|
|
asset_keywords = SubElement(asset, 'keywords')
|
|
asset_revision = SubElement(asset, 'revision')
|
|
asset_subject = SubElement(asset, 'subject')
|
|
asset_tittle = SubElement(asset, 'title')
|
|
#asset_tittle.text = FreeCAD.ActiveDocument.Name
|
|
asset_unit = SubElement(asset, 'unit')
|
|
asset_unit.set('meter', '0.001')
|
|
asset_unit.set('name', 'millimeter')
|
|
|
|
# xml: 2. library_materials:
|
|
library_materials = SubElement(root, 'library_materials')
|
|
buf = ['Frames', 'Tree_trunk', 'Tree_crown', 'Topography_mesh']
|
|
|
|
for i in range(0, len(buf)):
|
|
material = SubElement(library_materials, 'material')
|
|
material.set('id', 'Material{0}'.format(i))
|
|
material.set('name', buf[i])
|
|
material_effect = SubElement(material, 'instance_effect')
|
|
material_effect.set('url', '#Material{0}-fx'.format(i))
|
|
|
|
# xml: 3. library_effects:
|
|
library_effects = SubElement(root, 'library_effects')
|
|
buf = ['0.250000 0.500000 0.000000 1.000000',
|
|
'0.500000 0.375000 0.250000 1.000000',
|
|
'0.250000 1.000000 0.000000 1.000000',
|
|
'0.250000 1.000000 0.000000 1.000000']
|
|
|
|
for i in range(0, len(buf)):
|
|
effect = SubElement(library_effects, 'effect')
|
|
effect.set('id', 'Material{0}-fx'.format(i))
|
|
effect.set('name', 'Material{0}'.format(i))
|
|
profile_COMMON = SubElement(effect, 'profile_COMMON')
|
|
library_effects_effect_technique = SubElement(profile_COMMON, 'technique')
|
|
library_effects_effect_technique.set('sid', 'standard')
|
|
library_effects_effect_technique_lambert = SubElement(library_effects_effect_technique, 'lambert')
|
|
library_effects_effect_technique_lambert_emission = SubElement(library_effects_effect_technique_lambert,
|
|
'emission')
|
|
library_effects_effect_technique_lambert_emission_color = SubElement(
|
|
library_effects_effect_technique_lambert_emission, 'color')
|
|
library_effects_effect_technique_lambert_emission_color.set('sid', 'emission')
|
|
library_effects_effect_technique_lambert_emission_color.text = '0.000000 0.000000 0.000000 1.000000'
|
|
ambient = SubElement(library_effects_effect_technique_lambert, 'ambient')
|
|
ambient_color = SubElement(ambient, 'color')
|
|
ambient_color.set('sid', 'ambient')
|
|
ambient_color.text = '0.200000 0.200000 0.200000 1.000000'
|
|
diffuse = SubElement(library_effects_effect_technique_lambert, 'diffuse')
|
|
diffuse_color = SubElement(diffuse, 'color')
|
|
diffuse_color.set('sid', 'diffuse')
|
|
diffuse_color.text = buf[i]
|
|
transparent = SubElement(library_effects_effect_technique_lambert, 'transparent')
|
|
transparent.set('opaque', 'RGB_ZERO')
|
|
transparent_color = SubElement(transparent, 'color')
|
|
transparent_color.set('sid', 'transparent')
|
|
transparent_color.text = '0.000000 0.000000 0.000000 1.000000'
|
|
transparency = SubElement(library_effects_effect_technique_lambert, 'transparency')
|
|
transparency_value = SubElement(transparency, 'float')
|
|
transparency_value.set('sid', 'transparency')
|
|
transparency_value.text = '0'
|
|
|
|
# xml: 4. library_geometries:
|
|
library_geometries = SubElement(root, 'library_geometries')
|
|
def add_geometry(objtype, vindex, findex, objind = 0, centers = None):
|
|
|
|
isFrame = False
|
|
if objtype == 0:
|
|
geometryName = 'Frame'
|
|
referenceSTR = 'frame'
|
|
isFrame = True
|
|
elif objtype == 1:
|
|
geometryName = 'ShadowMesh' ## --> ???
|
|
referenceSTR = 'ShadowMesh' ## --> ???
|
|
elif objtype == 2:
|
|
geometryName = 'TerrainMesh'
|
|
referenceSTR = 'TerrainMesh'
|
|
|
|
geometry = SubElement(library_geometries, 'geometry')
|
|
geometry.set('id', geometryName + '{0}'.format(objind))
|
|
mesh = SubElement(geometry, 'mesh')
|
|
|
|
source = SubElement(mesh, 'source')
|
|
source.set('id', referenceSTR + '{0}MeshSource'.format(objind))
|
|
float_array = SubElement(source, 'float_array')
|
|
float_array.set('id', referenceSTR + '{0}FloatArray'.format(objind))
|
|
float_array.set('count', '{0}'.format(len(vindex)))
|
|
float_array.text = "" # vindex
|
|
for ver in vindex:
|
|
if len(float_array.text) > 0:
|
|
float_array.text += ' '
|
|
float_array.text += '{0:.6f}'.format(ver)
|
|
|
|
technique_common = SubElement(source, 'technique_common')
|
|
accessor = SubElement(technique_common, 'accessor')
|
|
accessor.set('count', '{0}'.format(len(vindex)))
|
|
accessor.set('source', '#' + referenceSTR + '{0}FloatArray'.format(objind))
|
|
accessor.set('stride', '3')
|
|
param = SubElement(accessor, 'param')
|
|
param.set('name', 'X')
|
|
param.set('type', 'float')
|
|
param = SubElement(accessor, 'param')
|
|
param.set('name', 'Y')
|
|
param.set('type', 'float')
|
|
param = SubElement(accessor, 'param')
|
|
param.set('name', 'Z')
|
|
param.set('type', 'float')
|
|
|
|
vertices = SubElement(mesh, 'vertices')
|
|
vertices.set('id', referenceSTR + '{0}VerticesSource'.format(objind))
|
|
input = SubElement(vertices, "input")
|
|
input.set('semantic', 'POSITION')
|
|
input.set('source', '#' + referenceSTR + '{0}MeshSource'.format(objind))
|
|
|
|
triangles = SubElement(mesh, 'triangles')
|
|
triangles.set('count', '0')
|
|
triangles.set('material', 'Material0')
|
|
input = SubElement(triangles, "input")
|
|
input.set('offset', '0')
|
|
input.set('semantic', 'VERTEX')
|
|
input.set('source', '#' + referenceSTR + '{0}VerticesSource'.format(objind))
|
|
p = SubElement(triangles, "p")
|
|
p.text = ''
|
|
for f in findex:
|
|
if len(p.text) > 0:
|
|
p.text += ' '
|
|
p.text += '{0}'.format(f)
|
|
|
|
if isFrame:
|
|
frame = SubElement(mesh, 'tracker_parameters' if isTracker else 'frame_parameters')
|
|
module_width = SubElement(frame, "module_width")
|
|
module_width.text = '{0}'.format(int(obj.Setup.ModuleWidth.Value))
|
|
module_height = SubElement(frame, "module_height")
|
|
module_height.text = '{0}'.format(int(obj.Setup.ModuleHeight.Value))
|
|
module_x_spacing = SubElement(frame, "module_x_spacing")
|
|
module_x_spacing.text = '{0}'.format(int(obj.Setup.ModuleColGap.Value))
|
|
module_y_spacing = SubElement(frame, "module_y_spacing")
|
|
module_y_spacing.text = '{0}'.format(int(obj.Setup.ModuleRowGap.Value))
|
|
module_manufacturer = SubElement(frame, "module_manufacturer")
|
|
module_manufacturer.text = 'generic'
|
|
module_name = SubElement(frame, "module_name")
|
|
module_name.text = 'generic'
|
|
if isTracker:
|
|
tracker_type = SubElement(frame, 'tracker_type')
|
|
tracker_type.text = 'single_axis_trackers'
|
|
axis = SubElement(frame, 'axis_vertices')
|
|
for ind in range(0, len(centers)):
|
|
array = SubElement(axis, 'float_array')
|
|
array.set('id', 'tracker{0}AxisFloatArray1'.format(ind))
|
|
array.set('count', '3')
|
|
array.text = '{0:.6f} {1:.6f} {2:.6f}'.format(centers[ind].x, centers[ind].y, centers[ind].z)
|
|
|
|
min_phi = SubElement(frame, 'min_phi')
|
|
min_phi.text = '{0}'.format(int(obj.Setup.MinPhi.Value))
|
|
max_phi = SubElement(frame, 'max_phi')
|
|
max_phi.text = '{0}'.format(int(obj.Setup.MaxPhi.Value))
|
|
min_theta = SubElement(frame, 'min_theta')
|
|
min_theta.text = '{0}'.format(0)
|
|
max_theta = SubElement(frame, 'max_theta')
|
|
max_theta.text = '{0}'.format(0)
|
|
|
|
# xml: 5. library_visual_scenes:
|
|
instance_geometry = SubElement(node, 'instance_geometry')
|
|
instance_geometry.set('url', geometryName + '{0}'.format(objind))
|
|
|
|
bind_material = SubElement(instance_geometry, 'bind_material')
|
|
technique_common = SubElement(bind_material, 'technique_common')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material0')
|
|
instance_material.set('target', '#Material0')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material1')
|
|
instance_material.set('target', '#Material1')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material2')
|
|
instance_material.set('target', '#Material2')
|
|
|
|
# xml: 5. library_visual_scenes:
|
|
library_visual_scenes = SubElement(root, 'library_visual_scenes')
|
|
visual_scene = SubElement(library_visual_scenes, 'visual_scene')
|
|
visual_scene.set('id', '')
|
|
visual_scene.set('name', '')
|
|
|
|
node = SubElement(visual_scene, 'node')
|
|
node.set('id', 'Fbx_Root')
|
|
node.set('name', 'Fbx_Root')
|
|
node.set('sid', 'Fbx_Root')
|
|
matrix = SubElement(node, 'matrix')
|
|
matrix.set('sid', 'matrix')
|
|
matrix.text = '1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000'
|
|
node = SubElement(node, 'node')
|
|
node.set('id', 'PVcase52')
|
|
node.set('name', 'PVcase52')
|
|
node.set('sid', 'PVcase52')
|
|
matrix = SubElement(node, 'matrix')
|
|
matrix.set('sid', 'matrix')
|
|
matrix.text = '1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000'
|
|
|
|
extra = SubElement(visual_scene, 'extra')
|
|
technique = SubElement(extra, 'technique')
|
|
technique.set('profile', 'MAX3D')
|
|
frame_rate = SubElement(technique, 'frame_rate')
|
|
frame_rate.text = '30.000000'
|
|
technique = SubElement(extra, 'technique')
|
|
technique.set('profile', 'FCOLLADA')
|
|
start_time = SubElement(technique, 'start_time')
|
|
start_time.text = '0.000000'
|
|
end_time = SubElement(technique, 'end_time')
|
|
end_time.text = '1.000000'
|
|
|
|
# xml: 6. scene:
|
|
scene = SubElement(root, 'scene')
|
|
instance = SubElement(scene, 'instance_visual_scene')
|
|
instance.set('url', '#')
|
|
|
|
full_list_of_objects = FreeCAD.ActiveDocument.Objects
|
|
|
|
# CASO 1 - FRAMES:
|
|
frameType = site.Frames
|
|
frame_setup = {"type": [],
|
|
"footprint": []}
|
|
for obj in frameType:
|
|
frame_setup["type"] = obj
|
|
frame_setup["footprint"] = ""
|
|
|
|
objind = 0
|
|
|
|
# TODO: revisar
|
|
for typ in frameType:
|
|
isTracker = "tracker" in typ.Proxy.Type.lower()
|
|
|
|
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
|
tmp = []
|
|
for obj in objectlist:
|
|
if obj.Name.startswith("TrackerSetup"):
|
|
continue
|
|
else:
|
|
tmp.append(obj)
|
|
objectlist = tmp.copy()
|
|
|
|
for obj in objectlist:
|
|
if obj.Setup == typ:
|
|
findex = numpy.array([])
|
|
|
|
modules = obj.Setup.Shape.SubShapes[0].SubShapes[0]
|
|
pts = []
|
|
for i in range(4):
|
|
pts.append(modules.BoundBox.getPoint(i))
|
|
|
|
# temp -----
|
|
if obj.Tilt.Value != 0:
|
|
zz = int(pts[0].z) - (obj.Setup.MainBeamHeight.Value / 2 + obj.Setup.BeamHeight.Value)
|
|
for p in pts:
|
|
p.z -= zz
|
|
new_shape = Part.Face(Part.makePolygon(pts))
|
|
new_shape.Placement.Rotation.setEulerAngles("XYZ", obj.Tilt.Value, 0, 0)
|
|
pts = []
|
|
for ver in new_shape.Vertexes:
|
|
p = ver.Point
|
|
p.z += zz
|
|
pts.append(p)
|
|
# end temp -----
|
|
|
|
new_shape = Part.Face(Part.makePolygon(pts))
|
|
new_shape.Placement = obj.Placement.copy()
|
|
m = Mesh.Mesh(triangulate(new_shape))
|
|
centers = []
|
|
|
|
if isTracker:
|
|
minLengths = []
|
|
for ed in new_shape.Edges:
|
|
minLengths.append(ed.Length)
|
|
minLength = min(minLengths)
|
|
for ed in new_shape.Edges:
|
|
if ed.Length == minLength:
|
|
centers.append(ed.CenterOfMass)
|
|
if m:
|
|
Topology = m.Topology
|
|
|
|
# 1. vertex indices
|
|
vindex = numpy.empty(len(Topology[0]) * 3)
|
|
for i in range(len(Topology[0])):
|
|
v = Topology[0][i]
|
|
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
|
(v.y - center.y) * scale)
|
|
|
|
# 2. face indices
|
|
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
|
for i in range(len(Topology[1])):
|
|
f = Topology[1][i]
|
|
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
|
|
|
add_geometry(0, vindex, findex, objind, centers if isTracker else None)
|
|
objind += 1
|
|
|
|
# CASE 2: Shadow objects
|
|
#objectlist = FreeCAD.ActiveDocument.findObjects(Label = "Tree") # TODO: Cambiar label por name
|
|
objectlist=[]
|
|
for obj in FreeCAD.ActiveDocument.Objects:
|
|
if obj.Name.startswith("Tracker"):
|
|
continue
|
|
else:
|
|
objectlist.append(obj)
|
|
objind = 0
|
|
|
|
for obj in objectlist:
|
|
findex = numpy.array([])
|
|
m = None
|
|
if obj.isDerivedFrom("Part::Feature"):
|
|
new_shape = obj.Shape.copy()
|
|
new_shape.Placement = obj.getGlobalPlacement()
|
|
m = Mesh.Mesh(triangulate(new_shape))
|
|
elif obj.isDerivedFrom("Mesh::Feature"):
|
|
m = obj.Mesh
|
|
elif obj.isDerivedFrom("App::Part"):
|
|
for child in obj.OutList:
|
|
objectlist.append(child)
|
|
continue
|
|
else:
|
|
continue
|
|
|
|
if m:
|
|
Topology = m.Topology
|
|
# vertex indices
|
|
vindex = numpy.empty(len(Topology[0]) * 3)
|
|
for i in range(len(Topology[0])):
|
|
v = Topology[0][i]
|
|
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
|
(v.y - center.y) * scale)
|
|
|
|
# face indices
|
|
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
|
for i in range(len(Topology[1])):
|
|
f = Topology[1][i]
|
|
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
|
|
|
add_geometry(1, vindex, findex, objind)
|
|
objind += 1
|
|
|
|
|
|
# CASE 3: Terrain
|
|
# TODO: ver si se puede partir en varias mesh para que trabaje más rápido
|
|
if exportTerrain:
|
|
m = terrain
|
|
if m:
|
|
Topology = m.Topology
|
|
# Facets = m.Facets
|
|
|
|
# vertex indices
|
|
vindex = numpy.empty(len(Topology[0]) * 3)
|
|
for i in range(len(Topology[0])):
|
|
v = Topology[0][i]
|
|
vindex[list(range(i * 3, i * 3 + 3))] = (-v.x * scale, v.z * scale, v.y * scale)
|
|
|
|
# face indices
|
|
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
|
for i in range(len(Topology[1])):
|
|
f = Topology[1][i]
|
|
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
|
|
|
add_geometry(2, vindex, findex)
|
|
|
|
# xml: 5. library_visual_scenes: ¿¿¿¿¿???????
|
|
'''
|
|
instance_geometry = SubElement(node, 'instance_geometry')
|
|
instance_geometry.set('url', 'TerrainMesh{0}'.format(0))
|
|
|
|
bind_material = SubElement(instance_geometry, 'bind_material')
|
|
technique_common = SubElement(bind_material, 'technique_common')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material0')
|
|
instance_material.set('target', '#Material0')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material1')
|
|
instance_material.set('target', '#Material1')
|
|
instance_material = SubElement(technique_common, 'instance_material')
|
|
instance_material.set('symbol', 'Material2')
|
|
instance_material.set('target', '#Material2')
|
|
'''
|
|
|
|
extra = SubElement(node, 'extra')
|
|
technique = SubElement(extra, 'technique')
|
|
technique.set('profile', 'FCOLLADA')
|
|
visibility = SubElement(technique, 'visibility')
|
|
visibility.text = '1.000000'
|
|
|
|
|
|
# save the file:
|
|
st = prettify(root)
|
|
#print(st)
|
|
f = open(filename, "w")
|
|
f.write(st)
|
|
f.close()
|
|
FreeCAD.Console.PrintMessage("Se ha generado correctamente el archivo PVC: ", filename)
|
|
return True
|
|
|
|
def prettify(elem):
|
|
""" Return a pretty-printed XML string for the Element. """
|
|
|
|
from xml.etree import ElementTree
|
|
from xml.dom import minidom
|
|
|
|
rough_string = ElementTree.tostring(elem, 'utf-8')
|
|
reparsed = minidom.parseString(rough_string)
|
|
return reparsed.toprettyxml(indent=" ")
|
|
|
|
def exportToH2P(path): # sólo válido para mesas
|
|
filename = path + ".h2p"
|
|
f2 = '{:.2f}'
|
|
f3 = '{:.3f}'
|
|
|
|
st = 'START\n'
|
|
# TODO: hacer un bucle para cada tipo-tamaño de estructura.
|
|
# posible solucción: un primer bucle para identificar los tipos-tamaños de estructura
|
|
#FreeCAD.ActiveDocument.findObjects
|
|
#FreeCAD.ActiveDocument.Objects
|
|
|
|
objects = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
|
grouptype = []
|
|
#for obj in objects:
|
|
grouptype.append(objects[0])
|
|
|
|
for typ in grouptype:
|
|
st += 'TABLE\n' \
|
|
'10\n'
|
|
st += f3.format(typ.Width.Value) + ',' + f3.format(typ.Length.Value) + ',' + \
|
|
f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + "\n"
|
|
#'#{ f3 %pvsyst.ilb.to_mm },#{f3 %pvsyst.irb.to_mm},#{f3 %pvsyst.itb.to_mm},' \
|
|
#'#{f3 %pvsyst.ibb.to_mm}\n'
|
|
st += '20\n'
|
|
st += str(int(typ.ModulesCols.Value)) + ',' + str(int(typ.ModulesRows.Value)) + ',' + \
|
|
str(typ.ModuleColGap.Value) + ',' + str(typ.ModuleRowGap.Value) + ',' + '30\n'
|
|
st += '30\n'
|
|
st += '1,' + f3.format(typ.ModuleWidth.Value) + ',' + f3.format(typ.ModuleHeight.Value) + ',' + \
|
|
f3.format(typ.ModuleThick.Value) + ',' + f2.format(450) + '\n' #f2.format(typ.ModulePower.Value) + '\n'
|
|
|
|
# cornerdown = find_component_sizes(group.cdef)[1]
|
|
# pvorigin = Geom::Point3d.new(cornerdown.x, cornerdown.y, 0)
|
|
# group.instances.each{ | ins | str += pvsyst_insert(ins, pvorigin)}
|
|
|
|
for obj in objects:
|
|
if obj.CloneOf == typ:
|
|
st += H2PInsert(obj)
|
|
|
|
## TODO: Bucle para buscar objetos que den sombra y el terreno. Todos llaman a H2PMesh
|
|
mesh = FreeCAD.ActiveDocument.getObjectsByLabel('Surface')[0].Mesh
|
|
st += H2PMesh(mesh, False)
|
|
st += "END\n"
|
|
|
|
# save the file:
|
|
f = open(filename, "w")
|
|
f.write(st)
|
|
f.close()
|
|
FreeCAD.Console.PrintMessage("Se ha generado el archivo PVC: ", filename)
|
|
return True
|
|
|
|
def H2PInsert(obj):
|
|
f3 = '{:.3f}'
|
|
f2 = '{:.2f}'
|
|
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
|
|
|
|
st = 'INSERT\n' \
|
|
'10\n'
|
|
st += f3.format(obj.Placement.Base.x * scale) + ',' + f3.format(obj.Placement.Base.y * scale) + ',' + \
|
|
f3.format((obj.Placement.Base.z + obj.PoleLength.Value - obj.RammingDeep.Value + 1000) * scale) + '\n'
|
|
st += '50\n'
|
|
st += f2.format(-obj.Placement.Rotation.toEuler()[0]) + '\n'
|
|
st += '55\n'
|
|
st += f2.format(obj.Placement.Rotation.toEuler()[1]) + '\n'
|
|
st += '56\n'
|
|
st += f2.format(obj.Placement.Rotation.toEuler()[2]) + '\n'
|
|
|
|
return st
|
|
|
|
def H2PMesh(mesh, typ):
|
|
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
|
|
|
|
f3 = '{:.3f}'
|
|
st = ''
|
|
if typ:
|
|
st = 'ShadowObject\nFence\n'
|
|
else:
|
|
st = 'DGM\n'
|
|
|
|
for face in mesh.Facets:
|
|
p1 = face.Points[0]
|
|
p2 = face.Points[1]
|
|
p3 = face.Points[2]
|
|
|
|
st += f3.format(p1[0] * scale) + "," + f3.format(p1[1] * scale) + "," + f3.format(p1[2] * scale) + ";"
|
|
st += f3.format(p2[0] * scale) + "," + f3.format(p2[1] * scale) + "," + f3.format(p2[2] * scale) + ";"
|
|
st += f3.format(p3[0] * scale) + "," + f3.format(p3[1] * scale) + "," + f3.format(p3[2] * scale) + "\n"
|
|
|
|
return st
|
|
|
|
class PVSystTaskPanel:
|
|
|
|
def __init__(self):
|
|
self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/exportPVSyst.ui")
|
|
self.form = self.form
|
|
|
|
def show(self):
|
|
# self.form.setWindowModality(Qt.WindowModal)
|
|
self.form.show()
|
|
|
|
def accept(self):
|
|
import datetime
|
|
x = datetime.datetime.now()
|
|
date = x.strftime("%Y%m%d%H%M%S")
|
|
overwrite = True
|
|
|
|
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "PVSyst")
|
|
if not os.path.exists(path):
|
|
os.makedirs(path)
|
|
|
|
name = FreeCAD.ActiveDocument.Label
|
|
#if not overwrite:
|
|
name = date + "-" + name
|
|
filename = os.path.join(path, name)
|
|
|
|
#if self.form.cbDAE.isChecked():
|
|
# exportToDAE(filename)
|
|
|
|
if self.form.cbPVC.isChecked():
|
|
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
|
|
|
if self.form.cbH2P.isChecked():
|
|
exportToH2P(filename)
|
|
|
|
FreeCADGui.Control.closeDialog()
|
|
return True
|
|
|
|
def reject(self):
|
|
FreeCADGui.Control.closeDialog()
|
|
return True
|
|
|
|
'''class _CommandExportToPVSyst:
|
|
"Export to PVSyst"
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap': str(os.path.join(DirIcons, "PVsyst.png")),
|
|
'Accel': "E, P",
|
|
'MenuText': QT_TRANSLATE_NOOP("Outputs", "Export to PVSyst"),
|
|
'ToolTip': QT_TRANSLATE_NOOP("Outputs", "Exportar a PVSyst")}
|
|
|
|
def Activated(self):
|
|
taskd = _PVSystTaskPanel()
|
|
# taskd.show()
|
|
FreeCADGui.Control.showDialog(taskd)
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
if FreeCAD.GuiUp:
|
|
FreeCADGui.addCommand('ExportToPVSyst', _CommandExportToPVSyst())'''
|