primera subida

This commit is contained in:
2025-01-28 00:04:13 +01:00
commit a91237c3e1
577 changed files with 457418 additions and 0 deletions

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

8
.idea/PVPlant.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.10" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/PVPlant.iml" filepath="$PROJECT_DIR$/.idea/PVPlant.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,201 @@
def getcenter(lEdges, r):
import math
rndEdges = lEdges[0:2]
rndEdges = Part.__sortEdges__(rndEdges)
lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]]
U1 = lVertexes[0].Point.sub(lVertexes[1].Point)
U1.normalize()
U2 = lVertexes[2].Point.sub(lVertexes[1].Point)
U2.normalize()
alpha = U1.getAngle(U2)
# Edges have same direction
'''if (round(alpha, 0) == 0) or (round(alpha - math.pi, 0) == 0):
pt1 = FreeCAD.Vector(U1)
x = pt1.x
pt1.x = pt1.y
pt1.y = -x
return rndEdges
pointsDirection = lineSelected.discretize(Number=500) # discretize the path line first selection
v = pointsDirection[0].sub(pointsDirection[1]) # avec vecteurs 1 et 2 (direction debut ligne)
r = App.Rotation(App.Vector(0, 0, 1), v)'''
dToCenter = r / math.sin(alpha / 2.0)
# dToTangent = (dToCenter ** 2 - r ** 2) ** 0.5
dirVect = U2.add(U1)
dirVect.normalize()
dirVect.scale(dToCenter, dToCenter, dToCenter)
pt1 = lVertexes[1].Point.add(dirVect)
pt2 = -1 * pt1
pt2.z = pt1.z
print(pt1, " - ", pt2)
return [pt1, pt2]
# from DRAFT
def split_wire(wire, newPoint, edgeIndex):
import Draft
wire1Points = []
wire2Points = []
print("New Point: ", newPoint)
print("edgeIndex: ", edgeIndex)
for index, point in enumerate(wire.Points):
print("index: ", index, " - point: ", point)
if index == edgeIndex:
wire1Points.append(point)
wire1Points.append(wire.Placement.inverse().multVec(newPoint))
wire2Points.append(newPoint)
# wire2Points.append(wire.Placement.multVec(point))
elif index < edgeIndex:
wire1Points.append(point)
elif index > edgeIndex:
wire2Points.append(wire.Placement.multVec(point))
if len(wire1Points) == 1:
wire1Points.insert(0, wire.Points[0])
if len(wire2Points) == 1:
wire2Points.appen(wire.Points[-1])
wire.Points = wire1Points
wire2 = Draft.makeWire(wire2Points, placement=wire.Placement)
FreeCAD.ActiveDocument.recompute()
wires = [wire, wire2]
return sorted(wires, key=lambda obj: obj.Length, reverse=True)
def SplitTrench(trench, point):
if not trench or not point:
return None
import Part
wire = trench.Base
vertex = Part.Vertex(point)
'''Find the minimum distance to another shape:
- distToShape(shape) -> (dist, vectors, infos) dist is the minimum distance, in mm (float value).
vectors is a list of pairs of App.Vector. Each pair corresponds to solution.
Example:
[(Vector (2.0, -1.0, 2.0), Vector (2.0, 0.0, 2.0)), (Vector (2.0,-1.0, 2.0), Vector (2.0, -1.0, 3.0))]
- First vector is a point on self, second vector is a point on s.
infos contains additional info on the solutions. It is a list of tuples: (topo1, index1, params1, topo2, index2, params2)
- topo1, topo2 are strings identifying type of BREP element: 'Vertex', 'Edge', or 'Face'.
- index1, index2 are indexes of the elements (zero-based).
- params1, params2 are parameters of internal space of the elements. For vertices, params is None.
For edges, params is one float, u. For faces, params is a tuple (u,v).
(719762.0578530617,
[(Vector (225057.375, 783988.625, 715062.875), Vector (306961.4605149741, 778138.3688035253, 0.0))],
[('Vertex', 0, None, 'Edge', 0, 79697.52668425611)])
'''
(dist, vectors, infos) = vertex.distToShape(wire.Shape)
if not dist:
return None
Location = FreeCAD.Vector(vectors[0][1])
if not Location:
return None
Normal = None
edind = 0
if infos[0][3] == 'Vertex':
vec = Location.sub(point)
Normal = FreeCAD.Vector(vec[1], -vec[0], vec[2])
edind = int(infos[0][4]) - 1
elif infos[0][3] == 'Edge':
ed = wire.Shape.Edges[int(dist[2][0][4])]
Normal = ed.Vertexes[1].Point.sub(ed.Vertexes[0].Point)
edind = int(infos[0][4])
wires = split_wire(wire, Location, edind)
trench.Base = wires[0]
trench1 = makeTrench(wire[1])
return Location, [trench, trench1], node
def JoinTrench(trench1, trench2):
if not trench1 or not trench2:
return None
class CommandSplitTrench: # V1:
"""Gui command for the Line tool."""
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")),
'MenuText': "Trench",
'Accel': "C, T",
'ToolTip': "Creates a Trench object from setup dialog."}
def IsActive(self):
return (not (FreeCAD.ActiveDocument is None) and
not (FreeCAD.ActiveDocument.getObject("Trench") is None))
def Activated(self):
"""Execute when the command is called."""
sel = FreeCADGui.Selection.getSelection()
done = False
if len(sel) > 0:
import Draft
for obj in sel:
if Draft.getType(obj) == "Wire":
FreeCAD.ActiveDocument.openTransaction("Create Trench")
makeTrench(obj)
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
done = True
break
if not done:
taskd = TrenchTaskPanel()
if taskd:
FreeCADGui.Control.showDialog(taskd)
else:
print(" No ha sido posible crear el formulario")
class CommandJoinTrench: # V1:
"""Gui command for the Line tool."""
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap': str(os.path.join(DirIcons, "trenchsplit.svg")),
'MenuText': "Semi-Automatic Trench Generator",
'Accel': "T, S",
'ToolTip': "Creates a Trench object from setup dialog."}
def IsActive(self):
return (not (FreeCAD.ActiveDocument is None) and
not (FreeCAD.ActiveDocument.getObject("Trench") is None))
def Activated(self):
"""Execute when the command is called."""
semi = semiAutomaticTrench()
if FreeCAD.GuiUp:
class CommandTrenchGroup:
def GetCommands(self):
return tuple(['PVPlantSplitTrench',
'PVPlantJoinTrench',
])
def GetResources(self):
return {'MenuText': 'Trench operations',
'ToolTip': 'Trench operations'
}
def IsActive(self):
active = not (FreeCAD.ActiveDocument is None)
terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None)
active = active and terrain
if terrain:
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
return active
FreeCADGui.addCommand('PVPlantTrench', CommandTrench())
FreeCADGui.addCommand('PVPlantSemiAutomaticTrench', CommandSemiAutomaticTrench())
FreeCADGui.addCommand('Trenches', CommandTrenchGroup())

View File

@@ -0,0 +1,23 @@
#-------------------------------------------------
#
# Project created by QtCreator 2016-04-12T20:54:56
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = CompositeListWidgetExample
TEMPLATE = app
SOURCES += main.cpp\
qmainwidget.cpp \
thewidgetitem.cpp
HEADERS += qmainwidget.h \
thewidgetitem.h
FORMS += qmainwidget.ui \
thewidgetitem.ui

View File

@@ -0,0 +1,5 @@
# Composite List Widget Example [Quick Qt 3]
![img](https://cdn-images-1.medium.com/max/880/1*1Q6ypFyuBSfR4uccKeCdqg.png)

View File

@@ -0,0 +1,10 @@
#include "qmainwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWidget w;
w.show();
return a.exec();
}

View File

@@ -0,0 +1,38 @@
#include "qmainwidget.h"
#include "ui_qmainwidget.h"
QMainWidget::QMainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::QMainWidget)
{
ui->setupUi(this);
}
QMainWidget::~QMainWidget()
{
delete ui;
}
void QMainWidget::on_addBtn_clicked()
{
//Creating a new list widget item whose parent is the listwidget itself
QListWidgetItem *listWidgetItem = new QListWidgetItem(ui->listWidget);
//Adding the item to the listwidget
ui->listWidget->addItem (listWidgetItem);
//Creating an object of the designed widget which is to be added to the listwidget
TheWidgetItem *theWidgetItem = new TheWidgetItem;
//Making sure that the listWidgetItem has the same size as the TheWidgetItem
listWidgetItem->setSizeHint (theWidgetItem->sizeHint ());
//Finally adding the itemWidget to the list
ui->listWidget->setItemWidget (listWidgetItem, theWidgetItem);
}
void QMainWidget::on_delBtn_clicked()
{
//Delete selected item from the listWidget
delete ui->listWidget->currentItem ();
}

View File

@@ -0,0 +1,34 @@
#ifndef QMAINWIDGET_H
#define QMAINWIDGET_H
#include <QWidget>
#include <QListWidget>
#include <QListWidgetItem>
#include "thewidgetitem.h"
#include <QVector>
#include <QDebug>
namespace Ui {
class QMainWidget;
}
class QMainWidget : public QWidget
{
Q_OBJECT
public:
explicit QMainWidget(QWidget *parent = 0);
~QMainWidget();
QVector <TheWidgetItem*> itemVec;
private slots:
void on_addBtn_clicked();
void on_delBtn_clicked();
private:
Ui::QMainWidget *ui;
};
#endif // QMAINWIDGET_H

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QMainWidget</class>
<widget class="QWidget" name="QMainWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>459</width>
<height>329</height>
</rect>
</property>
<property name="windowTitle">
<string>QMainWidget</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="addBtn">
<property name="text">
<string>Add Widget Item</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="delBtn">
<property name="text">
<string>Delete Selected Item</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,26 @@
#include "thewidgetitem.h"
#include "ui_thewidgetitem.h"
TheWidgetItem::TheWidgetItem(QWidget *parent) :
QWidget(parent),
ui(new Ui::TheWidgetItem)
{
ui->setupUi(this);
}
TheWidgetItem::~TheWidgetItem()
{
delete ui;
}
void TheWidgetItem::on_pressThisBtn_clicked()
{
//Getting text from lineEdit and setting it to the label
ui->label->setText (ui->lineEdit->text ());
}
void TheWidgetItem::on_horizontalSlider_valueChanged(int value)
{
//Connecting slider with the progressbar
ui->progressBar->setValue (value);
}

View File

@@ -0,0 +1,32 @@
#ifndef THEWIDGETITEM_H
#define THEWIDGETITEM_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QSlider>
#include <QProgressBar>
#include <QLabel>
namespace Ui {
class TheWidgetItem;
}
class TheWidgetItem : public QWidget
{
Q_OBJECT
public:
explicit TheWidgetItem(QWidget *parent = 0);
~TheWidgetItem();
private slots:
void on_pressThisBtn_clicked();
void on_horizontalSlider_valueChanged(int value);
private:
Ui::TheWidgetItem *ui;
};
#endif // THEWIDGETITEM_H

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TheWidgetItem</class>
<widget class="QWidget" name="TheWidgetItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>363</width>
<height>83</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Write something</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pressThisBtn">
<property name="text">
<string>Press this</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Text from lineEdit</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="horizontalSlider">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

6
DEM/importLidar.py Normal file
View File

@@ -0,0 +1,6 @@
import numpy as np
import laspy as lpinput_path="gdrive/My Drive/10-MEDIUM/DATA/Point Cloud Sample/"
dataname="NZ19_Wellington.las"point_cloud=lp.file.File(input_path+dataname+".las", mode="r")
points = np.vstack((point_cloud.x, point_cloud.y, point_cloud.z)).transpose()
colors = np.vstack((point_cloud.red, point_cloud.green, point_cloud.blue)).transpose()

103
DataImport.ui Normal file
View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formDataImport</class>
<widget class="QWidget" name="formDataImport">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Importar datos desde:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="radioInternet">
<property name="text">
<string>Importar nube desde Google</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioFile">
<property name="text">
<string>Nube de puntos desde archivo</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_3">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Archivo:</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Archivo:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_4"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="buttonSel">
<property name="text">
<string>Sel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,333 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import PVPlantResources
def makeCable(base = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Cable")
Cable(obj)
ViewProviderCable(obj.ViewObject)
if base:
obj.Base = base
return obj
class Cable(ArchComponent.Component):
"A Base Frame Obcject - Class"
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
# General:
if not ("Manufacturer" in pl):
obj.addProperty("App::PropertyString",
"Manufacturer",
"General",
"Connection")
if not ("Factory" in pl):
obj.addProperty("App::PropertyString",
"Factory",
"General",
"Connection ")
if not ("DesignStandard" in pl):
obj.addProperty("App::PropertyString",
"DesignStandard",
"General",
"Connection ")
if not ("CableDesignation" in pl):
obj.addProperty("App::PropertyString",
"CableDesignation",
"General",
"Connection ").CableDesignation="RH5Z1-OL"
if not ("MaximumVoltage" in pl):
obj.addProperty("App::PropertyElectricPotential",
"MaximumVoltage",
"General",
"Connection ").MaximumVoltage="30kV"
if not ("MaxTemperatureForContinuousOperation" in pl):
obj.addProperty("App::PropertyInteger",
"MaxTemperatureForContinuousOperation",
"General",
"Connection ").MaxTemperatureForContinuousOperation=95
if not ("MaxTemperatureDuringEmergencyConditions" in pl):
obj.addProperty("App::PropertyInteger",
"MaxTemperatureDuringEmergencyConditions",
"General",
"Connection ").MaxTemperatureDuringEmergencyConditions=105
if not ("MaxTemperatureDuringShortCircuit" in pl):
obj.addProperty("App::PropertyInteger",
"MaxTemperatureDuringShortCircuit",
"General",
"Connection ").MaxTemperatureDuringShortCircuit=250
# Conductor:
if not ("Material" in pl):
obj.addProperty("App::PropertyEnumeration",
"Material",
"Conductor",
"Connection ").Material=["Copper", "Aluminium"]
if not ("Standard" in pl):
obj.addProperty("App::PropertyString",
"Standard",
"Conductor",
"Connection ").Standard = "IEC 60228"
if not ("CrossSection" in pl):
obj.addProperty("App::PropertyArea",
"CrossSection",
"Conductor",
"Connection ").CrossSection = 95
if not ("MaximumConductorDiameter" in pl):
obj.addProperty("App::PropertyLength",
"MaximumConductorDiameter",
"Conductor",
"Connection ")
if not ("MinimumConductorDiameter" in pl):
obj.addProperty("App::PropertyLength",
"MinimumConductorDiameter",
"Conductor",
"Connection ")
if not ("MaximumResistance" in pl):
obj.addProperty("App::PropertyInteger",
"MaximumResistance",
"Conductor",
"Connection ")
# Insulation:
if not ("InsulationMaterial" in pl):
obj.addProperty("App::PropertyEnumeration",
"InsulationMaterial",
"Insulation",
"Connection ").InsulationMaterial=["HEPR", "XLPE"]
if not ("InsulationStandard" in pl):
obj.addProperty("App::PropertyString",
"InsulationStandard",
"Insulation",
"Connection ").InsulationStandard = "IEC 60502-2"
if not ("InsulationNominalThickness" in pl):
obj.addProperty("App::PropertyLength",
"InsulationNominalThickness",
"Insulation",
"Sección").InsulationNominalThickness = 7.25
if not ("InsulationMinimumThickness" in pl):
obj.addProperty("App::PropertyLength",
"InsulationMinimumThickness",
"Insulation",
"Sección").InsulationMinimumThickness = 6.43
if not ("InsulationResistance" in pl):
obj.addProperty("App::PropertyInteger",
"InsulationResistance",
"Insulation",
"Sección").InsulationResistance = 3670000
# Outer semi-conductive layer:
if not ("OuterMaterial" in pl):
obj.addProperty("App::PropertyString",
"OuterMaterial",
"OuterLayer",
"Connection ").OuterMaterial = "Semicon. compound"
if not ("OuterNominalThickness" in pl):
obj.addProperty("App::PropertyLength",
"OuterNominalThickness",
"OuterLayer",
"Sección").OuterNominalThickness = 0.5
if not ("OuterMinimumThickness" in pl):
obj.addProperty("App::PropertyLength",
"OuterMinimumThickness",
"OuterLayer",
"Sección").OuterMinimumThickness = 0.5
# algo
if not ("ExternalDiameter" in pl):
obj.addProperty("App::PropertyDistance",
"ExternalDiameter",
"Cable",
QT_TRANSLATE_NOOP("App::Property", "Diameter")).ExternalDiameter = 6.6
if not ("Section" in pl):
obj.addProperty("App::PropertyArea",
"Section",
"Cable",
QT_TRANSLATE_NOOP("App::Property", "Sección"))
if not ("Core" in pl):
obj.addProperty("App::PropertyEnumeration",
"Core",
"Cable",
"Core").Core = ["1", "2", "3", ]
if not ("RadiusOfCurvature" in pl):
obj.addProperty("App::PropertyDistance",
"RadiusOfCurvature",
"Cable",
QT_TRANSLATE_NOOP("App::Property", "Diameter")).RadiusOfCurvature = 100
self.Type = "Cable"
obj.Proxy = self
obj.IfcType = "Cable Segment"
obj.setEditorMode("IfcType", 1)
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and object properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
obj.Proxy = self
def onChanged(self, obj, prop):
''' Do something when a property has changed '''
def getPoint(self, val):
if val.Proxy.Type == 'String':
return val.StringPoles[0]
elif val.Proxy.Type == 'StringBox':
input = val.Shape.SubShapes[2].SubShapes[0]
return input.CenterOfMass
else:
return val.Placement.Base
def execute(self, obj):
import Part, DraftGeomUtils
import Draft
if obj.Base:
w = obj.Base.Shape.SubShapes[1].SubShapes[0]
w = DraftGeomUtils.filletWire(w, 150)
else:
return
"""if obj.Base:
# Si tiene ruta, dibujar ruteado
import PVPlantTrench as trench
if isinstance(obj.Base, trench.Trench):
w = obj.Base.Shape.SubShapes[0]
else:
w = obj.Base.Shape
elif obj.From and obj.Name:
'''line = Part.LineSegment()
line.StartPoint = getPoint(obj.From)
line.EndPoint = getPoint(obj.To)
w = Part.Wire(line.toShape())'''
w = Part.makePolygon([self.getPoint(obj.From), self.getPoint(obj.To)])
else:
return"""
r = obj.ExternalDiameter.Value / 2
p = Part.Wire([Part.Circle(FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(0, 1, 0),
r).toShape()])
c = obj.Offset
c.x -= r
c.z += r
v1 = w.Vertexes[1].Point - w.Vertexes[0].Point
v2 = DraftGeomUtils.getNormal(p)
p.Placement.Base = w.Vertexes[0].Point + c
p.Placement.Rotation = FreeCAD.Rotation(v2, v1)
obj.Shape = w.makePipeShell([p], True, False, 0)
obj.Distance = w.Length
class ViewProviderCable(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "cable.svg"))
class CommandCable:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "cable.svg")),
'Accel': "E, C",
'MenuText': QT_TRANSLATE_NOOP("Placement", "Cable"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
def Activated(self):
import Draft
sel = FreeCADGui.Selection.getSelection()
wire = None
for obj in sel:
if Draft.getType(obj) == "Wire":
wire = obj
break
makeCable(wire)
FreeCAD.ActiveDocument.recompute()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantCable', CommandCable())

View File

@@ -0,0 +1,308 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import PVPlantResources
__dir__ = os.path.join(PVPlantResources.__dir__, "Electrical", "Cable")
def makeElectricalLine(base = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ElectricalLine")
ElectricalLine(obj)
ViewProviderElectricalLine(obj.ViewObject)
if base:
obj.Base = base
'''try:
folder = FreeCAD.ActiveDocument.Trenches
except:
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Trenches')
folder.Label = "Trenches"
folder.addObject(obj)'''
return obj
class ElectricalLine(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
if not ("Paths" in pl):
obj.addProperty("App::PropertyLinkSubList",
"Paths",
"General",
"Connection")
if not ("offset" in pl):
obj.addProperty("App::PropertyVector",
"offset",
"General",
"Connection")
if not ("From" in pl):
obj.addProperty("App::PropertyLink",
"From",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "Connection "))
if not ("To" in pl):
obj.addProperty("App::PropertyLink",
"To",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "Connection "))
if not ("Cable" in pl):
obj.addProperty("App::PropertyLink",
"Cable",
"Line",
QT_TRANSLATE_NOOP("App::Property", "Connection "))
if not ("Phases" in pl):
obj.addProperty("App::PropertyEnumeration",
"Phases",
"Line",
"Connection").Phases = ["2", "3"]
obj.Phases = "3"
if not ("LineType" in pl):
obj.addProperty("App::PropertyEnumeration",
"LineType",
"Line",
"Connection").LineType = ["AC", "DC"]
if not ("Setup" in pl):
obj.addProperty("App::PropertyEnumeration",
"Setup",
"Line",
"Connection").Setup = ["Trifoil", "Parallel"]
obj.Proxy = self
obj.IfcType = "Cable Segment"
obj.setEditorMode("IfcType", 1)
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and object properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def updateOutputProperties(self, obj):
''' '''
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
if prop == "Cable":
''' '''
if hasattr(obj.Cable, "Proxy") and (obj.Cable.Proxy.Type == "Cable"):
''' '''
else:
obj.Cable = None
if prop == "LineType":
obj.Phases = "2" if obj.LineType == "DC" else "3"
obj.setEditorMode("Phases", obj.LineType == "DC")
self.updateOutputProperties(self, obj)
def execute(self, obj):
import Part, DraftGeomUtils
w = self.generatePath(obj)
if (not w) and (not obj.Cable):
return
r = obj.Cable.ExternalDiameter.Value / 2
sh = Part.makeCompound([])
cnt = int(obj.Phases)
if obj.Phases == "3" and obj.Setup == "Parallel":
offsets = [FreeCAD.Vector(-2 * r, 0, r),
FreeCAD.Vector(2 * r, 0, r),
FreeCAD.Vector(0, 0, r)]
else:
offsets = [FreeCAD.Vector(-r, 0, r),
FreeCAD.Vector(r, 0, r),
FreeCAD.Vector(0, 0, r * (1 + 3 ** 0.5))]
for i in range(cnt):
ph = Part.Wire([Part.Circle(offsets[i] + obj.offset,
FreeCAD.Vector(0, 1, 0),
r).toShape()])
v1 = w.Vertexes[1].Point - w.Vertexes[0].Point
if v1.y < 0:
v1 = -v1
v2 = DraftGeomUtils.getNormal(ph)
ph.Placement.Base = w.Vertexes[0].Point
ph.Placement.Rotation = FreeCAD.Rotation(v2, v1)
sh.add(w.makePipeShell([ph, ], True, False, 0))
obj.Shape = sh
def generatePath(self, obj):
import Utils.PVPlantFillets as fillets
result = None
# 1. sort
if not obj.Base:
return None
print(obj.Paths)
w = obj.Base.Shape.SubShapes[1].SubShapes[0]
w = fillets.filletWire(w, obj.Cable.RadiusOfCurvature)
return w
class ViewProviderElectricalLine(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
self.Object = None
vobj.Proxy = self
def attach(self, vobj):
''' Create Object visuals in 3D view. '''
self.Object = vobj.Object
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "electricalline.png"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Cable:
children.append(self.Object.Cable)
return children
class ElectricalLineTaskPanel:
def __init__(self, obj=None):
self.new = False
self.selection = None
self.selectionViewObject = None
self.obj = obj
if obj is None:
self.new = True
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(__dir__, "PVPlantElectricalLine.ui"))
'''self.form.buttonAddLayer.clicked.connect(self.addLayer)
self.form.buttonDeleteLayer.clicked.connect(self.removeLayer)
self.form.buttonUp.clicked.connect(self.moveUp)
self.form.buttonDown.clicked.connect(self.moveDown)'''
#self.observer = SelectionObserver.SelObserver()
FreeCADGui.Selection.addObserver(self)
def addSelection(self, document, object, element, position): # Selection
''' '''
obj = FreeCAD.getDocument(document).getObject(object)
if hasattr(obj, "Proxy"):
if obj.Proxy.Type == "Trench":
self.selection = obj
self.selectionViewObject = obj.ViewObject.DisplayMode
obj.ViewObject.DisplayMode = "Wireframe"
self.TrechDialog = FreeCADGui.PySideUic.loadUi(os.path.join(__dir__, "PVPlantElectricalLineDialog.ui"))
self.TrechDialog.labelTitle.setText(obj.Name)
self.TrechDialog.spinBox.setMaximum(obj.Cables)
self.TrechDialog.buttonAccept.clicked.connect(self.addTrenchRoute)
self.TrechDialog.show()
def clearSelection(self, doc):
''' '''
if self.selectionViewObject:
self.selection.ViewObject.DisplayMode = self.selectionViewObject
self.selectionViewObject = None
pass
def addTrenchRoute(self):
val = self.TrechDialog.spinBox.value
FreeCADGui.Selection.clearSelection()
self.TrechDialog.close()
def accept(self):
FreeCAD.ActiveDocument.openTransaction("Create Electrical Line")
makeElectricalLine()
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
self.closeForm()
return True
def reject(self):
self.closeForm()
return False
def closeForm(self):
FreeCADGui.Selection.removeObserver(self)
FreeCADGui.Control.closeDialog()
class CommandElectricalLine:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "electricalline.png")),
'Accel': "E, L",
'MenuText': "Línea eléctrica",
'ToolTip': "Crea una línea electríca en AC o DC.\n Selecciona la configuración de cable."}
def Activated(self):
TaskPanel = ElectricalLineTaskPanel()
FreeCADGui.Control.showDialog(TaskPanel)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
active = not (FreeCAD.ActiveDocument is None)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlanElectricalLine', CommandElectricalLine())

View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Dimensions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Heigth (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>2000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formTrechPathSelector</class>
<widget class="QDialog" name="formTrechPathSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>178</width>
<height>79</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Seleccionar ruta</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="labelTitle">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(180, 180, 180);
border-color: rgb(129, 129, 129);</string>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinBox">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="buttonAccept">
<property name="text">
<string>Aceptar</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,373 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
from pivy import coin
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import PVPlantResources
def makeStringbox():
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringBox")
_StringBox(obj)
_ViewProviderStringBox(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
#FreeCADGui.ActiveDocument.ActiveView.fitAll()
return obj
class _StringBox(ArchComponent.Component):
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setProperties(obj)
self.Type = "StringBox"
# Does a IfcType exist?
obj.IfcType = "Electric Distribution Board"
obj.setEditorMode("IfcType", 1)
# obj.MoveWithHost = False
def setProperties(self, obj):
# Definicion de Propiedades:
'''[
'App::PropertyBool',
'App::PropertyBoolList',
'App::PropertyFloat',
'App::PropertyFloatList',
'App::PropertyFloatConstraint',
'App::PropertyPrecision',
'App::PropertyQuantity',
'App::PropertyQuantityConstraint',
'App::PropertyAngle',
'App::PropertyDistance',
'App::PropertyLength',
'App::PropertyArea',
'App::PropertyVolume',
'App::PropertyFrequency',
'App::PropertySpeed',
'App::PropertyAcceleration',
'App::PropertyForce',
'App::PropertyPressure',
'App::PropertyVacuumPermittivity',
'App::PropertyInteger',
'App::PropertyIntegerConstraint',
'App::PropertyPercent',
'App::PropertyEnumeration',
'App::PropertyIntegerList',
'App::PropertyIntegerSet',
'App::PropertyMap',
'App::PropertyString',
'App::PropertyPersistentObject',
'App::PropertyUUID',
'App::PropertyFont',
'App::PropertyStringList',
'App::PropertyLink',
'App::PropertyLinkChild',
'App::PropertyLinkGlobal',
'App::PropertyLinkHidden',
'App::PropertyLinkSub',
'App::PropertyLinkSubChild',
'App::PropertyLinkSubGlobal',
'App::PropertyLinkSubHidden',
'App::PropertyLinkList',
'App::PropertyLinkListChild',
'App::PropertyLinkListGlobal',
'App::PropertyLinkListHidden',
'App::PropertyLinkSubList',
'App::PropertyLinkSubListChild',
'App::PropertyLinkSubListGlobal',
'App::PropertyLinkSubListHidden',
'App::PropertyXLink',
'App::PropertyXLinkSub',
'App::PropertyXLinkSubList',
'App::PropertyXLinkList',
'App::PropertyMatrix',
'App::PropertyVector',
'App::PropertyVectorDistance',
'App::PropertyPosition',
'App::PropertyDirection',
'App::PropertyVectorList',
'App::PropertyPlacement',
'App::PropertyPlacementList',
'App::PropertyPlacementLink',
'App::PropertyColor',
'App::PropertyColorList',
'App::PropertyMaterial',
'App::PropertyMaterialList',
'App::PropertyPath',
'App::PropertyFile',
'App::PropertyFileIncluded',
'App::PropertyPythonObject',
'App::PropertyExpressionEngine',
'Part::PropertyPartShape',
'Part::PropertyGeometryList',
'Part::PropertyShapeHistory',
'Part::PropertyFilletEdges',
'Mesh::PropertyNormalList',
'Mesh::PropertyCurvatureList',
'Mesh::PropertyMeshKernel',
'Sketcher::PropertyConstraintList'
]'''
pl = obj.PropertiesList
if not "InputsFromStrings" in pl:
obj.addProperty("App::PropertyQuantity",
"InputsFromStrings",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "Connection ")).InputsFromStrings = 12
if not ("PositiveInputs" in pl):
obj.addProperty("App::PropertyVectorList",
"PositiveInputs",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).PositiveInputs = []
obj.setEditorMode("PositiveInputs", 1)
if not ("NegativeInputs" in pl):
obj.addProperty("App::PropertyVectorList",
"NegativeInputs",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).NegativeInputs = []
obj.setEditorMode("NegativeInputs", 1)
if not "InputCables" in pl:
obj.addProperty("App::PropertyLinkList",
"InputCables",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "InputCables"))
# Outputs
'''
if not "Outputs" in pl:
obj.addProperty("App::PropertyQuantity",
"Outputs",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "Connection ")).Outputs = 1
'''
if not ("PositiveOut" in pl):
obj.addProperty("Part::PropertyPartShape",
"PositiveOut",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
obj.setEditorMode("PositiveOut", 1)
if not ("NegativeOut" in pl):
obj.addProperty("Part::PropertyPartShape",
"NegativeOut",
"Connections",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
obj.setEditorMode("NegativeOut", 1)
# Size:
if not "Width" in pl:
obj.addProperty("App::PropertyLength",
"Width",
"Box",
QT_TRANSLATE_NOOP("App::Property", "Connection ")).Width = 330
if not "Length" in pl:
obj.addProperty("App::PropertyLength",
"Length",
"Box",
QT_TRANSLATE_NOOP("App::Property", "Connection ")).Length = 848
if not "Height" in pl:
obj.addProperty("App::PropertyLength",
"Height",
"Box",
QT_TRANSLATE_NOOP("App::Property", "Connection ")).Height = 615
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and Arch wall properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
obj.Proxy = self
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
if prop == "InputsFromStrings":
for i in range(int(obj.getPropertyByName(prop).Value)):
obj.removeProperty("PositiveInput" + str(i+1))
obj.removeProperty("NegativeInput" + str(i + 1))
def execute(self, obj):
# obj.Shape: compound
# |- body: compound
# |-- body: solid
# |-- door: solid
# |-- inputs: solids
# |-- outputs: solids
# |- inputs references: compound
# |-- positives: compound
# |--- positive: point of vertex
solids = []
pts = []
def getdownFace(object):
downface = object.Faces[0]
for face in object.Faces:
if face.CenterOfMass.z < downface.CenterOfMass.z:
downface = face
return downface
def drawInputs(numrows, offsetx, type, cpd):
numInputs = int(obj.InputsFromStrings.Value)
nperrow = int(round(numInputs / numrows, 0))
gap = 45
diameter = 20
points = []
cnt = 0
for r in range(numrows):
xx = -obj.Length.Value / 2 + offsetx + gap / 2 * (r % 2)
yy = -diameter + gap * r
for i in range(min(numInputs, nperrow)):
cyl = Part.makeCylinder(10, 20, FreeCAD.Vector(xx + gap * i, yy, -20))
solids.append(cyl)
points.append(getdownFace(cyl).CenterOfMass)
cnt += 1
inname = ("PositiveIn" if type == 0 else "NegativeIn") + str(cnt)
obj.addProperty("Part::PropertyPartShape",
inname,
"Inputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
obj.setEditorMode(inname, 1)
setattr(obj, inname, getdownFace(cyl))
cpd.add(cyl)
numInputs -= nperrow
return points
box = Part.makeBox(obj.Length.Value, obj.Width.Value, obj.Height.Value)
box.Placement.Base.x -= obj.Length.Value / 2
box.Placement.Base.y -= obj.Width.Value / 2
# Output:
cpd_out = Part.makeCompound([])
outp = Part.makeCylinder(65/2, 20, FreeCAD.Vector(0, 0, -20))
#out.Placement.Base.x += 50
#out.Placement.Base.y += 65/2
solids.append(outp)
cpd_out.add(outp)
obj.PositiveOut = getdownFace(outp)
outn = outp.copy()
outn.Placement.Base.x += 65 + 10
solids.append(outn)
cpd_out.add(outn)
obj.NegativeOut = getdownFace(outn)
# Inputs:
cpd_Pos_Inputs = Part.makeCompound([])
cpd_Neg_Inputs = Part.makeCompound([])
obj.PositiveInputs = drawInputs(2, 80, 0, cpd_Pos_Inputs).copy()
obj.NegativeInputs = drawInputs(4, 650, 1, cpd_Neg_Inputs).copy()
pts.append(getdownFace(box).CenterOfMass)
pts.append(getdownFace(outn).CenterOfMass)
pts.append(getdownFace(outp).CenterOfMass)
obj.Shape = Part.makeCompound([box, cpd_out, cpd_Pos_Inputs, cpd_Neg_Inputs])
class _ViewProviderStringBox(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "StringBox.svg"))
def attach(self, vobj):
self.Object = vobj.Object
sep = coin.SoSeparator()
self.coords = coin.SoCoordinate3()
sep.addChild(self.coords)
self.coords.point.deleteValues(0)
symbol = coin.SoMarkerSet()
symbol.markerIndex = FreeCADGui.getMarkerIndex("", 5)
sep.addChild(symbol)
rn = vobj.RootNode
rn.addChild(sep)
ArchComponent.ViewProviderComponent.attach(self, vobj)
def updateData(self, obj, prop):
if prop == "PositiveInputs":
if obj.PositiveInputs:
self.coords.point.setNum(len(obj.PositiveInputs))
self.coords.point.setValues([[p.x, p.y, p.z] for p in obj.PositiveInputs])
else:
self.coords.point.deleteValues(0)
class _CommandBoxEnclosure:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "StringBox.svg")),
'Accel': "C, E",
'MenuText': QT_TRANSLATE_NOOP("Placement", "Movimiento de tierras"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el movimiento de tierras")}
def Activated(self):
makeStringbox()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantStringBox', _CommandBoxEnclosure())

550
Electrical/Conduit.py Normal file
View File

@@ -0,0 +1,550 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* Copyright (c) 2016 Yorik van Havre <yorik@uncreated.net> *
#* *
#* 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 ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from draftutils.translate import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt,txt):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
## @package ArchPipe
# \ingroup ARCH
# \brief The Pipe object and tools
#
# This module provides tools to build Pipe and Pipe connector objects.
# Pipes are tubular objects extruded along a base line.
__title__ = "Arch Pipe tools"
__author__ = "Yorik van Havre"
__url__ = "https://www.freecadweb.org"
def makePipe(baseobj=None, diameter=0, length=0, placement=None, name="Pipe"):
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj= FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
Pipe(obj)
if FreeCAD.GuiUp:
ViewProviderPipe(obj.ViewObject)
if baseobj:
baseobj.ViewObject.hide()
if baseobj:
obj.Base = baseobj
else:
if length:
obj.Length = length
if diameter:
obj.Diameter = diameter
if placement:
obj.Placement = placement
return obj
def makePipeConnector(pipes,radius=0,name="Connector"):
"makePipeConnector(pipes,[radius,name]): creates a connector between the given pipes"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj= FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
obj.Label = name
_ArchPipeConnector(obj)
obj.Pipes = pipes
if not radius:
radius = pipes[0].Diameter
obj.Radius = radius
if FreeCAD.GuiUp:
_ViewProviderPipe(obj.ViewObject)
return obj
class Pipe(ArchComponent.Component):
"the Arch Pipe object"
def __init__(self,obj):
ArchComponent.Component.__init__(self,obj)
self.setProperties(obj)
def setProperties(self,obj):
pl = obj.PropertiesList
if not "Diameter" in pl:
obj.addProperty("App::PropertyLength",
"Diameter",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","The diameter of this pipe, if not based on a profile")).Diameter = 60
if not "Length" in pl:
obj.addProperty("App::PropertyLength",
"Length",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","The length of this pipe, if not based on an edge"))
if not "Profile" in pl:
obj.addProperty("App::PropertyLink",
"Profile",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","An optional closed profile to base this pipe on"))
if not "Start" in pl:
obj.addProperty("App::PropertyLength",
"Start",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","Offset from the start point"))
if not "Offset" in pl:
obj.addProperty("App::PropertyVector",
"Offset",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","Offset from the start point xy"))
if not "WallThickness" in pl:
obj.addProperty("App::PropertyLength",
"WallThickness",
"Conduit",
QT_TRANSLATE_NOOP("App::Property","The wall thickness of this pipe, if not based on a profile")).WallThickness = 2
self.Type = "Conduit"
# IfcPipeSegment is new in IFC4
from ArchIFC import IfcTypes
if "Cable Carrier Segment" in IfcTypes:
obj.IfcType = "Cable Carrier Segment"
obj.PredefinedType = "CONDUITSEGMENT"
obj.setEditorMode("IfcType", 1)
obj.setEditorMode("PredefinedType", 1)
else:
# IFC2x3 does not know a Pipe Segment
obj.IfcType = "Undefined"
def onDocumentRestored(self,obj):
ArchComponent.Component.onDocumentRestored(self,obj)
self.setProperties(obj)
def execute(self,obj):
import Part, DraftGeomUtils, math
pl = obj.Placement
w = self.getWire(obj)
if not w:
FreeCAD.Console.PrintError(translate("Arch","Unable to build the base path")+"\n")
return
if obj.Start.Value:
# new:
d = obj.Start.Value
for i, e in enumerate(w.Edges):
if e.Length < d:
d -= e.Length
else:
v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize()
v.multiply(d)
p = e.Vertexes[0].Point.add(v)
pts = [ver.Point for ver in w.Vertexes]
pts = [p] + pts[i + 1:]
w = Part.makePolygon(pts)
break
if obj.Length.Value == 0:
obj.Length.Value = w.Length
elif obj.Length.Value > 0:
d = obj.Length.Value
for i, e in enumerate(w.Edges):
if e.Length < d:
d -= e.Length
else:
v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize()
v.multiply(d)
p = e.Vertexes[0].Point.add(v)
pts = [ver.Point for ver in w.Vertexes]
pts = pts[:i + 1] + [p]
w = Part.makePolygon(pts)
break
pla = FreeCAD.Vector(w.Vertexes[int(len(w.Vertexes)/2)].Point)
w.Placement.Base -= pla
p = self.getProfile(obj)
if not p:
FreeCAD.Console.PrintError(translate("Arch", "Unable to build the profile") + "\n")
return
# move and rotate the profile to the first point
if hasattr(p,"CenterOfMass"):
c = p.CenterOfMass
else:
c = p.BoundBox.Center
delta = w.Vertexes[0].Point - c + FreeCAD.Vector(obj.Offset.y, obj.Offset.x, 0)
p.translate(delta)
import Draft
if Draft.getType(obj.Base) == "BezCurve":
v1 = obj.Base.Placement.multVec(obj.Base.Points[1])-w.Vertexes[0].Point
else:
v1 = w.Vertexes[1].Point-w.Vertexes[0].Point
v2 = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(v2,v1)
p.rotate(w.Vertexes[0].Point,rot.Axis,math.degrees(rot.Angle))
shapes = []
try:
if p.Faces:
for f in p.Faces:
sh = w.makePipeShell([f.OuterWire],True,False,2)
for shw in f.Wires:
if shw.hashCode() != f.OuterWire.hashCode():
sh2 = w.makePipeShell([shw],True,False,2)
sh = sh.cut(sh2)
sh.Placement.Base += pla
shapes.append(sh)
elif p.Wires:
for pw in p.Wires:
sh = w.makePipeShell([pw],True,False,2)
sh.Placement.Base += pla
shapes.append(sh)
except Exception:
FreeCAD.Console.PrintError(translate("Arch","Unable to build the pipe")+"\n")
else:
if len(shapes) == 0:
return
elif len(shapes) == 1:
sh = shapes[0]
else:
sh = Part.makeCompound(shapes)
obj.Shape = sh
if not obj.Base:
obj.Placement = pl
def getWire(self,obj):
import Part
if obj.Base:
if not hasattr(obj.Base,'Shape'):
FreeCAD.Console.PrintError(translate("Arch","The base object is not a Part")+"\n")
return
if len(obj.Base.Shape.Wires) != 1:
FreeCAD.Console.PrintError(translate("Arch","Too many wires in the base shape")+"\n")
return
if obj.Base.Shape.Wires[0].isClosed():
FreeCAD.Console.PrintError(translate("Arch","The base wire is closed")+"\n")
return
w = obj.Base.Shape.Wires[0]
else:
if obj.Length.Value == 0:
return
w = Part.Wire([Part.LineSegment(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,obj.Length.Value)).toShape()])
return w
def getProfile(self,obj):
import Part
if obj.Profile:
if not obj.Profile.getLinkedObject().isDerivedFrom("Part::Part2DObject"):
FreeCAD.Console.PrintError(translate("Arch","The profile is not a 2D Part")+"\n")
return
if not obj.Profile.Shape.Wires[0].isClosed():
FreeCAD.Console.PrintError(translate("Arch","The profile is not closed")+"\n")
return
p = obj.Profile.Shape.Wires[0]
else:
if obj.Diameter.Value == 0:
return
p = Part.Wire([Part.Circle(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),obj.Diameter.Value/2).toShape()])
if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Diameter.Value/2):
p2 = Part.Wire([Part.Circle(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),(obj.Diameter.Value/2-obj.WallThickness.Value)).toShape()])
p = Part.Face(p)
p2 = Part.Face(p2)
p = p.cut(p2)
return p
class ViewProviderPipe(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self,vobj):
ArchComponent.ViewProviderComponent.__init__(self,vobj)
vobj.ShapeColor = (255, 50, 50)
def getIcon(self):
return ":/icons/Arch_Pipe_Tree.svg"
class CommandConduit:
"the Arch Pipe command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_Pipe',
'MenuText': QT_TRANSLATE_NOOP("Arch_Pipe","Pipe"),
'Accel': "P, I",
'ToolTip': QT_TRANSLATE_NOOP("Arch_Pipe","Creates a pipe object from a given Wire or Line")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
s = FreeCADGui.Selection.getSelection()
if s:
for obj in s:
if hasattr(obj,'Shape'):
if len(obj.Shape.Wires) == 1:
FreeCAD.ActiveDocument.openTransaction("Create Conduit")
makePipe(obj, name="Conduit")
FreeCAD.ActiveDocument.commitTransaction()
else:
FreeCAD.ActiveDocument.openTransaction("Create Conduit")
makePipe(name="Conduit")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
class _CommandPipeConnector:
"the Arch Pipe command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_PipeConnector',
'MenuText': QT_TRANSLATE_NOOP("Arch_PipeConnector","Connector"),
'Accel': "P, C",
'ToolTip': QT_TRANSLATE_NOOP("Arch_PipeConnector","Creates a connector between 2 or 3 selected pipes")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
import Draft
s = FreeCADGui.Selection.getSelection()
if not (len(s) in [2,3]):
FreeCAD.Console.PrintError(translate("Arch","Please select exactly 2 or 3 Pipe objects")+"\n")
return
o = "["
for obj in s:
if Draft.getType(obj) != "Pipe":
FreeCAD.Console.PrintError(translate("Arch","Please select only Pipe objects")+"\n")
return
o += "FreeCAD.ActiveDocument."+obj.Name+","
o += "]"
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Connector"))
FreeCADGui.addModule("Arch")
FreeCADGui.doCommand("obj = Arch.makePipeConnector("+o+")")
FreeCADGui.addModule("Draft")
FreeCADGui.doCommand("Draft.autogroup(obj)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
class _ArchPipeConnector(ArchComponent.Component):
"the Arch Pipe Connector object"
def __init__(self,obj):
ArchComponent.Component.__init__(self,obj)
self.setProperties(obj)
obj.IfcType = "Pipe Fitting"
def setProperties(self,obj):
pl = obj.PropertiesList
if not "Radius" in pl:
obj.addProperty("App::PropertyLength", "Radius", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The curvature radius of this connector"))
if not "Pipes" in pl:
obj.addProperty("App::PropertyLinkList", "Pipes", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The pipes linked by this connector"))
if not "ConnectorType" in pl:
obj.addProperty("App::PropertyEnumeration", "ConnectorType", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The type of this connector"))
obj.ConnectorType = ["Corner","Tee"]
obj.setEditorMode("ConnectorType",1)
self.Type = "PipeConnector"
def onDocumentRestored(self,obj):
ArchComponent.Component.onDocumentRestored(self,obj)
self.setProperties(obj)
def execute(self,obj):
tol = 1 # tolerance for alignment. This is only visual, we can keep it low...
ptol = 0.001 # tolerance for coincident points
import math,Part,DraftGeomUtils,ArchCommands
if len(obj.Pipes) < 2:
return
if len(obj.Pipes) > 3:
FreeCAD.Console.PrintWarning(translate("Arch","Only the 3 first wires will be connected")+"\n")
if obj.Radius.Value == 0:
return
wires = []
order = []
for o in obj.Pipes:
wires.append(o.Proxy.getWire(o))
if wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol:
order = ["start","start"]
point = wires[0].Vertexes[0].Point
elif wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol:
order = ["start","end"]
point = wires[0].Vertexes[0].Point
elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol:
order = ["end","end"]
point = wires[0].Vertexes[-1].Point
elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol:
order = ["end","start"]
point = wires[0].Vertexes[-1].Point
else:
FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n")
return
if order[0] == "start":
v1 = wires[0].Vertexes[1].Point.sub(wires[0].Vertexes[0].Point).normalize()
else:
v1 = wires[0].Vertexes[-2].Point.sub(wires[0].Vertexes[-1].Point).normalize()
if order[1] == "start":
v2 = wires[1].Vertexes[1].Point.sub(wires[1].Vertexes[0].Point).normalize()
else:
v2 = wires[1].Vertexes[-2].Point.sub(wires[1].Vertexes[-1].Point).normalize()
p = obj.Pipes[0].Proxy.getProfile(obj.Pipes[0])
# If the pipe has a non-zero WallThickness p is a shape instead of a wire:
if p.ShapeType != "Wire":
p = p.Wires
p = Part.Face(p)
if len(obj.Pipes) == 2:
if obj.ConnectorType != "Corner":
obj.ConnectorType = "Corner"
if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]:
FreeCAD.Console.PrintError(translate("Arch","Pipes are already aligned")+"\n")
return
normal = v2.cross(v1)
offset = math.tan(math.pi/2-v1.getAngle(v2)/2)*obj.Radius.Value
v1.multiply(offset)
v2.multiply(offset)
self.setOffset(obj.Pipes[0],order[0],offset)
self.setOffset(obj.Pipes[1],order[1],offset)
# find center
perp = v1.cross(normal).normalize()
perp.multiply(obj.Radius.Value)
center = point.add(v1).add(perp)
# move and rotate the profile to the first point
delta = point.add(v1)-p.CenterOfMass
p.translate(delta)
vp = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(vp,v1)
p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle))
sh = p.revolve(center,normal,math.degrees(math.pi-v1.getAngle(v2)))
#sh = Part.makeCompound([sh]+[Part.Vertex(point),Part.Vertex(point.add(v1)),Part.Vertex(center),Part.Vertex(point.add(v2))])
else:
if obj.ConnectorType != "Tee":
obj.ConnectorType = "Tee"
if wires[2].Vertexes[0].Point == point:
order.append("start")
elif wires[0].Vertexes[-1].Point == point:
order.append("end")
else:
FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n")
if order[2] == "start":
v3 = wires[2].Vertexes[1].Point.sub(wires[2].Vertexes[0].Point).normalize()
else:
v3 = wires[2].Vertexes[-2].Point.sub(wires[2].Vertexes[-1].Point).normalize()
if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]:
pair = [v1,v2,v3]
elif round(v1.getAngle(v3),tol) in [0,round(math.pi,tol)]:
pair = [v1,v3,v2]
elif round(v2.getAngle(v3),tol) in [0,round(math.pi,tol)]:
pair = [v2,v3,v1]
else:
FreeCAD.Console.PrintError(translate("Arch","At least 2 pipes must align")+"\n")
return
offset = obj.Radius.Value
v1.multiply(offset)
v2.multiply(offset)
v3.multiply(offset)
self.setOffset(obj.Pipes[0],order[0],offset)
self.setOffset(obj.Pipes[1],order[1],offset)
self.setOffset(obj.Pipes[2],order[2],offset)
normal = pair[0].cross(pair[2])
# move and rotate the profile to the first point
delta = point.add(pair[0])-p.CenterOfMass
p.translate(delta)
vp = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(vp,pair[0])
p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle))
t1 = p.extrude(pair[1].multiply(2))
# move and rotate the profile to the second point
delta = point.add(pair[2])-p.CenterOfMass
p.translate(delta)
vp = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(vp,pair[2])
p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle))
t2 = p.extrude(pair[2].negative().multiply(2))
# create a cut plane
cp = Part.makePolygon([point,point.add(pair[0]),point.add(normal),point])
cp = Part.Face(cp)
if cp.normalAt(0,0).getAngle(pair[2]) < math.pi/2:
cp.reverse()
cf, cv, invcv = ArchCommands.getCutVolume(cp,t2)
t2 = t2.cut(cv)
sh = t1.fuse(t2)
obj.Shape = sh
def setOffset(self,pipe,pos,offset):
if pos == "start":
if pipe.OffsetStart != offset:
pipe.OffsetStart = offset
pipe.Proxy.execute(pipe)
else:
if pipe.OffsetEnd != offset:
pipe.OffsetEnd = offset
pipe.Proxy.execute(pipe)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Conduit', CommandConduit())
FreeCADGui.addCommand('Arch_PipeConnector',_CommandPipeConnector())
class _ArchPipeGroupCommand:
def GetCommands(self):
return tuple(['Arch_Pipe','Arch_PipeConnector'])
def GetResources(self):
return { 'MenuText': QT_TRANSLATE_NOOP("Arch_PipeTools",'Pipe tools'),
'ToolTip': QT_TRANSLATE_NOOP("Arch_PipeTools",'Pipe tools')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('Arch_PipeTools', _ArchPipeGroupCommand())

View File

@@ -0,0 +1,398 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
import ArchComponent
import os
import zipfile
import re
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
else:
# \cond
def translate(ctxt,txt):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
import os
from PVPlantResources import DirIcons as DirIcons
__title__ = "PVPlant Areas"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")
def makeStringInverter():
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringInverter")
InverterBase(obj)
ViewProviderStringInverter(obj.ViewObject)
try:
folder = FreeCAD.ActiveDocument.StringInverters
except:
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'StringInverters')
folder.Label = "StringInverters"
folder.addObject(obj)
return obj
class InverterBase(ArchComponent.Component):
def __init__(self, obj):
''' Initialize the Area object '''
ArchComponent.Component.__init__(self, obj)
self.oldMPPTs = 0
self.Type = None
self.obj = None
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
if not "File" in pl:
obj.addProperty("App::PropertyFile",
"File",
"Inverter",
"The base file this component is built upon")
if not ("MPPTs" in pl):
obj.addProperty("App::PropertyQuantity",
"MPPTs",
"Inverter",
"Points that define the area"
).MPPTs = 0
if not ("Generator" in pl):
obj.addProperty("App::PropertyEnumeration",
"Generator",
"Inverter",
"Points that define the area"
).Generator = ["Generic", "Library"]
obj.Generator = "Generic"
if not ("Type" in pl):
obj.addProperty("App::PropertyString",
"Type",
"Base",
"Points that define the area"
).Type = "InverterBase"
obj.setEditorMode("Type", 1)
self.Type = obj.Type
obj.Proxy = self
def onDocumentRestored(self, obj):
""" Method run when the document is restored """
self.setProperties(obj)
def onBeforeChange(self, obj, prop):
if prop == "MPPTs":
self.oldMPPTs = int(obj.MPPTs)
def onChanged(self, obj, prop):
''' '''
if prop == "Generator":
if obj.Generator == "Generic":
obj.setEditorMode("MPPTs", 0)
else:
obj.setEditorMode("MPPTs", 1)
if prop == "MPPTs":
''' '''
if self.oldMPPTs > obj.MPPTs:
''' borrar sobrantes '''
obj.removeProperty()
elif self.oldMPPTs < obj.MPPTs:
''' crear los faltantes '''
for i in range(self.oldMPPTs, int(obj.MPPTs)):
''' '''
print(i)
else:
pass
if (prop == "File") and obj.File:
''' '''
def execute(self, obj):
''' '''
# obj.Shape: compound
# |- body: compound
# |-- inverter: solid
# |-- door: solid
# |-- holder: solid
# |- connectors: compound
# |-- DC: compound
# |--- MPPT 1..x: compound
# |---- positive: compound
# |----- connector 1..y: ??
# |---- negative 1..y: compound
# |----- connector 1..y: ??
# |-- AC: compound
# |--- R,S,T,: ??
# |-- Communication
pl = obj.Placement
filename = self.getFile(obj)
if filename:
parts = self.getPartsList(obj)
if parts:
zdoc = zipfile.ZipFile(filename)
if zdoc:
f = zdoc.open(parts[list(parts.keys())[-1]][1])
shapedata = f.read()
f.close()
shapedata = shapedata.decode("utf8")
shape = self.cleanShape(shapedata, obj, parts[list(parts.keys())[-1]][2])
obj.Shape = shape
if not pl.isIdentity():
obj.Placement = pl
obj.MPPTs = len(shape.SubShapes[1].SubShapes[0].SubShapes)
def cleanShape(self, shapedata, obj, materials):
"cleans the imported shape"
import Part
shape = Part.Shape()
shape.importBrepFromString(shapedata)
'''if obj.FuseArch and materials:
# separate lone edges
shapes = []
for edge in shape.Edges:
found = False
for solid in shape.Solids:
for soledge in solid.Edges:
if edge.hashCode() == soledge.hashCode():
found = True
break
if found:
break
if found:
break
else:
shapes.append(edge)
print("solids:",len(shape.Solids),"mattable:",materials)
for key,solindexes in materials.items():
if key == "Undefined":
# do not join objects with no defined material
for solindex in [int(i) for i in solindexes.split(",")]:
shapes.append(shape.Solids[solindex])
else:
fusion = None
for solindex in [int(i) for i in solindexes.split(",")]:
if not fusion:
fusion = shape.Solids[solindex]
else:
fusion = fusion.fuse(shape.Solids[solindex])
if fusion:
shapes.append(fusion)
shape = Part.makeCompound(shapes)
try:
shape = shape.removeSplitter()
except Exception:
print(obj.Label,": error removing splitter")'''
return shape
def getFile(self, obj, filename=None):
"gets a valid file, if possible"
if not filename:
filename = obj.File
if not filename:
return None
if not filename.lower().endswith(".fcstd"):
return None
if not os.path.exists(filename):
# search for the file in the current directory if not found
basename = os.path.basename(filename)
currentdir = os.path.dirname(obj.Document.FileName)
altfile = os.path.join(currentdir,basename)
if altfile == obj.Document.FileName:
return None
elif os.path.exists(altfile):
return altfile
else:
# search for subpaths in current folder
altfile = None
subdirs = self.splitall(os.path.dirname(filename))
for i in range(len(subdirs)):
subpath = [currentdir]+subdirs[-i:]+[basename]
altfile = os.path.join(*subpath)
if os.path.exists(altfile):
return altfile
return None
return filename
def getPartsList(self, obj, filename=None):
"returns a list of Part-based objects in a FCStd file"
parts = {}
materials = {}
filename = self.getFile(obj,filename)
if not filename:
return parts
zdoc = zipfile.ZipFile(filename)
with zdoc.open("Document.xml") as docf:
name = None
label = None
part = None
materials = {}
writemode = False
for line in docf:
line = line.decode("utf8")
if "<Object name=" in line:
n = re.findall('name=\"(.*?)\"',line)
if n:
name = n[0]
elif "<Property name=\"Label\"" in line:
writemode = True
elif writemode and "<String value=" in line:
n = re.findall('value=\"(.*?)\"',line)
if n:
label = n[0]
writemode = False
elif "<Property name=\"Shape\" type=\"Part::PropertyPartShape\"" in line:
writemode = True
elif writemode and "<Part file=" in line:
n = re.findall('file=\"(.*?)\"',line)
if n:
part = n[0]
writemode = False
elif "<Property name=\"MaterialsTable\" type=\"App::PropertyMap\"" in line:
writemode = True
elif writemode and "<Item key=" in line:
n = re.findall('key=\"(.*?)\"',line)
v = re.findall('value=\"(.*?)\"',line)
if n and v:
materials[n[0]] = v[0]
elif writemode and "</Map>" in line:
writemode = False
elif "</Object>" in line:
if name and label and part:
parts[name] = [label,part,materials]
name = None
label = None
part = None
materials = {}
writemode = False
return parts
def getColors(self,obj):
"returns the DiffuseColor of the referenced object"
filename = self.getFile(obj)
if not filename:
return None
part = obj.Part
if not obj.Part:
return None
zdoc = zipfile.ZipFile(filename)
if not "GuiDocument.xml" in zdoc.namelist():
return None
colorfile = None
with zdoc.open("GuiDocument.xml") as docf:
writemode1 = False
writemode2 = False
for line in docf:
line = line.decode("utf8")
if ("<ViewProvider name=" in line) and (part in line):
writemode1 = True
elif writemode1 and ("<Property name=\"DiffuseColor\"" in line):
writemode1 = False
writemode2 = True
elif writemode2 and ("<ColorList file=" in line):
n = re.findall('file=\"(.*?)\"',line)
if n:
colorfile = n[0]
break
if not colorfile:
return None
if not colorfile in zdoc.namelist():
return None
colors = []
cf = zdoc.open(colorfile)
buf = cf.read()
cf.close()
for i in range(1,int(len(buf)/4)):
colors.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0))
if colors:
return colors
return None
def splitall(self,path):
"splits a path between its components"
allparts = []
while 1:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts
class ViewProviderStringInverter(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg"))
class CommandStringInverter:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg")),
'Accel': "E, I",
'MenuText': "String Inverter",
'ToolTip': "String Placement",}
def Activated(self):
sinverter = makeStringInverter()
def IsActive(self):
active = not (FreeCAD.ActiveDocument is None)
return active
if FreeCAD.GuiUp:
FreeCADGui.addCommand('StringInverter', CommandStringInverter())

0
Electrical/Wiring.py Normal file
View File

80
Export/PVPlantBOQCivil.py Normal file
View File

@@ -0,0 +1,80 @@
# /**********************************************************************
# * *
# * 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 FreeCAD, Draft
import PVPlantSite
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
import Part
import pivy
from pivy import coin
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Trench"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
from PVPlantResources import DirIcons as DirIcons
from PVPlantResources import DirDocuments as DirDocuments
import openpyxl
def makeBOQCivil():
''' create a excel '''
class _CommandBOQCivil:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "boqc.svg")),
'Accel': "R, C",
'MenuText': "BOQ Civil",
'ToolTip': ""}
def Activated(self):
makeBOQCivil()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('BOQCivil', _CommandBOQCivil())

View File

@@ -0,0 +1,104 @@
# /**********************************************************************
# * *
# * 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 FreeCAD, Draft
import copy
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
import Part
import pivy
from pivy import coin
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Trench"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
from PVPlantResources import DirIcons as DirIcons
def makeBOQElectrical():
''' create a excel '''
print("makeBOQElectrical")
import sys
sys.path.append(r"/")
funct = 0
if funct == 0:
print(" ------- Prueba generar layout en excel: ")
# export layout to Excel:
from Export import layoutToExcel
layoutToExcel.generateLayout()
return
else:
print(" ------- Prueba dibujar contorno de los objetos seleccionados: ")
import numpy as np
import MeshTools.MeshGetBoundary as mgb
# contorno:
maxdist = 6000
if FreeCADGui.Selection.hasSelection():
sel = FreeCADGui.Selection.getSelection()
pts = []
for obj in sel:
for panel in obj.Shape.SubShapes[0].SubShapes[0].SubShapes:
zm = panel.BoundBox.ZMax
for i in range(8):
pt = panel.BoundBox.getPoint(i)
if pt.z == zm:
pts.append(pt)
import PVPlantCreateTerrainMesh
m = PVPlantCreateTerrainMesh.Triangulate(np.array(pts), MaxlengthLE=maxdist, use3d=False)
b = mgb.get_boundary(m)
Part.show(b)
class _CommandBOQElectrical:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "boqe.svg")),
'Accel': "R, E",
'MenuText': "BOQ Electrical",
'ToolTip': ""}
def Activated(self):
makeBOQElectrical()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('BOQElectrical', _CommandBOQElectrical())

View File

@@ -0,0 +1,344 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import openpyxl
from openpyxl.styles import Alignment, Border, Side, PatternFill, Font
import PVPlantResources
import PVPlantSite
# Estilos:
thin = Side(border_style="thin", color="7DA4B8")
double = Side(border_style="double", color="ff0000")
border_thin = Border(top=thin, left=thin, right=thin, bottom=thin)
border_fat = Border(top=thin, left=thin, right=thin, bottom=thin)
# fill = PatternFill("solid", fgColor="DDDDDD")
# fill = GradientFill(stop=("000000", "FFFFFF"))
scale = 0.001 # milimeters to meter
def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None):
"""
Apply styles to a range of cells as if they were a single cell.
:param ws: Excel worksheet instance
:param range: An excel range to style (e.g. A1:F20)
:param border: An openpyxl Border
:param fill: An openpyxl PatternFill or GradientFill
:param font: An openpyxl Font object
"""
top = Border(top=border.top)
left = Border(left=border.left)
right = Border(right=border.right)
bottom = Border(bottom=border.bottom)
first_cell = ws[cell_range.split(":")[0]]
if alignment:
first_cell.alignment = alignment
rows = ws[cell_range]
if font:
first_cell.font = font
for cell in rows[0]:
cell.border = cell.border + top
for cell in rows[-1]:
cell.border = cell.border + bottom
for row in rows:
l = row[0]
r = row[-1]
l.border = l.border + left
r.border = r.border + right
if fill:
for c in row:
c.fill = fill
def spreadsheetBOQFrames(sheet, sel):
sheet['A1'] = 'Index'
sheet['B1'] = 'Frame'
sheet['C1'] = 'Frame Type'
sheet['D1'] = 'X'
sheet['E1'] = 'Y'
sheet['F1'] = 'Z'
sheet['G1'] = 'Angle N-S'
sheet['H1'] = 'Angle L-W'
sheet['I1'] = 'Nº Poles'
sheet.column_dimensions['A'].width = 8
sheet.column_dimensions['B'].width = 30
sheet.column_dimensions['C'].width = 20
sheet.column_dimensions['D'].width = 20
sheet.column_dimensions['E'].width = 20
sheet.column_dimensions['F'].width = 20
sheet.column_dimensions['G'].width = 15
sheet.column_dimensions['H'].width = 15
sheet.column_dimensions['I'].width = 15
sheet.row_dimensions[1].height = 40
style_range(sheet, 'A1:I1',
border=Border(top=thin, left=thin, right=thin, bottom=thin),
fill=PatternFill("solid", fgColor="7DA4B8"),
font=Font(name='Quicksand', size=10, b=True, color="FFFFFF"),
alignment=Alignment(horizontal="center", vertical="center"))
for ind in range(0, len(sel)):
row = ind + 2
sheet['A{0}'.format(row)] = ind + 1
sheet['B{0}'.format(row)] = sel[ind].Label
sheet['C{0}'.format(row)] = sel[ind].Setup.Label
sheet['D{0}'.format(row)] = sel[ind].Placement.Base.x * scale
sheet['E{0}'.format(row)] = sel[ind].Placement.Base.y * scale
sheet['R{0}'.format(row)] = sel[ind].Placement.Base.z * scale
sheet['G{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[0]
sheet['H{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[1]
sheet['I{0}'.format(row)] = sel[ind].Setup.NumberPole.Value
style_range(sheet, 'A' + str(row) + ':I' + str(row),
border=Border(top=thin, left=thin, right=thin, bottom=thin),
font=Font(name='Quicksand', size=10),
alignment=Alignment(horizontal="center", vertical="center"))
def spreadsheetBOQPoles(sheet, sel):
import MeshPart as mp
from Mechanical.Frame import PVPlantFrame
# Data:
terrain = PVPlantSite.get().Terrain.Mesh # Shape
# Headers:
sheet['A1'] = 'Frame'
sheet['B1'] = 'Pole'
sheet['C1'] = 'Pole Type'
sheet['D1'] = 'X'
sheet['E1'] = 'Y'
sheet['F1'] = 'Z frame attach'
sheet['G1'] = 'Z aerial head'
sheet['H1'] = 'Pole length'
sheet['I1'] = 'Pole aerial length'
sheet['J1'] = 'Pole terrain enter length'
sheet.column_dimensions['A'].width = 30
sheet.column_dimensions['B'].width = 8
sheet.column_dimensions['C'].width = 20
sheet.column_dimensions['D'].width = 20
sheet.column_dimensions['E'].width = 20
sheet.column_dimensions['F'].width = 20
sheet.column_dimensions['G'].width = 20
sheet.column_dimensions['H'].width = 20
sheet.column_dimensions['I'].width = 20
sheet.column_dimensions['J'].width = 20
sheet.row_dimensions[1].height = 40
style_range(sheet, 'A1:J1',
border=Border(top=thin, left=thin, right=thin, bottom=thin),
fill=PatternFill("solid", fgColor="7DA4B8"),
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
alignment=Alignment(horizontal="center", vertical="center"))
sheet['A2'] = ""
sheet.row_dimensions[2].height = 5
data = {"Frame": [],
#"FrameType": [],
"Pole": [],
"PoleType": [],
"PoleLength": [],
"Center": [],
"Head": []}
cnt = 0
for frame_ind, frame in enumerate(sel):
poles = frame.Shape.SubShapes[1].SubShapes[0].SubShapes
numpoles = int(frame.Setup.NumberPole.Value)
seq = frame.Setup.PoleSequence
if len(seq) < numpoles:
seq = PVPlantFrame.getarray(frame.Setup.PoleSequence, numpoles)
for pole_ind in range(numpoles):
pole = poles[pole_ind]
poletype = frame.Setup.PoleType[seq[pole_ind]]
data["Frame"].append(frame.Label)
#data["FrameType"].append(frame.Setup.Label)
data["Pole"].append(pole_ind + 1)
data["PoleType"].append(poletype.Label)
data["PoleLength"].append(int(poletype.Height))
data["Center"].append(pole.BoundBox.Center)
data["Head"].append(pole.BoundBox.ZMax)
cnt += 1
pts = mp.projectPointsOnMesh(data["Center"], terrain, FreeCAD.Vector(0, 0, 1))
#if cnt == len(pts):
data["Soil"] = pts
row = 3
group_from = row
f = data["Frame"][0]
for i in range(0, len(data["Frame"])):
if f != data["Frame"][i]:
style_range(sheet, 'A' + str(group_from) + ':F' + str(row - 1),
border=Border(top=thin, left=thin, right=thin, bottom=thin),
font=Font(name='Quicksand', size=11, ),
alignment=Alignment(horizontal="center", vertical="center"))
sheet.merge_cells('A' + str(group_from) + ':A' + str(row - 1))
style_range(sheet, 'A' + str(group_from) + ':A' + str(row - 1),
border=Border(top=thin, left=thin, right=thin, bottom=thin),
font=Font(name='Quicksand', size=11, ),
alignment=Alignment(horizontal="center", vertical="center"))
#sheet['A{0}'.format(row)] = ""
sheet.row_dimensions[row].height = 5
row += 1
f = data["Frame"][i]
group_from = row
sheet['A{0}'.format(row)] = data['Frame'][i]
sheet['B{0}'.format(row)] = data['Pole'][i]
sheet['C{0}'.format(row)] = data['PoleType'][i]
sheet['D{0}'.format(row)] = round(data['Center'][i].x, 0) * scale
sheet['D{0}'.format(row)].number_format = "0.000"
sheet['E{0}'.format(row)] = round(data['Center'][i].y, 0) * scale
sheet['E{0}'.format(row)].number_format = "0.000"
try:
sheet['F{0}'.format(row)] = round(data['Soil'][i].z, 0) * scale
sheet['F{0}'.format(row)].number_format = "0.000"
except:
pass
sheet['G{0}'.format(row)] = round(data['Head'][i]) * scale
sheet['G{0}'.format(row)].number_format = "0.000"
sheet['H{0}'.format(row)] = data["PoleLength"][i] * scale
sheet['H{0}'.format(row)].number_format = "0.000"
sheet['I{0}'.format(row)] = '=G{0}-F{0}'.format(row)
sheet['I{0}'.format(row)].number_format = "0.000"
sheet['J{0}'.format(row)] = '=H{0}-I{0}'.format(row)
sheet['J{0}'.format(row)].number_format = "0.000"
style_range(sheet, 'A' + str(row) + ':J' + str(row),
border=Border(top=thin, left=thin, right=thin, bottom=thin),
font=Font(name='Quicksand', size=11,),
alignment=Alignment(horizontal="center", vertical="center"))
row += 1
def spreadsheetBOQPanelCollision(sheet, sel):
# Headers:
sheet['A1'] = 'Frame'
sheet['B1'] = 'Nombre'
sheet['C1'] = 'X'
sheet['D1'] = 'Y'
sheet['E1'] = 'Z'
sheet['G1'] = 'Ángulo E-O'
sheet['H1'] = 'Nº Hincas'
sheet.column_dimensions['A'].width = 30
sheet.column_dimensions['B'].width = 8
sheet.column_dimensions['C'].width = 20
sheet.column_dimensions['D'].width = 20
sheet.column_dimensions['E'].width = 20
sheet.column_dimensions['F'].width = 20
sheet.column_dimensions['G'].width = 20
sheet.column_dimensions['H'].width = 20
sheet.column_dimensions['I'].width = 20
sheet.row_dimensions[1].height = 40
style_range(sheet, 'A1:I1',
border=Border(top=thin, left=thin, right=thin, bottom=thin),
fill=PatternFill("solid", fgColor="7DA4B8"),
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
alignment=Alignment(horizontal="center", vertical="center"))
sheet['A2'] = ""
sheet.row_dimensions[2].height = 5
# Data:
for frame_ind in range(0, len(sel)):
frame = sel[frame_ind]
sheet['A{0}'.format(ind + 2)] = ind
sheet['B{0}'.format(ind + 2)] = sel[ind].Label
sheet['C{0}'.format(ind + 2)] = sel[ind].Placement.Base.x * scale
sheet['D{0}'.format(ind + 2)] = sel[ind].Placement.Base.y * scale
sheet['E{0}'.format(ind + 2)] = sel[ind].Placement.Base.z * scale
sheet['F{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[0]
sheet['G{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[1]
sheet['H{0}'.format(ind + 2)] = sel[ind].NumberPole.Value
class _CommandBOQMechanical:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "boqm.svg")),
'Accel': "R, M",
'MenuText': QT_TRANSLATE_NOOP("Placement", "BOQ Mecánico"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
def Activated(self):
# make file global:
#sel = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
sel = []
for obj in FreeCAD.ActiveDocument.Objects:
'''if not hasattr(obj, "Proxy"):
continue
if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
objects.append(obj)'''
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
sel.append(obj)
sel = sorted(sel, key=lambda x: x.Label)
if len(sel) > 0:
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
filename = os.path.join(path, "BOQMechanical.xlsx")
mywb = openpyxl.Workbook()
sheet = mywb.active
sheet.title = 'Frames information'
spreadsheetBOQFrames(sheet, sel)
sheet = mywb.create_sheet("Poles information")
spreadsheetBOQPoles(sheet, sel)
mywb.save(filename)
print("Se ha generado el BOQMechanical: ")
print(filename)
'''import sys
path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(path)'''
import subprocess
subprocess.Popen('explorer ' + path)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('BOQMechanical', _CommandBOQMechanical())

466
Export/exportDXF.py Normal file
View File

@@ -0,0 +1,466 @@
import math
import FreeCAD
from Utils.PVPlantUtils import findObjects
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Export to DXF"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
field = {"name": "", "width": 0, "heigth": 0}
class exportDXF:
def __init__(self, filename):
''' '''
self.doc = None
self.msp = None
self.filename = filename
'''
doc.linetypes.add("GRENZE2",
# linetype definition in acad.lin:
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
# replacing BOX by shape index 132 (got index from an AutoCAD file),
# ezdxf can't get shape index from ltypeshp.shx
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
description="Grenze eckig ----[]-----[]----[]-----[]----[]--",
length=1.45, # required for complex line types
})
doc.linetypes.add("GRENZE2",
# linetype definition in acad.lin:
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
# replacing BOX by shape index 132 (got index from an AutoCAD file),
# ezdxf can't get shape index from ltypeshp.shx
pattern = "A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
description = "Límite1 ----0-----0----0-----0----0-----0--",
length = 1.45, # required for complex line types
})
'''
def createFile(self, version='R2018'):
import ezdxf
from ezdxf.tools.standards import linetypes
# 1. Create a new document
self.doc = ezdxf.new(version)
# 2. Setup document:
self.doc.header["$INSUNITS"] = 6
self.doc.header['$MEASUREMENT'] = 1
self.doc.header['$LUNITS'] = 2
self.doc.header['$AUNITS'] = 0
# 3. Add new entities to the modelspace:
self.msp = self.doc.modelspace()
print("linetypes: ", self.doc.linetypes)
for name, desc, pattern in linetypes():
if name not in self.doc.linetypes:
self.doc.linetypes.add(name=name,
pattern=pattern,
description=desc,
)
self.doc.linetypes.add(name="FENCELINE1",
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
description="Límite1 ----0-----0----0-----0----0-----0--",
length=1.45) # required for complex line types
def save(self):
from os.path import exists
file_exists = exists(self.filename)
self.doc.saveas(self.filename)
self.doc.save()
def createLayer(self, layerName="newLayer", layerColor=(0,0,0), layerLineType='CONTINUOUS'):
layer = self.doc.layers.add(name=layerName, linetype=layerLineType)
layer.rgb = layerColor
return layer
def createBlock(self, blockName='newBlock'):
# Create a block
block = self.doc.blocks.new(name=blockName)
return block
def insertBlock(self, name, point=(0.0, 0.0), rotation=0.0, xscale=0.0, yscale=0.0):
if name == "":
return None
return self.msp.add_blockref(name, point, dxfattribs={
'xscale': xscale,
'yscale': yscale,
'rotation': rotation
})
def createPolyline(self, wire):
data = getWire(wire.Shape)
lwp = self.msp.add_lwpolyline(data)
return lwp
def getWire(wire, nospline=False, width=.0):
"""Return a list of DXF ready points and bulges from a wire.
It builds a list of points from the edges of a `wire`.
If the edges are circular arcs, the "bulge" of that edge is calculated,
for other cases, the bulge is considered zero.
Parameters
----------
wire : Part::TopoShape ('Wire')
A shape representing a wire.
nospline : bool, optional
It defaults to `False`.
If it is `True`, the edges of the wire are not considered as
being one of `'BSplineCurve'`, `'BezierCurve'`, or `'Ellipse'`,
and a simple point is added to the list.
Otherwise, `getSplineSegs(edge)` is used to extract
the points and add them to the list.
Returns
-------
list of tuples
It returns a list of tuples ``[(...), (...), ...]``
where each tuple indicates a point with additional information
besides the coordinates.
Two types of tuples may be returned.
[(float, float, float, None, None, float), ...]
When `lw` is `True` (`'lwpolyline'`)
the first three values represent the coordinates of the point,
the next two are `None`, and the last value is the bulge.
[((float, float, float), None, [None, None], float), ...]
When `lw` is `False` (`'polyline'`)
the first element is a tuple of three values that indicate
the coordinates of the point, the next element is `None`,
the next element is a list of two `None` values,
and the last element is the value of the bulge.
See also
--------
calcBulge
"""
import Part
import DraftGeomUtils
import math
def fmt(vec, b=0.0):
return (vec.x * 0.001, vec.y * 0.001, width, width, b)
points = []
edges = Part.__sortEdges__(wire.Edges)
for edge in edges:
v1 = edge.Vertexes[0].Point
if DraftGeomUtils.geomType(edge) == "Circle":
# polyline bulge -> negative makes the arc go clockwise
angle = edge.LastParameter - edge.FirstParameter
bul = math.tan(angle / 4)
if edge.Curve.Axis.dot(FreeCAD.Vector(0, 0, 1)) < 0:
bul = -bul
points.append(fmt(v1, bul))
elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve",
"BezierCurve",
"Ellipse"]) and (not nospline):
spline = getSplineSegs(edge)
spline.pop()
for p in spline:
points.append(fmt(p))
else:
points.append(fmt(v1))
v = edges[-1].Vertexes[-1].Point
points.append(fmt(v))
return points
def getArcData(edge):
"""Return center, radius, start, and end angles of a circle-based edge.
Parameters
----------
edge : Part::TopoShape ('Edge')
An edge representing a circular arc, either open or closed.
Returns
-------
(tuple, float, float, float)
It returns a tuple of four values; the first value is a tuple
with the coordinates of the center `(x, y, z)`;
the other three represent the magnitude of the radius,
and the start and end angles in degrees that define the arc.
(tuple, float, 0, 0)
If the number of vertices in the `edge` is only one, only the center
point exists, so it's a full circumference; in this case, both
angles are zero.
"""
ce = edge.Curve.Center
radius = edge.Curve.Radius
if len(edge.Vertexes) == 1:
# closed circle
return DraftVecUtils.tup(ce), radius, 0, 0
else:
# new method: recalculate ourselves as we cannot trust edge.Curve.Axis
# or XAxis
p1 = edge.Vertexes[0].Point
p2 = edge.Vertexes[-1].Point
v1 = p1.sub(ce)
v2 = p2.sub(ce)
# print(v1.cross(v2))
# print(edge.Curve.Axis)
# print(p1)
# print(p2)
# we can use Z check since arcs getting here will ALWAYS be in XY plane
# Z can be 0 if the arc is 180 deg
# if (v1.cross(v2).z >= 0) or (edge.Curve.Axis.z > 0):
# Calculates the angles of the first and last points
# in the circular arc, with respect to the global X axis.
if edge.Curve.Axis.z > 0:
# clockwise
ang1 = -DraftVecUtils.angle(v1)
ang2 = -DraftVecUtils.angle(v2)
else:
# counterclockwise
ang2 = -DraftVecUtils.angle(v1)
ang1 = -DraftVecUtils.angle(v2)
# obsolete method - fails a lot
# if round(edge.Curve.Axis.dot(Vector(0, 0, 1))) == 1:
# ang1, ang2 = edge.ParameterRange
# else:
# ang2, ang1 = edge.ParameterRange
# if edge.Curve.XAxis != Vector(1, 0, 0):
# ang1 -= DraftVecUtils.angle(edge.Curve.XAxis)
# ang2 -= DraftVecUtils.angle(edge.Curve.XAxis)
return (DraftVecUtils.tup(ce), radius,
math.degrees(ang1), math.degrees(ang2))
class _PVPlantExportDXF(QtGui.QWidget):
'''The editmode TaskPanel to select what you want to export'''
def __init__(self):
# super(_PVPlantExportDXF, self).__init__()
QtGui.QWidget.__init__(self)
import os
# self.form:
self.form = FreeCADGui.PySideUic.loadUi(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "exportDXF.ui"))
# setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))
# self.form.buttonTo.clicked.connect(self.addTo)
self.layout = QtGui.QHBoxLayout(self)
self.layout.setContentsMargins(4, 4, 4, 4)
self.layout.addWidget(self.form)
self.form.buttonAcept.clicked.connect(self.onAceptClick)
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "autocad")
if not os.path.exists(path):
os.makedirs(path)
self.filename = os.path.join(path, FreeCAD.ActiveDocument.Name) + ".dxf"
def createLayers(self):
''' '''
self.exporter.createLayer("Areas_Boundary", layerColor=(0, 125, 125), layerLineType="FENCELINE1")
self.exporter.createLayer("Areas_Exclusion", layerColor=(255, 0, 0))
self.exporter.createLayer("Areas_Offsets", layerColor=(128, 128, 255))
self.exporter.createLayer("Internal_Roads", layerColor=(128, 128, 128))
self.exporter.createLayer("Internal_Roads_Axis", layerColor=(255, 255, 255), layerLineType="DASHEDX2")
def writeArea(self):
for area in FreeCAD.ActiveDocument.Boundary.Group:
pol = self.exporter.createPolyline(area)
pol.dxf.layer = "Areas_Boundary"
for area in FreeCAD.ActiveDocument.Exclusion.Group:
pol = self.exporter.createPolyline(area)
pol.dxf.layer = "Areas_Exclusion"
for area in FreeCAD.ActiveDocument.Offsets.Group:
self.exporter.createPolyline(area)
pol.dxf.layer = "Areas_Offsets"
def writeFrameSetups(self):
import Part
# 1. Profiles:
profilelist = list()
for ts in FreeCAD.ActiveDocument.Site.Frames:
for poletype in ts.PoleType:
if not (poletype in profilelist):
profilelist.append(poletype)
block = self.exporter.createBlock(poletype.Label)
w = poletype.Base.Shape.Wires[0]
w.Placement.Base = FreeCAD.Vector(w.Placement.Base).sub(w.BoundBox.Center)
block.add_lwpolyline(getWire(w))
block.add_circle((0, 0), 0.2, dxfattribs={'color': 2})
p = math.sin(math.radians(45)) * 0.2
block.add_line((-p, -p), (p, p), dxfattribs={"layer": "MyLines"})
block.add_line((-p, p), (p, -p), dxfattribs={"layer": "MyLines"})
# 2. Frames
for ts in FreeCAD.ActiveDocument.Site.Frames:
w = max(ts.Shape.SubShapes[0].SubShapes[0].SubShapes[0].Faces, key=lambda x: x.Area)
pts = [w.BoundBox.getPoint(i) for i in range(4)]
pts.append(FreeCAD.Vector(pts[0]))
w = Part.makePolygon(pts)
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
mblockname = "Trina_TSM-DEG21C-20-6XXWp Vertex"
mblock = self.exporter.createBlock(mblockname)
mblock.add_lwpolyline(getWire(w))
rblock = self.exporter.createBlock(ts.Label)
w = max(ts.Shape.SubShapes[0].SubShapes[1].SubShapes[0].Faces, key=lambda x: x.Placement.Base.z).Wires[0]
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
rblock.add_lwpolyline(getWire(w))
for module in ts.Shape.SubShapes[0].SubShapes[0].SubShapes:
point = FreeCAD.Vector(module.BoundBox.Center) * 0.001
point = point[:2]
rblock.add_blockref(mblockname, point, dxfattribs={
'xscale': .0,
'yscale': .0,
'rotation': 0})
for ind in range(int(ts.NumberPole.Value)):
point = ts.Shape.SubShapes[1].SubShapes[0].SubShapes[ind].Placement.Base * 0.001
point = point[:2]
name = ts.PoleType[ts.PoleSequence[ind]].Label
rblock.add_blockref(name, point, dxfattribs={
'xscale': .0,
'yscale': .0,
'rotation': 0})
def writeFrames(self):
objects = findObjects('Tracker')
for frame in objects:
if hasattr(frame, "Setup"):
point = frame.Placement.Base * 0.001
point = point[:2]
self.exporter.insertBlock(frame.Setup.Label, point=point, rotation=frame.AngleZ)
def writeRoads(self):
objects = findObjects("Road")
#rblock = self.exporter.createBlock("Internal_roads")
for road in objects:
base = self.exporter.createPolyline(road.Base)
base.dxf.const_width = road.Width.Value * 0.001
base.dxf.layer = "Internal_Roads"
axis = self.exporter.createPolyline(road.Base)
axis.dxf.const_width = .2
axis.dxf.layer = "Internal_Roads_Axis"
#my_lines = doc.layers.get('MyLines')
def writeTrenches(self):
objects = findObjects("Trench")
# rblock = self.exporter.createBlock("Internal_roads")
for obj in objects:
base = self.exporter.createPolyline(obj.Base)
base.dxf.const_width = obj.Width.Value * 0.001
base.dxf.layer = "Trench"
def setup_layout4(self, doc):
layout2 = doc.layouts.new("scale 1-1")
# The default paperspace scale is 1:1
# 1 mm printed is 1 drawing unit in paperspace
# For most use cases this is the preferred scaling and important fact:
# the paperspace scaling has no influence on the VIEWPORT scaling - this is
# a total different topic, see example "viewports_in_paperspace.py"
layout2.page_setup(size=(297, 210),
margins=(10, 10, 10, 10),
units="mm",
scale=(1, 1),
#offset=(50, 50),
)
layout2.add_viewport(
# center of viewport in paperspace units
center=(100, 100),
# viewport size in paperspace units
size=(50, 50),
# modelspace point to show in center of viewport in WCS
view_center_point=(60, 40),
# how much modelspace area to show in viewport in drawing units
view_height=20,
#status=2,
)
lower_left, upper_right = layout2.get_paper_limits()
x1, y1 = lower_left
x2, y2 = upper_right
center = lower_left.lerp(upper_right)
# Add DXF entities to the "Layout1" in paperspace coordinates:
layout2.add_line((x1, center.y), (x2, center.y)) # horizontal center line
layout2.add_line((center.x, y1), (center.x, y2)) # vertical center line
layout2.add_circle((0, 0), radius=5) # plot origin
def onAceptClick(self):
self.exporter = exportDXF(self.filename)
if self.exporter:
self.exporter.createFile()
self.createLayers()
self.writeArea()
self.writeFrameSetups()
self.writeFrames()
self.writeRoads()
self.writeTrenches()
self.setup_layout4(self.exporter.doc)
self.exporter.save()
print(self.filename)
self.close()
class _CommandExportDXF:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
'Accel': "E, A",
'MenuText': "Export to DXF",
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Export choosed layers to dxf")}
def Activated(self):
taskd = _PVPlantExportDXF()
taskd.setParent(FreeCADGui.getMainWindow())
taskd.setWindowFlags(QtCore.Qt.Dialog or QtCore.Qt.Dialog)
taskd.setWindowModality(QtCore.Qt.WindowModal)
taskd.show()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('exportDXF', _CommandExportDXF())

77
Export/exportDXF.ui Normal file
View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formPVSyst</class>
<widget class="QDialog" name="formPVSyst">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>Export to PVSyst</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="buttonAcept">
<property name="text">
<string>Aceptar</string>
</property>
<property name="icon">
<iconset theme="Accept">
<normaloff>../../../../../.designer/backup</normaloff>../../../../../.designer/backup</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCancel">
<property name="text">
<string>Cancelar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

15
Export/exportGeoJson.py Normal file
View File

@@ -0,0 +1,15 @@
import geojson
from geojson import Point, Feature, FeatureCollection, dump
point = Point((-115.81, 37.24))
features = []
features.append(Feature(geometry=point, properties={"country": "Spain"}))
# add more features...
# features.append(...)
feature_collection = FeatureCollection(features)
with open('myfile.geojson', 'w') as f:
dump(feature_collection, f)

470
Export/exportPF.py Normal file
View File

@@ -0,0 +1,470 @@
import sys
import os
path_PF = r"C:\Program Files\DIgSILENT\PowerFactory 2021 SP2\Python\3.10"
os.environ['PATH'] = path_PF + ";" + os.environ['PATH']
os.add_dll_directory(path_PF)
sys.path.append(path_PF)
import powerfactory as pf
app = pf.GetApplication()
print(app)
user = app.GetCurrentuser()
project = app.ActivateProject('Nine-bus System') # Nombre del proyecto
prj = app.GetActiveProject()
import sys
import os
path = r'C:\Program Files\DIgSILENT\PowerFactory 2021 SP2\Python\3.10'
os.environ['PATH'] += (";" + path)
sys.path.append(path)
import powerfactory as pf
app = pf.GetApplication()
if app is None:
raise Exception('getting Powerfactory application failed')
# app.Show()
user = app.GetCurrentUser()
projects = user.GetContents('*.IntPrj', 0)
names = [pro.GetAttribute("loc_name") for pro in projects]
res = app.ActivateProject("ProyectoPrueba")
if res == 1: # no existe y hay crearlo
proj = app.CreateProject("ProyectoPrueba", "GridNueva")
else:
proj = app.GetActiveProject()
#########
# MODEL #
#########
# Network model
netmod = app.GetProjectFolder('netmod')
# Network data
netdata = app.GetProjectFolder('netdat')
# Diagrams
diag_fold = app.GetProjectFolder('dia')
# Variations
var_fold = app.GetProjectFolder('scheme')
###########
# LIBRARY #
###########
# Equipment library
equip = app.GetProjectFolder('equip')
# lines_fold = equip.GetContents('Lines')[0][0]
# User defined models
main_lib = equip.GetParent()
# udm = main_lib.GetContents('User Defined Models*')[0][0]
# Script library
script_lib = app.GetProjectFolder('script')
# Template library
templ_lib = app.GetProjectFolder('templ')
# Operational library
oplib = app.GetProjectFolder('oplib')
# Characteristics
# opchar = oplib.GetContents('Characteristics')[0][0]
# Thermal ratings
therm_fold = app.GetProjectFolder('therm')
# MVAr limit curves
mvar_fold = app.GetProjectFolder('mvar')
##############
# STUDY CASE #
##############
# Study cases
op_scen = app.GetProjectFolder('scen')
# Operational scenarios
sc_fold = app.GetProjectFolder('study')
# Diagramas:
schemas = diag_fold.GetContents('*.IntGrfnet', 0)
sch = None
for obj in schemas:
if obj.loc_name == "miEsquema":
sch = obj
break
if sch is None:
sch = diag_fold.CreateObject("IntGrfnet", "miEsquema")
# redes:
print(netdata)
grids = netdata.GetContents('*.ElmNet', 0)
grid = grids[0]
ln = grid.GetContents('*.ElmLne', 0)[0]
print(ln)
# print(ln.type_id)
print(f"Terminal 1 de la línea {ln.loc_name}: {ln.bus1}")
print(f"Terminal 2 de la línea {ln.loc_name}: {ln.bus2}")
def createCable(name):
''''''
cable = equip.CreateObject("TypCab", name)
cable.uline
cable.typCon = "cmp" # Forma (cmp: Macizo, hol: orificio, seg: sermento)
cable.diaCon = 5.0 # diámetro externo (double) (mm)
# Capas condutoras: conductor, funda, blindaje
cable.cHasEl = [1, 1, 1] # Existe
cable.materialCond = [] # Material ()
cable.crho = [] # Resistividad 20ºC (double) (uOhm*cm)
cable.my = [] # Premeabilidad relativa (double)
cable.cThEl = [] # Espesor (dobule) (mm)
cable.Cf = [] # Factor de Relleno (double) (%)
cable.rpha = [] # Resistenca DC 20ºC Ohm/km
# Capas de aislamiento: Aislamiento, Funda Exterior, Cubierta
cable.cHasIns = [0, 0, 0] # Existe
cable.materialIns = [] # Material
cable.ctand = [] # Factor de Pérdidas Dieléctricas
cable.cepsr = [] # Permeavilidad relativa
cable.thlns = [] # Espesor (mm)
# Capas semicoductoras: Nucleo, Aislamiento
cable.cHasSc = [] # Existe
cable.thSc = [] # Espesor (mm)
cable.cAdvSc = [] # Avanzado
cable.rhoSc = [] # Resistividad uOhm*cm
cable.mySc = [] # Permeabilidad relativa
cable.epsrSC = [] # Premitividad relativa
cable.manuf = "" # fabricante
# 1 Flujo de carga
cable.tmax = 90.0 # Máx. temperatura (ºC)
# 2. Cortocircuito VDE/IEC
cable.rtemp = 80.0 # temperatura final máxima (ºC)
cable.Ithr = 0.0 # Corriente de corta duración 1s (kA)
# 3. Análisis de Cables
cable.tmax_screen = 70.0 # max. temparatura operación (ºC)
cable.eps_emiss = 0.9 # coeficiente de emisión en la superficie
cable.iopt_treated = True # Secado e impregnado
cable.tshc = 1.0 # Cálculo de cortocircuito adiabático - Duración de la falla (s)
return cable
def createLine(name):
elmlne = p.CreateObject("ElmPvsys", name)
elmlne.type_id = "" # Tipo
elmlne.bus1 = "" # terminal i
elmlne.bus2 = "" # terminal j
elmlne.nlnum = 1 # líneas en paralelo
elmlne.dline = 0.8 # longitud de la línea (double) (km)
elmlne.fline = 1.0 # factor de reduccion (double)
elmlne.inAir = 0 # Medio (0: Tierra, 1: Aérea)
elmlne.i_dist = 0 # Modelo de la línea (0: Parámetros Concentrados, 1: Parámetros Distribuidos)
return elmlne
def createPVPanel(name):
typpvpanel = equip.CreateObject("TypPvPanel", name)
typpvpanel.Ppk = 500.0 # Potencia Pico MPP (double) (W)
typpvpanel.Umpp = 80.0 # Tensión nominal MPP (double) (V)
typpvpanel.Impp = 6.0 # Corriente nominal MPP (double) (A)
typpvpanel.Uoc = 90.0 # Tensión de Circuito Abierto (double) (V)
typpvpanel.Isc = 7.0 # Corriente de cortocircuito (double) (A)
typpvpanel.material = 0 # Material: 0:mono-SI, 1:poli-Si, 2:samor-SI, 3: CIS, 4:CdTe, 5: otro
typpvpanel.usetval = 1 # Usar valores típicos
typpvpanel.cT = -0.4 # Coeficiente de tenperatura P (double) (%/Cº)
typpvpanel.noct = 45.0 # SINTC (double) (Cº)
typpvpanel.manuf = "" # Fabricante
typpypanel.chr_name = "" # Nombre característico
typpypanel.appr_status = 1 # Estado: 0: No aprobado, 1: aprobado
return typpvpanel
def createPV(name):
elmpvsys = grid.CreateObject("ElmPvsys", name)
elmpvsys.typ_id = None
elmpvsys.bus1 = None # terminal ()
elmpvsys.mode_pgi = 0 # modelo (0:Entrada de potencia Activa, 1: Cálculo solar)
elmpvsys.phtech = 0 # tecnología (0: 3F, 1: 3F-T, 2:1F F-T, 3:1F F-N)
elmpvsys.ngnum = 1 # Inversores en paralelo (int)
elmpvsys.npnum = 1 # Paneles por incersro (int)
elmpvsys.sgn = 1.0 # Potencia aparente (double) (kVA)
elmpvsys.cosn = 0.8 # Factor de Potencia nominal (double)
# elmpvsys.
return elmpvsys
def createBarra(name, uknom=33.0):
typbar = equip.CreateObject("TypBar", name)
typbar.uknow = uknom
# 1. Cortocircuito VDE/IEC
typbar.Ithlim = 0 # Corriente Nominal de Corto tiempo (kA)
typbar.Tkr = 1 # tiempo Corriente Nominal de Corto tiempo (s)
typbar.Iplim = 0 # Corriente Pico de cortocircuito (kA)
return typbar
def createTerminal(name):
elmterm = grid.CreateObject("ElmTerm", name)
elmterm.systype = 0 # Tipo de sistema (0: AC, 1: DC, 2: AC/BI)
elmterm.iUsage = 0 # Uso (0:Busbar, 1:Junction, Node 2: Internal node)
elmterm.phtech = "ABC" # Tecnología de Fases: ABC, ABC-N, BI, BI-N, 2F, 2F-N, 1F, 1F-N, N
elmterm.uknom = 33.0 # Tensión Nominal Línea-Línea (double)
elmterm.iEarth = False # Aterrizado (bool)
# Load Flow:
# 1. Control de tensión:
elmterm.vtarget = 1.0 # Tensión destino (double) (p.u.)
elmterm.Vtarget = 0.0 # Tensión destino (double) (kV)
elmterm.dvmax = 0.0 # Max delta V (double) (%)
elmterm.dvmin = 0.0 # Min delta V (double) (%)
elmterm.ivpriority = -1 # prioriddad
# 2. Límite de tensión del estado estable
elmterm.vmax = 1.05 # Límite superior de tensión (p.u.)
elmterm.vmin = 0.0 # Límite inferior de tensión (p.u.)
# 3. Límites del cambio del paso de tensión
elmterm.vstep_change = True # Límites del cambio del paso de tensión (bool)
elmterm.vstep_n1 = 6.0 # n-1 (%)
elmterm.vstep_n2 = 12.0 # n-2 (%)
elmterm.vstep_bus = 12.0 # falla de barra (%)
# Análisis de Arco Eléctrico:
elmterm.iAccessLoc = False # Ubicación Accesible (bool)
elmterm.isSoftConstr = False # Restrinción blanda (bool)
crearCelda()
return elmterm
def createCubic(parent, name):
stacubic = parent.CreateObject("StaCubic", name)
stacubic.cterm = parent
# stacubic.obj_id =
# stacubic.plntObjs =
return stacubic
def createSwitch(name):
staswitch = ""
def createTransformer(name):
#
def createTransformerType(name):
typTr3 = equip.CreateObject("TypTr3", name)
typTr3.nt3ph = 3 # Tecnología (2: Monofásico, 3: trifásico)
# Potencia Nominal:
typTr3.strn3_h = 1.0 # Lado AT (double) (MVA)
typTr3.strn3_m = 1.0 # Lado MT (double) (MVA)
typTr3.strn3_l = 1.0 # Lado BT (double) (MVA)
# Tensión nominal:
typTr3.utrn3_h = 0.0 # Lado AT (double) (kV)
typTr3.utrn3_m = 0.0 # Lado MT (double) (kV)
typTr3.utrn3_l = 0.0 # Lado BT (double) (kV)
# Grupo Vectorial:
typTr3.tr3cn_h = "YN" # Lado AT (Y, YN, Z, ZN, D)
typTr3.nt3ag_h = 0.0 # Lado AT - Ángulo de desfase (double) (*30deg)
typTr3.tr3cn_m = "YN" # Lado MT (Y, YN, Z, ZN, D)
typTr3.nt3ag_m = 0.0 # Lado MT - Ángulo de desfase (double) (*30deg)
typTr3.tr3cn_l = "YN" # Lado BT (Y, YN, Z, ZN, D)
typTr3.nt3ag_l = 0.0 # Lado BT - Ángulo de desfase (double) (*30deg)
# Impedancia de secuencia positiva
# Tensión de Cortocircuito uk:
typTr3.uktr3_h = 3.0 # AT-MT (double) (%)
typTr3.uktr3_m = 3.0 # MT-BT (double) (%)
typTr3.uktr3_l = 3.0 # BT-AT (double) (%)
# Pérdidas en el Cobre
typTr3.pcut3_h = 0.0 # AT-MT (double) (kW)
typTr3.pcut3_m = 0.0 # MT-BT (double) (kW)
typTr3.pcut3_l = 0.0 # BT-AT (double) (kW)
# Impedancia de secuencia cero
# Tensión de Cortocircuito uk0:
typTr3.uk0hm = 3.0 # AT-MT (double) (%)
typTr3.uk0ml = 3.0 # MT-BT (double) (%)
typTr3.uk0hl = 3.0 # BT-AT (double) (%)
# Pérdidas en el Cobre
typTr3.ur0hm = 0.0 # AT-MT (double) (kW)
typTr3.ur0ml = 0.0 # MT-BT (double) (kW)
typTr3.ur0hl = 0.0 # BT-AT (double) (kW)
# print(dir(app))
# print(dir(app.CreateProject))
cable = equip.GetContents('*.TypCab', 0)[0]
print(dir(cable))
print(cable.cHasEl)
print(cable.materialCond)
print(cable.tab_con)
'''
Run a load flow
#define project name and study case
projName = '_TSI_Nine-bus System'
study_case = '01_Study_Case.IntCase'
#activate project
project = app.ActivateProject(projName)
proj = app.GetActiveProject()
#get the study case folder and activate project
oFolder_studycase = app.GetProjectFolder('study')
oCase = oFolder_studycase.GetContents(study_case)[0]
oCase.Activate()
#get load flow object and execute
oLoadflow=app.GetFromStudyCase('ComLdf') #get load flow object
oLoadflow.Execute() #execute load flow
'''
'''
Print the results for generators, lines, and buses
#get the generators and their active/reactive power and loading
Generators = app.GetCalcRelevantObjects('*.ElmSym')
for gen in Generators: #loop through list
name = getattr(gen, 'loc_name') # get name of the generator
actPower = getattr(gen,'c:p') #get active power
reacPower = getattr(gen,'c:q') #get reactive power
genloading = getattr(gen,'c:loading') #get loading
#print results
print('%s: P = %.2f MW, Q = %.2f MVAr, loading = %.0f percent' %(name,actPower,reacPower,genloading))
print('-----------------------------------------')
#get the lines and print their loading
Lines=app.GetCalcRelevantObjects('*.ElmLne')
for line in Lines: #loop through list
name = getattr(line, 'loc_name') # get name of the line
value = getattr(line, 'c:loading') #get value for the loading
#print results
print('Loading of the line: %s = %.2f percent' %(name,value))
print('-----------------------------------------')
#get the buses and print their voltage
Buses=app.GetCalcRelevantObjects('*.ElmTerm')
for bus in Buses: #loop through list
name = getattr(bus, 'loc_name') # get name of the bus
amp = getattr(bus, 'm:u1') #get voltage magnitude
phase = getattr(bus, 'm:phiu') #get voltage angle
#print results
print('Voltage at %s = %.2f pu %.2f deg' %(name,amp,phase))
'''
'''
Run an RMS simulation
#define project name and study case
projName = '_TSI_nine_bus_system'
study_case = '01_Study_Case.IntCase'
#activate project
project = app.ActivateProject(projName)
proj = app.GetActiveProject()
#get the study case folder and activate project
oFolder_studycase = app.GetProjectFolder('study')
oCase = oFolder_studycase.GetContents(study_case)[0]
oCase.Activate()
# calculate initial conditions
oInit = app.GetFromStudyCase('ComInc') #get initial condition calculation object
oInit.Execute()
#run RMS-simulation
oRms = app.GetFromStudyCase('ComSim') #get RMS-simulation object
oRms.Execute()
'''
'''
Retrieve RMS results from PowerFactory in Python
import numpy as np
import pandas as pd
def getResults():
#get result file
elmRes = app.GetFromStudyCase('*.ElmRes')
app.ResLoadData(elmRes)
#Get number of rows and columns
NrRow = app.ResGetValueCount(elmRes,0)
#get objects of interest
oSG1 = app.GetCalcRelevantObjects('G1.ElmSym')[0]
oBus1 = app.GetCalcRelevantObjects('Bus 1.ElmTerm')[0]
oLine4_5 = app.GetCalcRelevantObjects('Line 4-5.ElmLne')[0]
#Get index of variable of interest
ColIndex_time = app.ResGetIndex(elmRes,elmRes,'b:tnow')
ColIndex_ut = app.ResGetIndex(elmRes,oSG1,'s:ut')
ColIndex_P = app.ResGetIndex(elmRes,oSG1,'s:P1')
ColIndex_Q = app.ResGetIndex(elmRes,oSG1,'s:Q1')
ColIndex_speed = app.ResGetIndex(elmRes,oSG1,'s:xspeed')
ColIndex_u_bus1 = app.ResGetIndex(elmRes,oBus1,'m:u')
ColIndex_loading_line_4_5 = app.ResGetIndex(elmRes,oLine4_5,'c:loading')
#pre-allocate result variables
result_time = np.zeros((NrRow,))
result_ut = np.zeros((NrRow))
result_P = np.zeros((NrRow))
result_Q = np.zeros((NrRow))
result_speed = np.zeros((NrRow))
result_u_bus1 = np.zeros((NrRow))
result_loading_line_4_5 = np.zeros((NrRow))
#get results for each time step
for i in range(NrRow):
result_time[i] = app.ResGetData(elmRes,i,ColIndex_time)[1]
result_ut[i] = app.ResGetData(elmRes,i,ColIndex_ut)[1]
result_P[i] = app.ResGetData(elmRes,i,ColIndex_P)[1]
result_Q[i] = app.ResGetData(elmRes,i,ColIndex_Q)[1]
result_speed[i] = app.ResGetData(elmRes,i,ColIndex_speed)[1]
result_u_bus1[i] = app.ResGetData(elmRes,i,ColIndex_u_bus1)[1]
result_loading_line_4_5[i] = app.ResGetData(elmRes,i,ColIndex_loading_line_4_5)[1]
results = pd.DataFrame()
results['time'] = result_time
results['P'] = result_P
results['Q'] = result_Q
results['ut'] = result_ut
results['speed'] = result_speed
results['u_bus1'] = result_u_bus1
results['loading_line_4_5'] = result_loading_line_4_5
return results
#query results
RES = getResults()
'''
## Graphical objects:

864
Export/exportPVSyst.py Normal file
View File

@@ -0,0 +1,864 @@
# /**********************************************************************
# * *
# * 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
## @package importDAE
# \ingroup ARCH
# \brief DAE (Collada) file format importer and exporter
#
# This module provides tools to import and export Collada (.dae) files.
__title__ = "FreeCAD Collada importer"
__author__ = "Yorik van Havre"
__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[i].x, centers[i].y, centers[i].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 type in frameType:
isTracker = "tracker" in type.Proxy.Type.lower()
#TODO: Sólo para los proyectos de NAcho. Borrar
isTracker = False
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 == type:
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 type in grouptype:
st += 'TABLE\n' \
'10\n'
st += f3.format(type.Width.Value) + ',' + f3.format(type.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(type.ModulesCols.Value)) + ',' + str(int(type.ModulesRows.Value)) + ',' + \
str(type.ModuleColGap.Value) + ',' + str(type.ModuleRowGap.Value) + ',' + '30\n'
st += '30\n'
st += '1,' + f3.format(type.ModuleWidth.Value) + ',' + f3.format(type.ModuleHeight.Value) + ',' + \
f3.format(type.ModuleThick.Value) + ',' + f2.format(450) + '\n' #f2.format(type.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 == type:
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, type):
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
f3 = '{:.3f}'
st = ''
if type:
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())

84
Export/exportPVSyst.ui Normal file
View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formPVSyst</class>
<widget class="QDialog" name="formPVSyst">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>301</width>
<height>123</height>
</rect>
</property>
<property name="windowTitle">
<string>Export to PVSyst</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Export format:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="cbH2P">
<property name="text">
<string>Helios 3D (H2P) - PVSyst 6.0 or higher</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbPVC">
<property name="text">
<string>PVCase (PVC) - PVSyst 7.0 or higher</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbTerrain">
<property name="text">
<string>Exportar terreno</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

159
Export/layoutToExcel.py Normal file
View File

@@ -0,0 +1,159 @@
# /**********************************************************************
# * *
# * 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 FreeCAD, Draft
import PVPlantSite
import copy
if FreeCAD.GuiUp:
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import Part
import pivy
from pivy import coin
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Trench"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
from PVPlantResources import DirIcons as DirIcons
from PVPlantResources import DirDocuments as DirDocuments
'''import os
import platform
import subprocess
def open_file(path):
if platform.system() == "Windows":
os.startfile(path)
elif platform.system() == "Darwin":
subprocess.Popen(["open", path])
else:
subprocess.Popen(["xdg-open", path])'''
from PVPlantPlacement import getCols
def generateLayout(numcells = 3, gapc = 3):
# TODO: hacerlo por zonas
objects = []
for obj in FreeCAD.ActiveDocument.Objects:
if not hasattr(obj, "Proxy"):
continue
'''if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
objects.append(obj)'''
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
objects.append(obj)
if len(objects) == 0:
FreeCAD.Console.PrintError("No hay trackes" + "\n")
return False
columns = getCols(objects.copy())
MaxY = max(objects, key=lambda obj: obj.Placement.Base.y).Placement.Base.y
long_max = max(objects, key=lambda obj: obj.Setup.Length.Value).Setup.Length.Value
num_pole_max = max(objects, key=lambda obj: obj.Setup.NumberPole.Value).Setup.NumberPole.Value
num_module_max = max(objects, key=lambda obj: obj.Setup.ModuleColumns.Value).Setup.ModuleColumns.Value
# TODO: calcular la serparación entre trackers.
gapy = 500
import openpyxl
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
filename = os.path.join(path, "layout.xlsx")
mywb = openpyxl.Workbook()
# 1. Hincas
sheet = mywb.active
sheet.title = 'Poles'
m = -(numcells * num_pole_max + gapc) / (long_max + gapy)
line = lambda x: int(m * (x - MaxY))
drawLayout(sheet, columns, line, 0)
# 2. Paneles
sheet = mywb.create_sheet("Modules")
m = -(numcells * num_module_max + gapc) / (long_max + gapy)
line = lambda x: int(m * (x - MaxY) + 1)
#drawLayout(sheet, columns, line, 1)
mywb.save(filename)
print("Se ha generado el lyout en excel satisfactoriamente en: ")
print(filename)
os.startfile(path)
#from os import startfile
#startfile("archivo.txt")
return True
def drawLayout(sheet, cols, line, cnt = 0, numcell = 3, gap = 3):
from openpyxl.styles import Alignment, Border, Side, PatternFill, GradientFill, Font
thin = Side(border_style="thin", color="000000")
for i, col in enumerate(cols):
colnum = i * 3 + 1
for g, group in enumerate(col):
rownum = int(line(group[0].Placement.Base.y)) + 1
if rownum < 1:
continue
for frame in group:
num = int(frame.Setup.NumberPole.Value if cnt == 0 else frame.Setup.ModuleColumns.Value)
sheet.merge_cells(start_row=rownum, start_column=colnum,
end_row=rownum, end_column=colnum + 1)
titlecell = sheet.cell(row=rownum, column=colnum)
titlecell.value = frame.Label
titlecell.border = Border(top=thin, left=thin, right=thin, bottom=thin)
titlecell.font = Font(b=True, color="FF0000")
titlecell.alignment = Alignment(horizontal="center", vertical="center")
rownum += 1
for ind in range(num):
sheet.merge_cells(start_row=rownum, start_column=colnum,
end_row=rownum + numcell - 1, end_column=colnum)
polecell = sheet.cell(row=rownum, column=colnum)
polecell.value = ind + 1
polecell.alignment = Alignment(horizontal="center", vertical="center")
sheet.merge_cells(start_row=rownum, start_column=colnum + 1,
end_row=rownum + numcell - 1, end_column=colnum + 1)
polecell = sheet.cell(row=rownum, column=colnum + 1)
polecell.value = "Celda-" + str(ind + 1)
polecell.value = frame.Label
polecell.border = Border(top=thin, left=thin, right=thin, bottom=thin)
polecell.font = Font(b=True, color="FF0000")
polecell.alignment = Alignment(horizontal="center", vertical="center")
rownum += numcell
rownum += 1

264
GraphProfile.py Normal file
View File

@@ -0,0 +1,264 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
import Part
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftguitools.gui_trackers as DraftTrackers
import pivy
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "Graph Terrain Profile"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
def makeGraphProfile(path = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "GraphProfile")
_GraphProfile(obj)
_ViewProviderGraphProfile(obj.ViewObject)
if path:
obj.Path = path
return obj
class _GraphProfile:
def __init__(self, obj):
self.setCommonProperties(obj)
def setCommonProperties(self, obj):
pl = obj.PropertiesList
if not ("Path" in pl):
obj.addProperty("App::PropertyLink",
"Path",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "")
)
#obj.setEditorMode("NumberOfStrings", 1)
if not ("AdjustToContent" in pl):
obj.addProperty("App::PropertyBool",
"AdjustToContent",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "")
).AdjustToContent = True
if not ("PKMinorDistance" in pl):
obj.addProperty("App::PropertyDistance",
"PKMinorDistance",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "")
).PKMinorDistance = 20000
if not ("YAxisStep" in pl):
obj.addProperty("App::PropertyDistance",
"YAxisStep",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "")
).YAxisStep = 5000
if not ("Points" in pl):
obj.addProperty("App::PropertyVectorList",
"Points",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "")
)
obj.setEditorMode("Points", 1)
self.Type = "GraphProfile"
obj.Proxy = self
def onDocumentRestored(self, obj):
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
if prop == "Path":
if obj.getPropertyByName(prop):
from Utils import PVPlantUtils
profile = PVPlantUtils.FlattenWire(PVPlantUtils.makeProfileFromTerrain(obj.Path))
obj.Points = PVPlantUtils.getPointsFromVertexes(profile.Vertexes)
else:
obj.Points.clear()
print("Graph: onChanged")
def execute(self, obj):
if (obj.Path is None) or (obj.Points is None):
return
profile = Part.makePolygon(obj.Points)
xx_max = profile.BoundBox.XMax
yy_min = profile.BoundBox.YMin
yy_max = profile.BoundBox.YMax
yy_length = yy_max
yy_axis_minus = int(yy_min / 10) * 10 - obj.YAxisStep.Value
# 1. Make Axis:
x_axis = Part.makePolygon([FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(xx_max, 0, 0)])
y_axis = None
if obj.AdjustToContent:
yy_length -= yy_min
y_axis = Part.makePolygon([FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, (yy_max - yy_min) * 1.1, 0)])
profile.Placement.Base.y = profile.Placement.Base.y - yy_min + obj.YAxisStep.Value
else:
y_axis = Part.makePolygon([FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, yy_max, 0)])
shapes = []
# 2. grid
cnt = 0
while cnt <= xx_max:
line = y_axis.copy()
line.Placement.Base = y_axis.Placement.Base + FreeCAD.Vector(cnt, 0, 0)
shapes.append(line)
cnt += obj.PKMinorDistance.Value
cnt = 0
while cnt <= yy_length:
line = x_axis.copy()
line.Placement.Base = x_axis.Placement.Base + FreeCAD.Vector(0, cnt, 0)
shapes.append(line)
cnt += obj.YAxisStep.Value
shapes.append(profile)
# Shape:
obj.Shape = Part.makeCompound(shapes)
class _ViewProviderGraphProfile:
def __init__(self, vobj):
'''
Set view properties.
'''
self.Object = vobj.Object
vobj.Proxy = self
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
self.Object = vobj.Object
return
def getIcon(self):
'''
Return object treeview icon.
'''
return str(os.path.join(DirIcons, "stringsetup.svg"))
'''
def claimChildren(self):
"""
Provides object grouping
"""
return self.Object.Group
'''
def setEdit(self, vobj, mode=0):
"""
Enable edit
"""
return True
def unsetEdit(self, vobj, mode=0):
"""
Disable edit
"""
return False
def doubleClicked(self, vobj):
"""
Detect double click
"""
pass
def setupContextMenu(self, obj, menu):
"""
Context menu construction
"""
pass
def edit(self):
"""
Edit callback
"""
pass
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self,state):
"""
Get variables from file.
"""
return None
class _CommandGraphProfile:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "Profile.svg")),
'Accel': "C, P",
'MenuText': QT_TRANSLATE_NOOP("Placement", "Terrain Profile Graph"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "")}
def Activated(self):
path = None
sel = FreeCADGui.Selection.getSelection()
if len(sel)>0:
path = sel[0]
obj = makeGraphProfile(path)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('GraphTerrainProfile', _CommandGraphProfile())

143
Importer/importDXF.py Normal file
View File

@@ -0,0 +1,143 @@
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Export to DXF"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def importDXF(filename):
print("---------- import to dxf ----------")
if filename == "":
return
import sys
import ezdxf
doc = None
try:
doc = ezdxf.readfile(filename)
except IOError:
print(f"Not a DXF file or a generic I/O error.")
sys.exit(1)
except ezdxf.DXFStructureError:
print(f"Invalid or corrupted DXF file.")
sys.exit(2)
# iteration
for layer in doc.layers:
print(layer.dxf.name)
#if layer.dxf.name != "0":
# layer.off() # switch all layers off except layer "0"
# check for existing layer definition
if "MyLines" in doc.layers:
layer = doc.layers.get("MyLines")
layer_count = len(doc.layers) # total count of layer definitions
class _PVPlantImportDXF:
'''The editmode TaskPanel to select what you want to export'''
def __init__(self):
self.doc = None
# self.form:
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "importDXF.ui"))
self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "dxf.svg")))
self.form.buttonOpen.clicked.connect(self.openFile)
self.form.listLayer.currentItemChanged.connect(self.onLayerSelect)
def openFile(self):
''' '''
"getOpenFileName(parent: typing.Union[PySide2.QtWidgets.QWidget, NoneType] = None," \
"caption: str = ''," \
"dir: str = ''," \
"filter: str = ''," \
"options: PySide2.QtWidgets.QFileDialog.Options = Default(QFileDialog.Options)) -> typing.Tuple[str, str]"
filename, trash = QtGui.QFileDialog().getOpenFileName(None, 'Select File', os.getcwd(), 'Autocad dxf (*.dxf)')
if filename == "":
return
import sys
import ezdxf
try:
self.doc = ezdxf.readfile(filename)
except IOError:
print(f"Not a DXF file or a generic I/O error.")
sys.exit(1)
except ezdxf.DXFStructureError:
print(f"Invalid or corrupted DXF file.")
sys.exit(2)
# iteration
self.form.listLayer.clear()
for layer in self.doc.layers:
self.form.listLayer.addItem(layer.dxf.name)
msp = self.doc.modelspace()
for e in msp:
print(e.dxftype())
#self.form.listObjects.addItem(e.dxftype())
def onLayerSelect(self, item):
''' '''
print(item.text())
self.form.listLayer.clear()
if self.doc:
msp = self.doc.modelspace()
'''
layer = self.doc.layers.get(item.text())
for obj in layer.entities_in_redraw_order(reverse=False):
self.form.listObjects.addItem(obj)
'''
for obj in msp.query('*[layer=="'+item.text()+'"]'):
#self.form.listObjects.addItem(obj)
print(obj)
def accept(self):
''' '''
class CommandImportDXF:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
'Accel': "E, X",
'MenuText': "Importer to DXF",
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Importer choosed layers to dxf")}
def Activated(self):
taskd = _PVPlantImportDXF()
FreeCADGui.Control.showDialog(taskd)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('importDXF', CommandImportDXF())

75
Importer/importDXF.ui Normal file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formPVSyst</class>
<widget class="QDialog" name="formPVSyst">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>471</height>
</rect>
</property>
<property name="windowTitle">
<string>Export to PVSyst</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QPushButton" name="buttonOpen">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QListWidget" name="listLayer"/>
</item>
<item>
<widget class="QListWidget" name="listObjects"/>
</item>
<item>
<widget class="QPushButton" name="pushOpenFile">
<property name="text">
<string>Aceptar</string>
</property>
<property name="icon">
<iconset theme="Accept">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

92
Init.py Normal file
View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
#****************************************************************************
#* *
#* Kicad STEPUP (TM) (3D kicad board and models to STEP) for FreeCAD *
#* 3D exporter for FreeCAD *
#* Kicad STEPUP TOOLS (TM) (3D kicad board and models to STEP) for FreeCAD *
#* Copyright (c) 2015 *
#* Maurice easyw@katamail.com *
#* *
#* Kicad STEPUP (TM) is a TradeMark and cannot be freely useable *
#* *
#FreeCAD.addImportType("Kicad pcb board/mod File Type (*.kicad_pcb *.kicad_mod)","kicadStepUptools")
FreeCAD.addImportType("Industry Foundation Classes (*.ifc)","importIFC")
FreeCAD.addExportType("Industry Foundation Classes (*.ifc)","importIFC")
FreeCAD.addImportType("Wavefront OBJ - Arch module (*.obj)","importOBJ")
FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ")
FreeCAD.addExportType("WebGL file (*.html)","importWebGL")
FreeCAD.addExportType("JavaScript Object Notation (*.json)","importJSON")
FreeCAD.addImportType("Collada (*.dae)","importDAE")
FreeCAD.addExportType("Collada (*.dae)","importDAE")
FreeCAD.addImportType("3D Studio mesh (*.3ds)","import3DS")
FreeCAD.addImportType("SweetHome3D XML export (*.zip)","importSH3D")
import os
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
ICONPATH = os.path.join(os.path.dirname(__file__), "resources")
zone_list = ["Z1", "Z2", "Z3", "Z4", "Z5", "Z6", "Z7", "Z8", "Z9", "Z10", "Z11", "Z12",
"Z13", "Z14", "Z15", "Z16", "Z17", "Z18", "Z19", "Z20", "Z21", "Z22", "Z23", "Z24",
"Z25", "Z26", "Z27", "Z28", "Z29", "Z30", "Z31", "Z32", "Z33", "Z34", "Z35", "Z36",
"Z37", "Z38", "Z39", "Z40", "Z41", "Z42", "Z43", "Z44", "Z45", "Z46", "Z47", "Z48",
"Z49", "Z50", "Z51", "Z52", "Z53", "Z54", "Z55", "Z56", "Z57", "Z58", "Z59", "Z60"]
marker_dict = {
'NONE': -1, 'BACKSLASH_5_5': 4, 'BACKSLASH_7_7': 34,
'BACKSLASH_9_9': 64, 'BAR_5_5': 5, 'BAR_7_7': 35, 'BAR_9_9': 65,
'CAUTION_FILLED_5_5': 28, 'CAUTION_FILLED_7_7': 58, 'CAUTION_FILLED_9_9': 88,
'CAUTION_LINE_5_5': 18, 'CAUTION_LINE_7_7': 48, 'CAUTION_LINE_9_9': 78,
'CIRCLE_FILLED_5_5': 20, 'CIRCLE_FILLED_7_7': 50, 'CIRCLE_FILLED_9_9': 80,
'CIRCLE_LINE_5_5': 10, 'CIRCLE_LINE_7_7': 40, 'CIRCLE_LINE_9_9': 70,
'CROSS_5_5': 0, 'CROSS_7_7': 30, 'CROSS_9_9': 60, 'DIAMOND_FILLED_5_5': 22,
'DIAMOND_FILLED_7_7': 52, 'DIAMOND_FILLED_9_9': 82, 'DIAMOND_LINE_5_5': 12,
'DIAMOND_LINE_7_7': 42, 'DIAMOND_LINE_9_9': 72, 'EXTENSION': 512,
'FIRST_INSTANCE': 0, 'HOURGLASS_FILLED_5_5': 25, 'HOURGLASS_FILLED_7_7': 55,
'HOURGLASS_FILLED_9_9': 85, 'HOURGLASS_LINE_5_5': 15, 'HOURGLASS_LINE_7_7': 45,
'HOURGLASS_LINE_9_9': 75, 'LIGHTNING_5_5': 8, 'LIGHTNING_7_7': 38,
'LIGHTNING_9_9': 68, 'LINES': 7, 'LINE_STRIP': 8, 'MINUS_5_5': 2,
'MINUS_7_7': 32, 'MINUS_9_9': 62, 'NUM_MARKERS': 90, 'OTHER_INSTANCE': 2,
'PINE_TREE_FILLED_5_5': 27, 'PINE_TREE_FILLED_7_7': 57, 'PINE_TREE_FILLED_9_9': 87,
'PINE_TREE_LINE_5_5': 17, 'PINE_TREE_LINE_7_7': 47, 'PINE_TREE_LINE_9_9': 77,
'PLUS_5_5': 1, 'PLUS_7_7': 31, 'PLUS_9_9': 61, 'POINTS': 6, 'POLYGON': 3,
'PROTO_INSTANCE': 1, 'QUADS': 4, 'QUAD_STRIP': 5, 'RHOMBUS_FILLED_5_5': 24,
'RHOMBUS_FILLED_7_7': 54, 'RHOMBUS_FILLED_9_9': 84, 'RHOMBUS_LINE_5_5': 14,
'RHOMBUS_LINE_7_7': 44, 'RHOMBUS_LINE_9_9': 74, 'SATELLITE_FILLED_5_5': 26,
'SATELLITE_FILLED_7_7': 56, 'SATELLITE_FILLED_9_9': 86, 'SATELLITE_LINE_5_5': 16,
'SATELLITE_LINE_7_7': 46, 'SATELLITE_LINE_9_9': 76, 'SHIP_FILLED_5_5': 29,
'SHIP_FILLED_7_7': 59, 'SHIP_FILLED_9_9': 89, 'SHIP_LINE_5_5': 19,
'SHIP_LINE_7_7': 49, 'SHIP_LINE_9_9': 79, 'SLASH_5_5': 3, 'SLASH_7_7': 33,
'SLASH_9_9': 63, 'SQUARE_FILLED_5_5': 21, 'SQUARE_FILLED_7_7': 51,
'SQUARE_FILLED_9_9': 81, 'SQUARE_LINE_5_5': 11, 'SQUARE_LINE_7_7': 41,
'SQUARE_LINE_9_9': 71, 'STAR_5_5': 6, 'STAR_7_7': 36, 'STAR_9_9': 66,
'TRIANGLES': 2, 'TRIANGLE_FAN': 1, 'TRIANGLE_FILLED_5_5': 23,
'TRIANGLE_FILLED_7_7': 53, 'TRIANGLE_FILLED_9_9': 83, 'TRIANGLE_LINE_5_5': 13,
'TRIANGLE_LINE_7_7': 43, 'TRIANGLE_LINE_9_9': 73, 'TRIANGLE_STRIP': 0,
'VRML1': 1, 'VRML2': 2, 'WELL_5_5': 9, 'WELL_7_7': 39, 'WELL_9_9': 69,
'Y_5_5': 7, 'Y_7_7': 37, 'Y_9_9': 67}
line_patterns = {
"Continues _______________________________": 0xFFFF,
"Border __ . __ __ . __ __ . __ __ . __": 0x3CF2,
"Border (.5x) __.__.__.__.__.__.__.__.__.__._": 0x3939,
"Border (2x) ____ ____ . ____ ____ . _": 0xFDFA,
"Center ____ _ ____ _ ____ _ ____ _ ___": 0xFF3C,
"Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ _": 0xFC78,
"Center (2x) ________ __ ________ __ ___": 0xFFDE,
"Dash dot __ . __ . __ . __ . __ . __ . _": 0xE4E4,
"Dash dot (.5x) _._._._._._._._._._._._._._._._": 0xEBAE,
"Dash dot (2x) ____ . ____ . ____ . ____": 0xFF08,
"Dashed __ __ __ __ __ __ __ __ __ __ _": 0x739C,
"Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _": 0xDB6E,
"Dashed (2x) ____ ____ ____ ____ ____ _": 0xFFE0,
"Divide ____ . . ____ . . ____ . . ____": 0xFF24,
"Divide (.5x) __..__..__..__..__..__..__..__.": 0xEAEA,
"Divide (2x) ________ . . ________ . . ": 0xFFEA,
"Dot . . . . . . . . . . . . . . . .": 0x4924,
"Dot (.5x) ...............................": 0x5555,
"Dot (2x) . . . . . . . . . . .": 0x8888}

247
InitGui.py Normal file
View File

@@ -0,0 +1,247 @@
# /**********************************************************************
# * *
# * 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 *
# * *
# ***********************************************************************
__title__="FreeCAD Fotovoltaic Power Plant Toolkit"
__author__ = "Javier Braña"
__url__ = "sn"
class PVPlantWorkbench (Workbench):
import os
from PVPlantResources import DirIcons as DirIcons
MenuText = "PVPlant"
ToolTip = "Workbench for PV design"
Icon = str(os.path.join(DirIcons, "icon.svg"))
def Initialize(self):
import sys
sys.path.append(r"C:\Users\javie\AppData\Roaming\FreeCAD\Mod")
# Mias
import PVPlantGeoreferencing, PVPlantPlacement, \
PVPlantTerrainAnalisys, PVPlantSite, PVPlantImportGrid, PVPlantFence,\
PVPlantFoundation, PVPlantCreateTerrainMesh, \
PVPlantTreeGenerator, PVPlantBuilding, PVPlantTrench, PVPlantEarthWorks, \
PVPlantStringing, \
PVPlantPad, PVPlantRoad, PVPlantTerrain, PVPlantManhole, \
GraphProfile, Utils.PVPlantTrace,\
reload
import PVPlantRackChecking
from Project.Area import PVPlantArea, PVPlantAreaUtils
from Project import ProjectSetup
from Export import exportPVSyst, PVPlantBOQMechanical, PVPlantBOQElectrical, PVPlantBOQCivil,\
exportDXF
from Importer import importDXF
from Mechanical.Frame import PVPlantFrame
from Electrical.Cable import PVPlantCable, PVPlantElectricalLine
from Electrical.CombinerBox import PVPlantStringBox
from Electrical.Inverter import PVPlantInverter
# A list of command names created in the line above
self.projectlist = ["Reload",
"PVPlantSite",
"PVPlantGeoreferencing",
"ProjectSetup",
#"ImportGrid",
"Terrain",
"PointsGroup",
"PVPlantCreateTerrainMesh",
"PVPlantAreas",
"SplitArea",
"TerrainAnalisys",
"Trenches",
"PVPlantEarthworks",
"PVPlantPad",
"PVPlantRoad",
"PVPlantManhole",
#"PVPlantFoundation"
"GraphTerrainProfile",
"Trace",
]
self.framelist = [
"RackType",
"PVPlantRackCheck",
"Separator",
"PVPlantPlacement",
"PVPlantAdjustToTerrain",
"PVPlantConvertTo",
"PVArea"
]
self.objectlist = [
"PVPlantTree",
"PVPlantBuilding",
"PVPlantFenceGroup",
]
self.inportExportlist = ["BOQCivil",
"BOQMechanical",
"BOQElectrical",
"Separator",
"exportDXF",
#"importDXF",
"ExportToPVSyst",
]
self.electricalList = ["PVPlantStringBox",
"PVPlantCable",
"PVPlanElectricalLine",
"Conduit",
"Stringing",
"Separator",
"StringInverter",
]
self.roads = ["PVPlantRoad",
]
self.pads = ["PVPlantPad",
"Separator"
]
# Toolbar
self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands
self.appendToolbar("PVPlant", self.framelist) # creates a new toolbar with your commands
self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands
self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
# Menu
self.appendMenu("&Civil", self.projectlist) # creates a new menu
self.appendMenu("&PVPlant", self.framelist) # creates a new menu
self.appendMenu("&Shadow", self.objectlist) # creates a new menu
self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu
self.appendMenu("&Electrical", self.electricalList) # creates a new menu
# Draft tools
from DraftTools import translate
self.drafttools = ["Draft_Line","Draft_Wire","Draft_Circle","Draft_Arc","Draft_Ellipse",
"Draft_Polygon","Draft_Rectangle", "Draft_Text",
"Draft_Dimension", "Draft_BSpline","Draft_Point",
"Draft_Facebinder","Draft_BezCurve","Draft_Label"]
self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset",
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
"Draft_Clone"]
self.draftextratools = ["Draft_WireToBSpline","Draft_ShapeString",
"Draft_PathArray","Draft_Mirror","Draft_Stretch"]
self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup","Draft_AutoGroup",
"Draft_SelectGroup","Draft_SelectPlane",
"Draft_ShowSnapBar","Draft_ToggleGrid",]
self.draftutils = ["Draft_Heal","Draft_FlipDimension",
"Draft_ToggleConstructionMode","Draft_ToggleContinueMode","Draft_Edit",
"Draft_Slope","Draft_AddConstruction"]
self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular',
'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel',
'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center',
'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho','Draft_Snap_Special',
'Draft_Snap_Dimensions','Draft_Snap_WorkingPlane']
def QT_TRANSLATE_NOOP(scope, text): return text
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft tools"), self.drafttools)
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft mod tools"), self.draftmodtools)
self.appendMenu(QT_TRANSLATE_NOOP("arch", "&Draft"), self.drafttools + self.draftmodtools + self.draftextratools)
self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Utilities")], self.draftutils + self.draftcontexttools)
self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Snapping")], self.snapList)
import Part
self.measureTools = ["Part_Measure_Linear",
"Part_Measure_Angular",
"Separator",
"Part_Measure_Refresh",
"Part_Measure_Clear_All",
"Part_Measure_Toggle_All",
"Part_Measure_Toggle_3D",
"Part_Measure_Toggle_Delta"
]
self.appendToolbar("Medir", self.measureTools)
self.appendMenu("&Medir", self.measureTools)
self.observer = None
from widgets import CountSelection
def Activated(self):
"This function is executed when the workbench is activated"
import SelectionObserver
import FreeCADGui
self.observer = SelectionObserver.SelObserver()
FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
return
def Deactivated(self):
"This function is executed when the workbench is deactivated"
FreeCADGui.Selection.removeObserver(self.observer)
return
def ContextMenu(self, recipient):
"This is executed whenever the user right-clicks on screen"
# "recipient" will be either "view" or "tree"
#if FreeCAD.activeDraftCommand is None:
if recipient.lower() == "view":
print("Menus en la 'View'")
#if FreeCAD.activeDraftCommand is None:
presel = FreeCADGui.Selection.getPreselection()
print(presel.SubElementNames, " - ", presel.PickedPoints)
if not presel is None:
if presel.Object.Proxy.Type == "Road":
self.appendContextMenu("Road", self.roads)
elif presel.Object.Proxy.Type == "Pad":
self.appendContextMenu("Pad", self.pads)
'''
self.contextMenu = QtGui.QMenu()
menu_item_remove_selected = self.contextMenu.addAction("Remove selected geometry")
menu_item_remove_all = self.contextMenu.addAction("Clear list")
if not self.references:
menu_item_remove_selected.setDisabled(True)
menu_item_remove_all.setDisabled(True)
self.connect(
menu_item_remove_selected,
QtCore.SIGNAL("triggered()"),
self.remove_selected_reference
)
self.connect(
menu_item_remove_all,
QtCore.SIGNAL("triggered()"),
self.remove_all_references
)
parentPosition = self.list_References.mapToGlobal(QtCore.QPoint(0, 0))
self.contextMenu.move(parentPosition + QPos)
self.contextMenu.show()
'''
def GetClassName(self):
# this function is mandatory if this is a full python workbench
return "Gui::PythonWorkbench"
Gui.addWorkbench(PVPlantWorkbench())

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,877 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>1032</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Módulos:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Altura (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1.990000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.960000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.030000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Potencia (wp)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editModulePower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>150</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>350</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Estructura</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Columnas (un)</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="editFrontHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.800000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Orientación del módulo</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="editVerticalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editRows">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboFrameType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Fija</string>
</property>
</item>
<item>
<property name="text">
<string>Tracker 1 Eje</string>
</property>
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="editLeftOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Offset borde derecha (m)</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ángulo de inclinación (º)</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="editRightOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="editTilt">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="editInclination">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filas (un)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Distancia al suelo en el frente (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editCols">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelVerticalGap">
<property name="text">
<string>Separación vertical entre módulos (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboModuleOrientation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Landscape</string>
</property>
</item>
<item>
<property name="text">
<string>Portrait</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Separación horizontal entre módulos (m)</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Ängulo máximo de inclinación longitudinal (ª)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="editHorizontalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tipo de estructura</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Offset borde izquierda (m)</string>
</property>
</widget>
</item>
<item row="16" column="0" colspan="2">
<widget class="QWidget" name="widgetTracker" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>5</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="labelVerticalGap_2">
<property name="text">
<string>Separación entre uniones (m)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Separación Motor (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="editInternalGapNumber">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Número de uniones</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editInternalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editMotorGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Resultado</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Total de módulos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editTotalModules">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Potencia total (wp)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTotalPower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Longitud (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editTotalLength">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editTotalWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,31 @@
def get_boundary(mesh): # From trails
""" Create triangulation boundary """
import Part
import itertools as itools
from collections import Counter
from ast import literal_eval
facet_pidx = mesh.Topology[1]
edges = itools.chain(*(itools.permutations(pidx, 2) for pidx in facet_pidx))
count = Counter((str(edge) for edge in edges))
double_boundary = list((literal_eval(k) for k, v in count.items() if v == 1))
boundary = double_boundary[:1]
for candidate in double_boundary[1:]:
if candidate in boundary or candidate[::-1] in boundary:
pass
else:
boundary.append(candidate)
def mkEdge(p1, p2):
return Part.makeLine((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
points = mesh.Points
edges = []
for p1, p2 in boundary:
edges.append(mkEdge(points[p1], points[p2]))
wires = []
for opening in Part.sortEdges(edges):
wires.append(Part.Wire(opening))
return Part.makeCompound(wires)

View File

@@ -0,0 +1,92 @@
import FreeCAD
import math
def Triangulate(points, MaxlengthLE = 8000, MaxAngleLE = math.pi, use3d=True):
import numpy as np
from scipy.spatial import Delaunay
from stl import mesh as stlmesh
import Mesh
if points.__class__ is list:
points = np.array(points)
tri = Delaunay(points[:, :2])
faces = tri.simplices
wireframe = stlmesh.Mesh(np.zeros(faces.shape[0], dtype=stlmesh.Mesh.dtype))
for i, f in enumerate(faces):
if MaxLength(points[f[0]], points[f[1]], points[f[2]], MaxlengthLE) and \
MaxAngle(points[f[0]], points[f[1]], points[f[2]], MaxAngleLE):
for j in range(3):
wireframe.vectors[i][j] = points[f[j], :]
if not use3d:
wireframe.vectors[i][j][2] = 0
MeshObject = Mesh.Mesh(wireframe.vectors.tolist())
if len(MeshObject.Facets) == 0:
return None
MeshObject.harmonizeNormals()
if MeshObject.Facets[0].Normal.z < 0:
MeshObject.flipNormals()
return MeshObject
def MaxLength(P1, P2, P3, MaxlengthLE):
""" Calculation of the 2D length between triangle edges """
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2], [p2, p3], [p3, p1]]
for i, j in List:
vec = i.sub(j)
if vec.Length > MaxlengthLE:
return False
return True
def MaxAngle(P1, P2, P3, MaxAngleLE):
""" Calculation of the 2D angle between triangle edges """
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2, p3], [p2, p3, p1], [p3, p1, p2]]
for j, k, l in List:
vec1 = j.sub(k)
vec2 = l.sub(k)
radian = vec1.getAngle(vec2)
if radian > MaxAngleLE:
return False
return True
# prueba para ver si es mejor:
def Open3DTriangle(point_cloud):
import numpy as np
import open3d as o3d
'''
input_path = "your_path_to_file/"
output_path = "your_path_to_output_folder/"
dataname = "sample.xyz"
point_cloud = np.loadtxt(input_path + dataname, skiprows=1)
'''
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(point_cloud)
pcd.normals = o3d.utility.Vector3dVector(np.zeros((1, 3)))
pcd.estimate_normals()
pcd.orient_normals_consistent_tangent_plane(100)
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd,
depth=8,
width=0,
scale=1.1,
linear_fit=False,
n_threads=8)
o3d.visualization.draw_geometries([mesh])
#bbox = pcd.get_axis_aligned_bounding_box()
#p_mesh_crop = mesh.crop(bbox)
return mesh

554
PVPLantPlacement-old.py Normal file
View File

@@ -0,0 +1,554 @@
import ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import threading
def makePlacement(baseobj=None, diameter=0, length=0, placement=None, name="Placement"):
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
_PVPlantPlacement(obj)
if FreeCAD.GuiUp:
_ViewProviderPVPlantPlacement(obj.ViewObject)
if baseobj:
baseobj.ViewObject.hide()
return obj
class _CommandPVPlantPlacement:
"the Arch Schedule command definition"
def GetResources(self):
return {'Pixmap': 'Placement',
'Accel': "P, S",
'MenuText': QT_TRANSLATE_NOOP("Placement", "Placement"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Crear un campo fotovoltaico")}
def Activated(self):
taskd = _PVPlantPlacementTaskPanel()
FreeCADGui.Control.showDialog(taskd)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class _PVPlantPlacement(ArchComponent.Component):
"the PVPlantPlacement object"
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
# Does a IfcType exist?
# obj.IfcType = "Fence"
obj.MoveWithHost = False
def setProperties(self, obj):
ArchComponent.Component.setProperties(self, obj)
pl = obj.PropertiesList
if not "Section" in pl:
obj.addProperty("App::PropertyLink", "Land", "Placement", QT_TRANSLATE_NOOP(
"App::Property", "A single section of the fence"))
if not "Post" in pl:
obj.addProperty("App::PropertyLink", "Structure", "Placement", QT_TRANSLATE_NOOP(
"App::Property", "A single fence post"))
if not "Path" in pl:
obj.addProperty("App::PropertyLink", "Path", "Placement", QT_TRANSLATE_NOOP(
"App::Property", "The Path the fence should follow"))
if not "NumberOfSections" in pl:
obj.addProperty("App::PropertyInteger", "NumberOfSections", "Count", QT_TRANSLATE_NOOP(
"App::Property", "The number of sections the fence is built of"))
obj.setEditorMode("NumberOfSections", 1)
if not "NumberOfPosts" in pl:
obj.addProperty("App::PropertyInteger", "NumberOfPosts", "Count", QT_TRANSLATE_NOOP(
"App::Property", "The number of posts used to build the fence"))
obj.setEditorMode("NumberOfPosts", 1)
self.Type = "Fence"
def execute(self, obj):
# fills columns A, B and C of the spreadsheet
if not obj.Description:
return
def __getstate__(self):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
class _ViewProviderPVPlantPlacement:
"A View Provider for PVPlantPlacement"
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/Arch_Schedule.svg"
def attach(self, vobj):
self.Object = vobj.Object
def setEdit(self, vobj, mode):
# taskd = _ArchScheduleTaskPanel(vobj.Object)
# FreeCADGui.Control.showDialog(taskd)
return True
def doubleClicked(self, vobj):
# taskd = _ArchScheduleTaskPanel(vobj.Object)
# FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode):
# FreeCADGui.Control.closeDialog()
return
def claimChildren(self):
# if hasattr(self,"Object"):
# return [self.Object.Result]
return None
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def getDisplayModes(self, vobj):
return ["Default"]
def getDefaultDisplayMode(self):
return "Default"
def setDisplayMode(self, mode):
return mode
class _PVPlantPlacementTaskPanel:
'''The editmode TaskPanel for Schedules'''
def __init__(self, obj=None):
self.Terrain = None
self.Rack = None
self.Gap = 200
self.Pitch = 4500
# form:
self.form = QtGui.QWidget()
self.form.resize(800, 640)
self.form.setWindowTitle("Curvas de nivel")
self.form.setWindowIcon(QtGui.QIcon(":/icons/Arch_Schedule.svg"))
self.grid = QtGui.QGridLayout(self.form)
# parameters
self.labelTerrain = QtGui.QLabel()
self.labelTerrain.setText("Terreno:")
self.lineTerrain = QtGui.QLineEdit(self.form)
self.lineTerrain.setObjectName(_fromUtf8("lineTerrain"))
self.lineTerrain.readOnly = True
self.grid.addWidget(self.labelTerrain, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.lineTerrain, self.grid.rowCount() - 1, 1, 1, 1)
self.buttonAddTerrain = QtGui.QPushButton('Sel')
self.grid.addWidget(self.buttonAddTerrain, self.grid.rowCount() - 1, 2, 1, 1)
self.labelRack = QtGui.QLabel()
self.labelRack.setText("Rack:")
self.lineRack = QtGui.QLineEdit(self.form)
self.lineRack.setObjectName(_fromUtf8("lineRack"))
self.lineRack.readOnly = True
self.grid.addWidget(self.labelRack, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.lineRack, self.grid.rowCount() - 1, 1, 1, 1)
self.buttonAddRack = QtGui.QPushButton('Sel')
self.grid.addWidget(self.buttonAddRack, self.grid.rowCount() - 1, 2, 1, 1)
self.line1 = QtGui.QFrame()
self.line1.setFrameShape(QtGui.QFrame.HLine)
self.line1.setFrameShadow(QtGui.QFrame.Sunken)
self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1)
self.labelTypeStructure = QtGui.QLabel()
self.labelTypeStructure.setText("Tipo de estructura:")
self.valueTypeStructure = QtGui.QComboBox()
self.valueTypeStructure.addItems(["Fixed", "Tracker 1 Axis"])
self.valueTypeStructure.setCurrentIndex(0)
self.grid.addWidget(self.labelTypeStructure, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueTypeStructure, self.grid.rowCount() - 1, 1, 1, -1)
self.labelOrientation = QtGui.QLabel()
self.labelOrientation.setText("Orientacion:")
self.valueOrientation = QtGui.QComboBox()
self.valueOrientation.addItems(["Norte-Sur", "Este-Oeste"])
self.valueOrientation.setCurrentIndex(0)
self.grid.addWidget(self.labelOrientation, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueOrientation, self.grid.rowCount() - 1, 1, 1, -1)
self.labelGap = QtGui.QLabel()
self.labelGap.setText("Espacio entre Columnas:")
self.valueGap = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueGap.setText(str(self.Gap) + " mm")
self.grid.addWidget(self.labelGap, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueGap, self.grid.rowCount() - 1, 1, 1, -1)
self.labelPitch = QtGui.QLabel()
self.labelPitch.setText("Separacion entre Filas:")
self.valuePitch = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valuePitch.setText(str(self.Pitch) + " mm")
self.grid.addWidget(self.labelPitch, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valuePitch, self.grid.rowCount() - 1, 1, 1, -1)
self.labelAlign = QtGui.QLabel()
self.labelAlign.setText("Método de alineación:")
self.valueAlign = QtGui.QComboBox()
self.valueAlign.addItems(["Si", "No"])
self.valueAlign.setCurrentIndex(0)
self.grid.addWidget(self.labelAlign, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueAlign, self.grid.rowCount() - 1, 1, 1, -1)
self.line2 = QtGui.QFrame()
self.line2.setFrameShape(QtGui.QFrame.HLine)
self.line2.setFrameShadow(QtGui.QFrame.Sunken)
self.grid.addWidget(self.line2, self.grid.rowCount(), 0, 1, -1)
self.labelSideSlope = QtGui.QLabel()
self.labelSideSlope.setText("Maxima inclinacion longitudinal:")
self.valueSideSlope = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueSideSlope.setText("15")
self.grid.addWidget(self.labelSideSlope, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueSideSlope, self.grid.rowCount() - 1, 1, 1, -1)
QtCore.QObject.connect(self.buttonAddTerrain, QtCore.SIGNAL("clicked()"), self.addTerrain)
QtCore.QObject.connect(self.buttonAddRack, QtCore.SIGNAL("clicked()"), self.addRack)
# QtCore.QObject.connect(self.form.buttonDel, QtCore.SIGNAL("clicked()"), self.remove)
# QtCore.QObject.connect(self.form.buttonClear, QtCore.SIGNAL("clicked()"), self.clear)
# QtCore.QObject.connect(self.form.buttonSelect, QtCore.SIGNAL("clicked()"), self.select)
def addTerrain(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.Terrain = sel[0]
self.lineTerrain.setText(self.Terrain.Label)
def addRack(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.Rack = sel[0]
self.lineRack.setText(self.Rack.Label)
def accept(self):
if self.Terrain is not None and self.Rack is not None:
self.Gap = FreeCAD.Units.Quantity(self.valueGap.text()).Value
self.Pitch = FreeCAD.Units.Quantity(self.valuePitch.text()).Value
self.placement()
return True
def placement(self):
if self.valueTypeStructure.currentIndex() == 0: # Fixed
print("Rack")
else:
print("Tracker")
if self.Rack.Height < self.Rack.Length:
print("rotar")
aux = self.Rack.Length
self.Rack.Length = self.Rack.Height
self.Rack.Height = aux
self.Rack.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin
self.Rack.Placement.Base.y = self.Terrain.Shape.BoundBox.YMin
DistColls = self.Rack.Length.Value + self.Gap
DistRows = self.Rack.Height.Value + self.Pitch
area = self.Rack.Shape.Faces[0].Area # * 0.999999999
import Draft
rec = Draft.makeRectangle(length=self.Terrain.Shape.BoundBox.XLength, height=self.Rack.Height, face=True,
support=None)
rec.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin
rec.Placement.Base.y = self.Terrain.Shape.BoundBox.YMin
try:
while rec.Shape.BoundBox.YMax <= self.Terrain.Shape.BoundBox.YMax:
common = self.Terrain.Shape.common(rec.Shape)
for shape in common.Faces:
if shape.Area >= area:
if False:
minorPoint = FreeCAD.Vector(0, 0, 0)
for spoint in shape.OuterWire.Vertexes:
if minorPoint.y >= spoint.Point.y:
if minorPoint.x >= spoint.x:
minorPoint = spoint
self.Rack.Placement.Base = spoint
else:
# más rápido
self.Rack.Placement.Base.x = shape.BoundBox.XMin
self.Rack.Placement.Base.y = shape.BoundBox.YMin
while self.Rack.Shape.BoundBox.XMax <= shape.BoundBox.XMax:
verts = [v.Point for v in rackClone.Shape.OuterWire.OrderedVertexes]
inside = True
for vert in verts:
if not shape.isInside(vert, 0, True):
inside = False
break
if inside:
raise
else:
# ajuste fino hasta encontrar el primer sitio:
rackClone.Placement.Base.x += 100 # un metro
'''old version
common1 = shape.common(self.Rack.Shape)
if common1.Area >= area:
raise
else:
# ajuste fino hasta encontrar el primer sitio:
self.Rack.Placement.Base.x += 500 # un metro
del common1
'''
# ajuste fino hasta encontrar el primer sitio:
rec.Placement.Base.y += 100
del common
except:
pass
#print("Found")
FreeCAD.ActiveDocument.removeObject(rec.Name)
from datetime import datetime
starttime = datetime.now()
if self.valueOrientation.currentIndex() == 0:
# Código para crear filas:
self.Rack.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin
i = 1
yy = self.Rack.Placement.Base.y
while yy < self.Terrain.Shape.BoundBox.YMax:
CreateRow1(self.Rack.Placement.Base.x, yy, self.Rack, self.Terrain, DistColls, area, i)
i += 1
yy += DistRows
elif self.valueOrientation.currentIndex() == 2:
# Código para crear columnas:
while self.Rack.Placement.Base.x > self.Terrain.Shape.BoundBox.XMin:
self.Rack.Placement.Base.x -= DistColls
else:
xx = self.Rack.Placement.Base.x
while xx < self.Terrain.Shape.BoundBox.XMax:
CreateGrid(xx, self.Rack.Placement.Base.y, self.Rack, self.Terrain, DistRows, area)
xx += DistColls
FreeCAD.activeDocument().recompute()
print("Everything OK (", datetime.now() - starttime, ")")
# Alinear solo filas. las columnas donde se pueda
def CreateRow(XX, YY, rack, land, gap, area, rowNumber):
import Draft
rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None)
rackClone.Label = 'rackClone{a}'.format(a=rowNumber)
rackClone.Placement.Base.x = XX
rackClone.Placement.Base.y = YY
rec = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height, face=True, support=None)
rec.Placement.Base.x = land.Shape.BoundBox.XMin
rec.Placement.Base.y = YY
FreeCAD.activeDocument().recompute()
common = land.Shape.common(rec.Shape)
for shape in common.Faces:
if shape.Area >= area:
rackClone.Placement.Base.x = shape.BoundBox.XMin
rackClone.Placement.Base.y = shape.BoundBox.YMin
while rackClone.Shape.BoundBox.XMax <= shape.BoundBox.XMax:
common1 = shape.common(rackClone.Shape)
if common1.Area >= area:
tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, placement=rackClone.Placement,
face=True, support=None)
tmp.Label = 'R{:03}-000'.format(rowNumber)
rackClone.Placement.Base.x += gap
else:
# ajuste fino hasta encontrar el primer sitio:
rackClone.Placement.Base.x += 500 # un metro
del common1
del common
FreeCAD.ActiveDocument.removeObject(rackClone.Name)
FreeCAD.ActiveDocument.removeObject(rec.Name)
# Alinear solo filas. las columnas donde se pueda
def CreateRow1(XX, YY, rack, land, gap, area, rowNumber):
import Draft
rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None)
rackClone.Label = 'rackClone{a}'.format(a=rowNumber)
rackClone.Placement.Base.x = XX
rackClone.Placement.Base.y = YY
rec = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height, face=True, support=None)
rec.Placement.Base.x = land.Shape.BoundBox.XMin
rec.Placement.Base.y = YY
FreeCAD.activeDocument().recompute()
common = land.Shape.common(rec.Shape)
for shape in common.Faces:
if shape.Area >= area:
if False:
minorPoint = FreeCAD.Vector(0, 0, 0)
for spoint in shape.OuterWire.Vertexes:
if minorPoint.y >= spoint.Point.y:
if minorPoint.x >= spoint.x:
minorPoint = spoint
rackClone.Placement.Base = spoint
else:
# más rápido
rackClone.Placement.Base.x = shape.BoundBox.XMin
rackClone.Placement.Base.y = shape.BoundBox.YMin
while rackClone.Shape.BoundBox.XMax <= shape.BoundBox.XMax:
verts = [v.Point for v in rackClone.Shape.OuterWire.OrderedVertexes]
inside = True
for vert in verts:
if not shape.isInside(vert, 0, True):
inside = False
break
if inside:
#tmp = rack.Shape.copy()
#tmp.Placement = rack.Placement
tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, placement=rackClone.Placement,
face=True, support=None)
tmp.Label = 'R{:03}-000'.format(rowNumber)
rackClone.Placement.Base.x += gap
else:
# ajuste fino hasta encontrar el primer sitio:
rackClone.Placement.Base.x += 500 # un metro
del common
FreeCAD.ActiveDocument.removeObject(rackClone.Name)
FreeCAD.ActiveDocument.removeObject(rec.Name)
# Alinear columna y fila (grid perfecta)
def CreateGrid(XX, YY, rack, land, gap, area):
print("CreateGrid")
import Draft
rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None)
rackClone.Label = 'rackClone{a}'.format(a=XX)
rackClone.Placement.Base.x = XX
rackClone.Placement.Base.y = YY
# if False:
while rackClone.Shape.BoundBox.YMax < land.Shape.BoundBox.YMax:
common = land.Shape.common(rackClone.Shape)
if common.Area >= area:
tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height,
placement=rackClone.Placement, face=True, support=None)
tmp.Label = 'rackClone{a}'.format(a=XX)
rackClone.Placement.Base.y += gap
# else:
# # ajuste fino hasta encontrar el primer sitio:
# rackClone.Placement.Base.y += 1000
FreeCAD.ActiveDocument.removeObject(rackClone.Name)
# Alinear solo filas. las columnas donde se pueda
def CreateCol(XX, YY, rack, land, gap, area):
import Draft
rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None)
rackClone.Label = 'rackClone{a}'.format(a=XX)
rackClone.Placement.Base.x = XX
rackClone.Placement.Base.y = YY
while rackClone.Shape.BoundBox.YMax < land.Shape.BoundBox.YMax:
common = land.Shape.common(rackClone.Shape)
if common.Area >= area:
tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height,
placement=rackClone.Placement, face=True, support=None)
tmp.Label = 'rackClone{a}'.format(a=XX)
rackClone.Placement.Base.y += gap
else:
# ajuste fino hasta encontrar el primer sitio:
rackClone.Placement.Base.y += 100
FreeCAD.ActiveDocument.removeObject(rackClone.Name)
# TODO: Probar a usar hilos:
class _CreateCol(threading.Thread):
def __init__(self, args=()):
super().__init__()
self.XX = args[0]
self.YY = args[1]
self.rack = args[2]
self.land = args[3]
self.gap = args[4]
self.area = args[5]
def run(self):
import Draft
# rackClone = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height,
# face=True, support=None)
# rackClone = FreeCAD.activeDocument().addObject('Part::Feature')
# rackClone.Shape = self.rack.Shape
rackClone = Draft.makeRectangle(length=self.rack.Length, height=self.rack.Height, face=True, support=None)
rackClone.Label = 'rackClone{a}'.format(a=self.XX)
rackClone.Placement.Base.x = self.XX
rackClone.Placement.Base.y = self.YY
# if False:
while rackClone.Shape.BoundBox.YMax < self.land.Shape.BoundBox.YMax:
common = self.land.Shape.common(rackClone.Shape)
if common.Area >= self.area:
rack = Draft.makeRectangle(length=self.rack.Length, height=self.rack.Height,
placement=rackClone.Placement, face=True, support=None)
rack.Label = 'rackClone{a}'.format(a=self.XX)
rackClone.Placement.Base.y += self.gap
# else:
# # ajuste fino hasta encontrar el primer sitio:
# rackClone.Placement.Base.y += 1000
# FreeCAD.ActiveDocument.removeObject(rackClone.Name)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantPlacement', _CommandPVPlantPlacement())

345
PVPlantBuilding.py Normal file
View File

@@ -0,0 +1,345 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
import draftguitools.gui_trackers as DraftTrackers
import draftguitools.gui_tool_utils as gui_tool_utils
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makeBuilding(name="Building"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Building")
obj.Label = name
_Building(obj)
_ViewProviderBuilding(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
return obj
class _Building(ArchComponent.Component):
"A Building Obcject"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.rooftype = ""
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
# Dimensions: --------------------------------------------------------------------------------------------------
if not "RoofWall" in pl:
obj.addProperty("App::PropertyLength",
"RoofWall",
"Building",
"The height of this object"
).RoofWall = 0
if not "RoofWallWidth" in pl:
obj.addProperty("App::PropertyLength",
"RoofWallWidth",
"Building",
"The height of this object"
).RoofWallWidth = 300
if not "Height" in pl:
obj.addProperty("App::PropertyLength",
"Height",
"Building",
"The height of this object"
).Height = 4000
if not "Width" in pl:
obj.addProperty("App::PropertyLength",
"Width",
"Building",
"The width of this object"
).Width = 7000
if not "Length" in pl:
obj.addProperty("App::PropertyLength",
"Length",
"Building",
"The height of this object"
).Length = 14000
if not "RoofHeight" in pl:
obj.addProperty("App::PropertyLength",
"RoofHeight",
"Building",
"The height of this object"
).RoofHeight = 1500
if not "TopCenter" in pl:
obj.addProperty("App::PropertyPercent",
"TopCenter",
"Building",
"The height of this object"
).TopCenter = 50
if not "TopLength" in pl:
obj.addProperty("App::PropertyPercent",
"TopLength",
"Building",
"The height of this object"
).TopLength = 0
# outputs:
if not "InternalVolume" in pl:
obj.addProperty("App::PropertyVolume",
"InternalVolume",
"Outputs",
"The height of this object"
)
if not "ExternalVolume" in pl:
obj.addProperty("App::PropertyVolume",
"ExternalVolume",
"Outputs",
"The height of this object"
)
self.Type = "Building"
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and Arch wall properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
def execute(self, obj):
w = obj.Width.Value
w_med = w / 2
l_med = obj.Length.Value / 2
p1 = FreeCAD.Vector(-l_med, -w_med, 0)
p2 = FreeCAD.Vector(-l_med, w_med, 0)
p3 = FreeCAD.Vector(-l_med, w_med, obj.Height.Value)
p4 = FreeCAD.Vector(-l_med, w * (obj.TopCenter - 50) / 100, obj.Height.Value + obj.RoofHeight.Value)
p5 = FreeCAD.Vector(-l_med, -w_med, obj.Height.Value)
profile = Part.Face(Part.makePolygon([p1, p2, p3, p4, p5, p1, ]))
shape = profile.extrude(FreeCAD.Vector(obj.Length.Value, 0, 0))
if obj.TopLength > 0:
p1 = FreeCAD.Vector(-l_med, w_med, obj.Height.Value)
p2 = FreeCAD.Vector(-l_med, w * (obj.TopCenter - 50) / 100, obj.Height.Value + obj.RoofHeight.Value)
p3 = FreeCAD.Vector(-l_med, -w_med, obj.Height.Value)
p4 = FreeCAD.Vector(-l_med + (obj.TopLength * obj.Length.Value / 200),
w * (obj.TopCenter - 50) / 100, obj.Height.Value + obj.RoofHeight.Value)
f1 = Part.Face(Part.makePolygon([p1, p2, p3, p1, ]))
f2 = Part.Face(Part.makePolygon([p1, p4, p3, p1, ]))
f3 = Part.Face(Part.makePolygon([p1, p2, p4, p1, ]))
f4 = Part.Face(Part.makePolygon([p3, p4, p2, p3, ]))
tool = Part.makeSolid(Part.Shell([f1, f4, f2, f3]))
shape = shape.cut(tool)
p1 = FreeCAD.Vector(l_med, w_med, obj.Height.Value)
p2 = FreeCAD.Vector(l_med,
w * (obj.TopCenter - 50) / 100,
obj.Height.Value + obj.RoofHeight.Value)
p3 = FreeCAD.Vector(l_med, -w_med, obj.Height.Value)
p4 = FreeCAD.Vector(l_med - (obj.TopLength * obj.Length.Value / 200),
w * (obj.TopCenter - 50) / 100,
obj.Height.Value + obj.RoofHeight.Value)
f1 = Part.Face(Part.makePolygon([p1, p2, p3, p1, ]))
f2 = Part.Face(Part.makePolygon([p1, p4, p3, p1, ]))
f3 = Part.Face(Part.makePolygon([p1, p2, p4, p1, ]))
f4 = Part.Face(Part.makePolygon([p3, p4, p2, p3, ]))
tool = Part.makeSolid(Part.Shell([f1, f4, f2, f3]))
shape = shape.cut(tool)
if obj.RoofWall.Value > 0 and obj.RoofWallWidth.Value > 0:
offset = 2 * obj.RoofWallWidth.Value
box1 = Part.makeBox(obj.Length.Value, obj.Width.Value, obj.RoofWall.Value)
box2 = Part.makeBox(obj.Length.Value - offset, obj.Width.Value - offset, obj.RoofWall.Value)
box2.Placement.Base.x += obj.RoofWallWidth.Value
box2.Placement.Base.y += obj.RoofWallWidth.Value
box1 = box1.cut(box2)
box1.Placement.Base.x = -l_med
box1.Placement.Base.y = -w_med
box1.Placement.Base.z = obj.Height.Value
shape = shape.fuse(box1).removeSplitter()
obj.Shape = shape
class _ViewProviderBuilding(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(DirIcons, "house.svg"))
def setEdit(self, vobj, mode):
"""Method called when the document requests the object to enter edit mode.
Edit mode is entered when a user double clicks on an object in the tree
view, or when they use the menu option [Edit -> Toggle Edit Mode].
Just display the standard Arch component task panel.
Parameters
----------
mode: int or str
The edit mode the document has requested. Set to 0 when requested via
a double click or [Edit -> Toggle Edit Mode].
Returns
-------
bool
If edit mode was entered.
"""
if (mode == 0) and hasattr(self, "Object"):
taskd = _BuildingTaskPanel(self.Object)
taskd.obj = self.Object
# taskd.update()
FreeCADGui.Control.showDialog(taskd)
return True
return False
class _BuildingTaskPanel:
def __init__(self, obj=None):
self.new = False
if obj is None:
self.new = True
obj = makeBuilding()
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantBuilding.ui")
self.node = None
self.view = FreeCADGui.ActiveDocument.ActiveView
self.tracker = DraftTrackers.ghostTracker(obj)
self.tracker.on()
self.call = self.view.addEventCallback("SoEvent", self.action)
def action(self, arg):
"""Handle the 3D scene events.
This is installed as an EventCallback in the Inventor view.
Parameters
----------
arg: dict
Dictionary with strings that indicates the type of event received
from the 3D view.
"""
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
self.finish()
elif arg["Type"] == "SoLocation2Event":
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
if info:
self.tracker.move(FreeCAD.Vector(info["x"], info["y"], info["z"]))
else:
self.tracker.move(point)
elif (arg["Type"] == "SoMouseButtonEvent" and
arg["State"] == "DOWN" and
arg["Button"] == "BUTTON1"):
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
if info:
self.obj.Placement.Base = FreeCAD.Vector(info["x"], info["y"], info["z"])
else:
self.obj.Placement.Base = point
self.finish()
def finish(self):
self.accept()
def accept(self):
self.closeForm()
return True
def reject(self):
if self.new:
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
self.closeForm()
return True
def closeForm(self):
self.tracker.finalize()
FreeCADGui.Control.closeDialog()
self.view.removeEventCallback("SoEvent", self.call)
class _CommandBuilding:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "house.svg")),
'MenuText': "Building",
'Accel': "C, M",
'ToolTip': "Creates a Building object from setup dialog."}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
if FreeCAD.ActiveDocument is not None:
if FreeCADGui.Selection.getCompleteSelection():
for ob in FreeCAD.ActiveDocument.Objects:
if ob.Name[:4] == "Site":
return True
def Activated(self):
TaskPanel = _BuildingTaskPanel()
FreeCADGui.Control.showDialog(TaskPanel)
return
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantBuilding', _CommandBuilding())

218
PVPlantBuilding.ui Normal file
View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Dimensions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Heigth (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>2000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

172
PVPlantCCTV.py Normal file
View File

@@ -0,0 +1,172 @@
import FreeCAD
import ArchComponent
import PVPlantSite
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Frames"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import os
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makePole(diameter=48, length=3000, placement=None, name="Post"):
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
Poles(obj)
if FreeCAD.GuiUp:
ViewProviderPost(obj.ViewObject)
if placement:
obj.Placement = placement
return obj
class Poles(ArchComponent.Component):
"A Base Frame Obcject - Class"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setCommonProperties(obj)
# Does a IfcType exist?
obj.IfcType = "Structural Item"
obj.setEditorMode("IfcType", 1)
self.totalAreaShape = None
self.changed = True
def setCommonProperties(self, obj):
# Definicion de Propiedades:
ArchComponent.Component.setProperties(self, obj)
pl = obj.PropertiesList
if not "TopDiameter" in pl:
obj.addProperty("App::PropertyLength",
"TopDiameter",
"Post",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).TopDiameter = 40
if not "BottomDiameter" in pl:
obj.addProperty("App::PropertyLength",
"BottomDiameter",
"Post",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).BottomDiameter = 60
if not "Height" in pl:
obj.addProperty("App::PropertyLength",
"Height",
"Post",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).Height = 6000
if not "BaseWidth" in pl:
obj.addProperty("App::PropertyLength",
"BaseWidth",
"Post",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).BaseWidth = 300
if not "BaseHeight" in pl:
obj.addProperty("App::PropertyLength",
"BaseHeight",
"Post",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).BaseHeight = 6
self.Type = "Post"
def onChanged(self, obj, prop):
''''''
def execute(self, obj):
pl = obj.Placement
base = Part.makeBox(obj.BaseWidth, obj.BaseWidth, obj.BaseHeight)
base1 = Part.show(Part.makeSphere(45, FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 30, 90, 360))
tube = Part.makeCone(obj.BottonDiameter / 2, obj.TopDiameter / 2, obj.Height)
obj.Shape = base.fuse([base1, tube])
obj.Placement = pl
class ViewProviderPost(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
import Arch_rc
return ":/icons/Arch_Pipe_Tree.svg"
class CommandMultiRowTracker:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "solar-tracker.svg")),
'MenuText': "Multi-row Tracker",
'Accel': "R, M",
'ToolTip': "Creates a multi-row Tracker object from trackers."}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
if FreeCAD.ActiveDocument is not None:
if FreeCADGui.Selection.getCompleteSelection():
for ob in FreeCAD.ActiveDocument.Objects:
if ob.Name[:4] == "Site":
return True
def Activated(self):
self.TaskPanel = _FixedRackTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return
if FreeCAD.GuiUp:
class CommandRackGroup:
def GetCommands(self):
return tuple(['PVPlantFixedRack',
'PVPlantTracker'
])
def GetResources(self):
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('PVPlantCreatePost', CommandFixedRack())

271
PVPlantCreateTerrainMesh.py Normal file
View File

@@ -0,0 +1,271 @@
# /**********************************************************************
# * *
# * 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 FreeCAD, FreeCADGui, Draft
from PySide import QtCore, QtGui, QtSvg
from PySide.QtCore import QT_TRANSLATE_NOOP
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os, math
from PVPlantResources import DirIcons as DirIcons
class _TaskPanel:
def __init__(self, obj = None):
self.obj = None
self.select = 0
self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/PVPlantCreateTerrainMesh.ui")
self.form.buttonAdd.clicked.connect(self.add)
def add(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.obj = sel[0]
self.form.editCloud.setText(self.obj.Label)
def MaxLength(self, P1, P2, P3, MaxlengthLE):
"""
Calculation of the 2D length between triangle edges
"""
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2], [p2, p3], [p3, p1]]
for i, j in List:
vec = i.sub(j)
if vec.Length > MaxlengthLE:
return False
return True
def MaxAngle(self, P1, P2, P3, MaxAngleLE):
"""
Calculation of the 2D angle between triangle edges
"""
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2, p3], [p2, p3, p1], [p3, p1, p2]]
for j, k, l in List:
vec1 = j.sub(k)
vec2 = l.sub(k)
radian = vec1.getAngle(vec2)
if radian > MaxAngleLE:
return False
return True
def accept(self):
from datetime import datetime
starttime = datetime.now()
import Part
import numpy as np
from scipy.spatial import Delaunay
bnd = FreeCAD.ActiveDocument.Site.Terrain.CuttingBoundary.Shape
if len(bnd.Faces) == 0:
bnd = Part.Face(bnd)
# Get user input
MaxlengthLE = int(self.form.MaxlengthLE.text()) * 1000
MaxAngleLE = math.radians(int(self.form.MaxAngleLE.text()))
firstPoint = self.obj.Points.Points[0]
nbase = FreeCAD.Vector(firstPoint.x, firstPoint.y, firstPoint.z)
data = []
for point in self.obj.Points.Points:
tmp = FreeCAD.Vector(0, 0, 0).add(point)
tmp.z = 0
if bnd.isInside(tmp, 0, True):
p = point - nbase
data.append([float(p.x), float(p.y), float(p.z)])
Data = np.array(data)
data.clear()
''' not working:
import multiprocessing
rows = Data.shape[0]
cpus = multiprocessing.cpu_count()
steps = math.ceil(rows / cpus)
for cpu in range(cpus - 1):
start = steps * cpu
end = steps * (cpu + 1)
if end > rows:
end = rows - start
tmp = Data[start : end, :]
p = multiprocessing.Process(target = Triangulate, args = (tmp,))
p.start()
p.join()
return
'''
# TODO: si es muy grande, dividir el cálculo de la maya en varias etapas
# Create delaunay triangulation
tri = Delaunay(Data[:, :2])
print("tiempo delaunay:", datetime.now() - starttime)
faces = tri.simplices
from stl import mesh
wireframe = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
if self.MaxLength(Data[f[0]], Data[f[1]], Data[f[2]], MaxlengthLE) and \
self.MaxAngle(Data[f[0]], Data[f[1]], Data[f[2]], MaxAngleLE):
for j in range(3):
wireframe.vectors[i][j] = Data[f[j], :]
import Mesh
MeshObject = Mesh.Mesh(wireframe.vectors.tolist())
MeshObject.Placement.move(nbase)
MeshObject.harmonizeNormals()
Surface = FreeCAD.ActiveDocument.addObject("Mesh::Feature", self.form.SurfaceNameLE.text())
Surface.Mesh = MeshObject
Surface.Label = self.form.SurfaceNameLE.text()
shape = MeshToShape(MeshObject)
import Part
Part.show(shape)
FreeCAD.ActiveDocument.recompute()
FreeCADGui.Control.closeDialog()
print(" --- Tiempo tardado:", datetime.now() - starttime)
def reject(self):
FreeCADGui.Control.closeDialog()
#----------------------------------------------------------------------------------------------
#from PySide.QtWidgets import QVBoxLayout, QLabel, QPushButton, QWidget, QMainWindow, QApplication
def Triangulate(Points, MaxlengthLE = 8000, MaxAngleLE = math.pi, use3d=True):
import numpy as np
from scipy.spatial import Delaunay
from stl import mesh as stlmesh
import Mesh
if Points.__class__ is list:
Points = np.array(Points)
tri = Delaunay(Points[:, :2])
faces = tri.simplices
wireframe = stlmesh.Mesh(np.zeros(faces.shape[0], dtype=stlmesh.Mesh.dtype))
for i, f in enumerate(faces):
if MaxLength(Points[f[0]], Points[f[1]], Points[f[2]], MaxlengthLE) and \
MaxAngle(Points[f[0]], Points[f[1]], Points[f[2]], MaxAngleLE):
for j in range(3):
wireframe.vectors[i][j] = Points[f[j], :]
if not use3d:
wireframe.vectors[i][j][2] = 0
MeshObject = Mesh.Mesh(wireframe.vectors.tolist())
if len(MeshObject.Facets) == 0:
return None
MeshObject.harmonizeNormals()
if MeshObject.Facets[0].Normal.z < 0:
MeshObject.flipNormals()
return MeshObject
def MaxLength(P1, P2, P3, MaxlengthLE):
""" Calculation of the 2D length between triangle edges """
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2], [p2, p3], [p3, p1]]
for i, j in List:
vec = i.sub(j)
if vec.Length > MaxlengthLE:
return False
return True
def MaxAngle(P1, P2, P3, MaxAngleLE):
""" Calculation of the 2D angle between triangle edges """
p1 = FreeCAD.Vector(P1[0], P1[1], 0)
p2 = FreeCAD.Vector(P2[0], P2[1], 0)
p3 = FreeCAD.Vector(P3[0], P3[1], 0)
List = [[p1, p2, p3], [p2, p3, p1], [p3, p1, p2]]
for j, k, l in List:
vec1 = j.sub(k)
vec2 = l.sub(k)
radian = vec1.getAngle(vec2)
if radian > MaxAngleLE:
return False
return True
def Open3DTriangle(point_cloud):
import numpy as np
import open3d as o3d
'''
input_path = "your_path_to_file/"
output_path = "your_path_to_output_folder/"
dataname = "sample.xyz"
point_cloud = np.loadtxt(input_path + dataname, skiprows=1)
'''
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(point_cloud)
pcd.normals = o3d.utility.Vector3dVector(np.zeros((1, 3)))
pcd.estimate_normals()
pcd.orient_normals_consistent_tangent_plane(100)
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd,
depth=8,
width=0,
scale=1.1,
linear_fit=False,
n_threads=8)
o3d.visualization.draw_geometries([mesh])
#bbox = pcd.get_axis_aligned_bounding_box()
#p_mesh_crop = mesh.crop(bbox)
return mesh
class _PVPlantCreateTerrainMesh:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "surface.svg")),
'MenuText': QT_TRANSLATE_NOOP("PVPlant", "Create Surface"),
'Accel': "C, S",
'ToolTip': QT_TRANSLATE_NOOP("PVPlant", "Creates a surface form a cloud of points.")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _TaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantCreateTerrainMesh', _PVPlantCreateTerrainMesh())

167
PVPlantCreateTerrainMesh.ui Normal file
View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateSurface</class>
<widget class="QDialog" name="CreateSurface">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>396</width>
<height>331</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Surface</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="SurfaceNameL">
<property name="text">
<string>Surface Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="SurfaceNameLE">
<property name="text">
<string>Surface</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="2">
<widget class="QLineEdit" name="editBoundary"/>
</item>
<item row="0" column="0" rowspan="2" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Boundary</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="buttonBoundary">
<property name="text">
<string>add</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="SelectPointGroupL">
<property name="text">
<string>Select Point Groups</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Nube de puntos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editCloud"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="buttonAdd">
<property name="text">
<string>sel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="MaxlengthL">
<property name="text">
<string>Maximum Triangle length:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="MaxlengthLE">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>5</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="OtherValueL">
<property name="text">
<string>Maximum Triangle Angle:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="MaxAngleLE">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>90</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

967
PVPlantEarthWorks.py Normal file
View File

@@ -0,0 +1,967 @@
import math
import FreeCAD
import Part
import ArchComponent
from pivy import coin
import numpy as np
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
voltype = ["Fill", "Cut"]
def makeEarthWorksVolume(vtype = 0):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", voltype[vtype])
EarthWorksVolume(obj)
ViewProviderEarthWorksVolume(obj.ViewObject)
return obj
class EarthWorksVolume(ArchComponent.Component):
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setProperties(obj)
def setProperties(self, obj):
# Definicion de Propiedades:
pl = obj.PropertiesList
if not ("VolumeType" in pl):
obj.addProperty("App::PropertyEnumeration",
"VolumeType",
"Volume",
"Connection").VolumeType = voltype
if not ("SurfaceSlope" in pl):
obj.addProperty("App::PropertyPercent",
"SurfaceSlope",
"Volume",
"Connection").SurfaceSlope = 2
if not ("VolumeMesh" in pl):
obj.addProperty("Mesh::PropertyMeshKernel",
"VolumeMesh",
"Volume",
"Volume")
obj.setEditorMode("VolumeMesh", 2)
if not ("Volume" in pl):
obj.addProperty("App::PropertyVolume",
"Volume",
"Volume",
"Volume")
obj.setEditorMode("Volume", 1)
obj.Proxy = self
obj.IfcType = "Civil Element"
obj.setEditorMode("IfcType", 1)
obj.Proxy = self
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onChange(self, obj, prop):
if prop == "VolumeMesh":
if obj.VolumeMesh:
obj.VolumeMesh = obj.VolumeMesh.Volume
def execute(self, obj):
''' '''
pass
class ViewProviderEarthWorksVolume:
"A View Provider for the Pipe object"
def __init__(self, vobj):
''' Set view properties. '''
pl = vobj.PropertiesList
(r, g, b) = (1.0, 0.0, 0.0) if vobj.Object.VolumeType == "Cut" else (0.0, 0.0, 1.0)
# Triangulation properties.
if not "Transparency" in pl:
vobj.addProperty("App::PropertyIntegerConstraint",
"Transparency", "Surface Style",
"Set triangle face transparency")
vobj.Transparency = (50, 0, 100, 1)
if not "ShapeColor" in pl:
vobj.addProperty("App::PropertyColor",
"ShapeColor",
"Surface Style",
"Set triangle face color")
vobj.ShapeColor = (r, g, b, vobj.Transparency / 100)
if not "ShapeMaterial" in pl:
vobj.addProperty("App::PropertyMaterial",
"ShapeMaterial", "Surface Style",
"Triangle face material")
vobj.ShapeMaterial = FreeCAD.Material()
if not "LineTransparency" in pl:
vobj.addProperty("App::PropertyIntegerConstraint",
"LineTransparency", "Surface Style",
"Set triangle edge transparency")
vobj.LineTransparency = (50, 0, 100, 1)
if not "LineColor" in pl:
vobj.addProperty("App::PropertyColor",
"LineColor", "Surface Style",
"Set triangle face color")
vobj.LineColor = (0.5, 0.5, 0.5, vobj.LineTransparency / 100)
'''vobj.addProperty(
"App::PropertyMaterial", "LineMaterial", "Surface Style",
"Triangle face material").LineMaterial = FreeCAD.Material()
vobj.addProperty(
"App::PropertyFloatConstraint", "LineWidth", "Surface Style",
"Set triangle edge line width").LineWidth = (0.0, 1.0, 20.0, 1.0)
# Boundary properties.
vobj.addProperty(
"App::PropertyColor", "BoundaryColor", "Boundary Style",
"Set boundary contour color").BoundaryColor = (0.0, 0.75, 1.0, 0.0)
vobj.addProperty(
"App::PropertyFloatConstraint", "BoundaryWidth", "Boundary Style",
"Set boundary contour line width").BoundaryWidth = (3.0, 1.0, 20.0, 1.0)
vobj.addProperty(
"App::PropertyEnumeration", "BoundaryPattern", "Boundary Style",
"Set a line pattern for boundary").BoundaryPattern = [*line_patterns]
vobj.addProperty(
"App::PropertyIntegerConstraint", "PatternScale", "Boundary Style",
"Scale the line pattern").PatternScale = (3, 1, 20, 1)
# Contour properties.
vobj.addProperty(
"App::PropertyColor", "MajorColor", "Contour Style",
"Set major contour color").MajorColor = (1.0, 0.0, 0.0, 0.0)
vobj.addProperty(
"App::PropertyFloatConstraint", "MajorWidth", "Contour Style",
"Set major contour line width").MajorWidth = (4.0, 1.0, 20.0, 1.0)
vobj.addProperty(
"App::PropertyColor", "MinorColor", "Contour Style",
"Set minor contour color").MinorColor = (1.0, 1.0, 0.0, 0.0)
vobj.addProperty(
"App::PropertyFloatConstraint", "MinorWidth", "Contour Style",
"Set major contour line width").MinorWidth = (2.0, 1.0, 20.0, 1.0)
'''
vobj.Proxy = self
vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor
def onChanged(self, vobj, prop):
'''
Update Object visuals when a view property changed.
'''
if prop == "ShapeColor" or prop == "Transparency":
if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"):
color = vobj.getPropertyByName("ShapeColor")
transparency = vobj.getPropertyByName("Transparency")
color = (color[0], color[1], color[2], transparency / 100)
vobj.ShapeMaterial.DiffuseColor = color
if prop == "ShapeMaterial":
if hasattr(vobj, "ShapeMaterial"):
material = vobj.getPropertyByName("ShapeMaterial")
self.face_material.diffuseColor.setValue(material.DiffuseColor[:3])
self.face_material.transparency = material.DiffuseColor[3]
if prop == "LineColor" or prop == "LineTransparency":
if hasattr(vobj, "LineColor") and hasattr(vobj, "LineTransparency"):
color = vobj.getPropertyByName("LineColor")
transparency = vobj.getPropertyByName("LineTransparency")
color = (color[0], color[1], color[2], transparency / 100)
vobj.LineMaterial.DiffuseColor = color
if prop == "LineMaterial":
material = vobj.getPropertyByName(prop)
self.edge_material.diffuseColor.setValue(material.DiffuseColor[:3])
self.edge_material.transparency = material.DiffuseColor[3]
if prop == "LineWidth":
width = vobj.getPropertyByName(prop)
self.edge_style.lineWidth = width
if prop == "BoundaryColor":
color = vobj.getPropertyByName(prop)
self.boundary_color.rgb = color[:3]
if prop == "BoundaryWidth":
width = vobj.getPropertyByName(prop)
self.boundary_style.lineWidth = width
if prop == "BoundaryPattern":
if hasattr(vobj, "BoundaryPattern"):
pattern = vobj.getPropertyByName(prop)
self.boundary_style.linePattern = line_patterns[pattern]
if prop == "PatternScale":
if hasattr(vobj, "PatternScale"):
scale = vobj.getPropertyByName(prop)
self.boundary_style.linePatternScaleFactor = scale
if prop == "MajorColor":
color = vobj.getPropertyByName(prop)
self.major_color.rgb = color[:3]
if prop == "MajorWidth":
width = vobj.getPropertyByName(prop)
self.major_style.lineWidth = width
if prop == "MinorColor":
color = vobj.getPropertyByName(prop)
self.minor_color.rgb = color[:3]
if prop == "MinorWidth":
width = vobj.getPropertyByName(prop)
self.minor_style.lineWidth = width
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
# GeoCoords Node.
self.geo_coords = coin.SoGeoCoordinate()
# Surface features.
self.triangles = coin.SoIndexedFaceSet()
self.face_material = coin.SoMaterial()
self.edge_material = coin.SoMaterial()
self.edge_color = coin.SoBaseColor()
self.edge_style = coin.SoDrawStyle()
self.edge_style.style = coin.SoDrawStyle.LINES
shape_hints = coin.SoShapeHints()
shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
mat_binding = coin.SoMaterialBinding()
mat_binding.value = coin.SoMaterialBinding.PER_FACE
offset = coin.SoPolygonOffset()
offset.styles = coin.SoPolygonOffset.LINES
offset.factor = -2.0
# Boundary features.
self.boundary_color = coin.SoBaseColor()
self.boundary_coords = coin.SoGeoCoordinate()
self.boundary_lines = coin.SoLineSet()
self.boundary_style = coin.SoDrawStyle()
self.boundary_style.style = coin.SoDrawStyle.LINES
# Boundary root.
boundaries = coin.SoType.fromName('SoFCSelection').createInstance()
boundaries.style = 'EMISSIVE_DIFFUSE'
boundaries.addChild(self.boundary_color)
boundaries.addChild(self.boundary_style)
boundaries.addChild(self.boundary_coords)
boundaries.addChild(self.boundary_lines)
# Major Contour features.
self.major_color = coin.SoBaseColor()
self.major_coords = coin.SoGeoCoordinate()
self.major_lines = coin.SoLineSet()
self.major_style = coin.SoDrawStyle()
self.major_style.style = coin.SoDrawStyle.LINES
# Major Contour root.
major_contours = coin.SoSeparator()
major_contours.addChild(self.major_color)
major_contours.addChild(self.major_style)
major_contours.addChild(self.major_coords)
major_contours.addChild(self.major_lines)
# Minor Contour features.
self.minor_color = coin.SoBaseColor()
self.minor_coords = coin.SoGeoCoordinate()
self.minor_lines = coin.SoLineSet()
self.minor_style = coin.SoDrawStyle()
self.minor_style.style = coin.SoDrawStyle.LINES
# Minor Contour root.
minor_contours = coin.SoSeparator()
minor_contours.addChild(self.minor_color)
minor_contours.addChild(self.minor_style)
minor_contours.addChild(self.minor_coords)
minor_contours.addChild(self.minor_lines)
# Highlight for selection.
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
highlight.style = 'EMISSIVE_DIFFUSE'
highlight.addChild(shape_hints)
highlight.addChild(mat_binding)
highlight.addChild(self.geo_coords)
highlight.addChild(self.triangles)
highlight.addChild(boundaries)
# Face root.
face = coin.SoSeparator()
face.addChild(self.face_material)
face.addChild(highlight)
# Edge root.
edge = coin.SoSeparator()
edge.addChild(self.edge_material)
edge.addChild(self.edge_style)
edge.addChild(highlight)
# Surface root.
surface_root = coin.SoSeparator()
surface_root.addChild(face)
surface_root.addChild(offset)
surface_root.addChild(edge)
surface_root.addChild(major_contours)
surface_root.addChild(minor_contours)
vobj.addDisplayMode(surface_root, "Surface")
# Boundary root.
boundary_root = coin.SoSeparator()
boundary_root.addChild(boundaries)
vobj.addDisplayMode(boundary_root, "Boundary")
# Elevation/Shaded root.
shaded_root = coin.SoSeparator()
shaded_root.addChild(face)
vobj.addDisplayMode(shaded_root, "Elevation")
vobj.addDisplayMode(shaded_root, "Slope")
vobj.addDisplayMode(shaded_root, "Shaded")
# Flat Lines root.
flatlines_root = coin.SoSeparator()
flatlines_root.addChild(face)
flatlines_root.addChild(offset)
flatlines_root.addChild(edge)
vobj.addDisplayMode(flatlines_root, "Flat Lines")
# Wireframe root.
wireframe_root = coin.SoSeparator()
wireframe_root.addChild(edge)
wireframe_root.addChild(major_contours)
wireframe_root.addChild(minor_contours)
vobj.addDisplayMode(wireframe_root, "Wireframe")
# Take features from properties.
self.onChanged(vobj, "ShapeColor")
self.onChanged(vobj, "LineColor")
self.onChanged(vobj, "LineWidth")
'''self.onChanged(vobj, "BoundaryColor")
self.onChanged(vobj, "BoundaryWidth")
self.onChanged(vobj, "BoundaryPattern")
self.onChanged(vobj, "PatternScale")
self.onChanged(vobj, "MajorColor")
self.onChanged(vobj, "MajorWidth")
self.onChanged(vobj, "MinorColor")
self.onChanged(vobj, "MinorWidth")'''
def updateData(self, obj, prop):
'''
Update Object visuals when a data property changed.
'''
# Set System.
geo_system = ["UTM", FreeCAD.ActiveDocument.Site.UtmZone, "FLAT"]
self.geo_coords.geoSystem.setValues(geo_system)
self.boundary_coords.geoSystem.setValues(geo_system)
self.major_coords.geoSystem.setValues(geo_system)
self.minor_coords.geoSystem.setValues(geo_system)
if prop == "VolumeMesh":
mesh = obj.VolumeMesh
copy_mesh = mesh.copy()
#copy_mesh.Placement.move(origin.Origin)
triangles = []
for i in copy_mesh.Topology[1]:
triangles.extend(list(i))
triangles.append(-1)
self.geo_coords.point.values = copy_mesh.Topology[0]
self.triangles.coordIndex.values = triangles
del copy_mesh
'''if prop == "ContourShapes":
contour_shape = obj.getPropertyByName(prop)
if contour_shape.SubShapes:
major_shape = contour_shape.SubShapes[0]
points, vertices = self.wire_view(major_shape, origin.Origin)
self.major_coords.point.values = points
self.major_lines.numVertices.values = vertices
minor_shape = contour_shape.SubShapes[1]
points, vertices = self.wire_view(minor_shape, origin.Origin)
self.minor_coords.point.values = points
self.minor_lines.numVertices.values = vertices
if prop == "BoundaryShapes":
boundary_shape = obj.getPropertyByName(prop)
points, vertices = self.wire_view(boundary_shape, origin.Origin, True)
self.boundary_coords.point.values = points
self.boundary_lines.numVertices.values = vertices
if prop == "AnalysisType" or prop == "Ranges":
analysis_type = obj.getPropertyByName("AnalysisType")
ranges = obj.getPropertyByName("Ranges")
if analysis_type == "Default":
if hasattr(obj.ViewObject, "ShapeMaterial"):
material = obj.ViewObject.ShapeMaterial
self.face_material.diffuseColor = material.DiffuseColor[:3]
if analysis_type == "Elevation":
colorlist = self.elevation_analysis(obj.Mesh, ranges)
self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist)
elif analysis_type == "Slope":
colorlist = self.slope_analysis(obj.Mesh, ranges)
self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist)
'''
def getIcon(self):
""" Return the path to the appropriate icon. """
return str(os.path.join(DirIcons, "solar-fixed.svg"))
def getDisplayModes(self, vobj):
'''
Return a list of display modes.
'''
modes = ["Surface", "Boundary", "Flat Lines", "Shaded", "Wireframe"]
return modes
def getDefaultDisplayMode(self):
'''
Return the name of the default display mode.
'''
return "Surface"
def setDisplayMode(self, mode):
'''
Map the display mode defined in attach with
those defined in getDisplayModes.
'''
return mode
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self, state):
"""
Get variables from file.
"""
return None
class _EarthWorksTaskPanel:
def __init__(self):
self.To = None
# self.form:
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantEarthworks.ui"))
self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))
def accept(self):
from datetime import datetime
starttime = datetime.now()
import MeshPart as mp
land = FreeCAD.ActiveDocument.Terrain.Mesh.copy()
frames = []
for obj in FreeCADGui.Selection.getSelection():
if hasattr(obj, "Proxy"):
if obj.Proxy.Type == "Tracker":
if not (obj in frames):
frames.append(obj)
elif obj.Proxy.Type == "FrameArea":
for fr in obj.Frames:
if not (fr in frames):
frames.append(fr)
if len(frames) == 0:
return False
FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras")
def calculateEarthWorks(line, extreme=False):
pts = []
pts1 = []
line1 = line.copy()
angles = line.Placement.Rotation.toEulerAngles("XYZ")
line1.Placement.Rotation.setEulerAngles("XYZ", 0, 0, angles[2])
line1.Placement.Base.z = 0
pro = mp.projectShapeOnMesh(line1, land, FreeCAD.Vector(0, 0, 1))
flat = []
for points in pro:
flat.extend(points)
pro = Part.makePolygon(flat)
points = pro.discretize(Distance=500)
for point in points:
ver = Part.Vertex(point)
dist = ver.distToShape(line)
linepoint = dist[1][0][1]
if not extreme:
if self.form.groupTolerances.isChecked():
if linepoint.z > point.z:
if linepoint.sub(point).Length > self.form.editToleranceCut.value():
pts.append(linepoint)
elif linepoint.z < point.z:
if linepoint.sub(point).Length > self.form.editToleranceFill.value():
pts1.append(linepoint)
else:
if linepoint.z > point.z:
pts.append(linepoint)
elif linepoint.z < point.z:
pts1.append(linepoint)
#pts.append(linepoint)
else:
if linepoint.z > point.z:
if linepoint.sub(point).Length > 200:
pts.append(linepoint)
return pts, pts1
tools = [[],[]]
ver = 2
if ver == 0:
frames = sorted(frames, key=lambda x: (x.Placement.Base.x, x.Placement.Base.y))
for frame in frames:
length = frame.Setup.Length.Value / 2
p1 = FreeCAD.Vector(-length, 0, 0)
p2 = FreeCAD.Vector(length, 0, 0)
line = Part.LineSegment(p1, p2).toShape()
line.Placement = frame.Placement.copy()
line.Placement.Base.x = frame.Shape.BoundBox.XMin
step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2
for n in range(3):
ret = calculateEarthWorks(line, n % 2)
tools[0].extend(ret[0])
tools[1].extend(ret[1])
line.Placement.Base.x += step
elif ver == 1:
from PVPlantPlacement import getCols
columns = getCols(frames)
'''colelements = set()
rowelements = set()
for groups in columns:
for group in groups:
for frame in group:
colelements.add(frame.Placement.Base.x)
rowelements.add(frame.Placement.Base.y)
colelements = sorted(colelements)
rowelements = sorted(rowelements, reverse=True)
print("Cols: ", len(colelements), " - ", colelements)
print("Rows: ", len(rowelements), " - ", rowelements)
a = []
colnum = len(colelements)
for r in range(len(rowelements)):
a.append([None] * colnum)
mat = np.array(a, dtype=object)
for groups in columns:
for group in groups:
for frame in group:
colidx = colelements.index(frame.Placement.Base.x)
rowidx = rowelements.index(frame.Placement.Base.y)
mat[rowidx][colidx] = frame
print(mat)
return'''
for groups in columns:
for group in groups:
first = group[0]
last = group[-1]
for frame in group:
length = frame.Setup.Length.Value / 2
p1 = FreeCAD.Vector(-(length + (self.form.editOffset.value() if frame == first else -1000)),
0, 0)
p2 = FreeCAD.Vector(length + (self.form.editOffset.value() if frame == last else -1000),
0, 0)
line = Part.LineSegment(p1, p2).toShape()
line.Placement = frame.Placement.copy()
line.Placement.Base.x = frame.Shape.BoundBox.XMin
step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2
for n in range(3):
ret = calculateEarthWorks(line, n % 2 == 1)
tools[0].extend(ret[0])
tools[1].extend(ret[1])
line.Placement.Base.x += step
elif ver == 2:
print("versión 2")
import PVPlantPlacement
rows, columns = PVPlantPlacement.getRows(frames)
if (rows is None) or (columns is None):
print("Nada que procesar")
return False
tools = []
lofts = []
for group in rows:
lines = []
cont = 0
while cont < len(group):
aw = 0
if cont > 0:
p0 = FreeCAD.Vector(group[cont - 1].Placement.Base)
p1 = FreeCAD.Vector(group[cont].Placement.Base)
aw = getAngle(p0, p1)
ae = 0
if cont < (len(group) - 1):
p1 = FreeCAD.Vector(group[cont].Placement.Base)
p2 = FreeCAD.Vector(group[cont + 1].Placement.Base)
ae = getAngle(p1, p2)
lng = int(group[cont].Setup.Length / 2)
wdt = int(group[cont].Setup.Width / 2)
line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0),
FreeCAD.Vector(lng, 0, 0)).toShape()
line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0),
FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape()
anf = (aw + ae) / 2
if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value:
anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value
zz = wdt * math.sin(math.radians(anf))
li = line.copy()
li.Placement = group[cont].Placement
li.Placement.Rotation = group[cont].Placement.Rotation
li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0))
li.Placement.Base.z -= zz
lines.append(li)
ld = line.copy()
ld.Placement = group[cont].Placement
ld.Placement.Rotation = group[cont].Placement.Rotation
ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0))
ld.Placement.Base.z += zz
lines.append(ld)
tools.append([group[cont], li, ld])
cont += 1
loft = Part.makeLoft(lines, False, True, False)
lofts.append(loft)
for group in rows:
lines = []
for frame in group:
col, idx = searchFrameInColumns(frame, columns)
tool = searchTool(frame, tools)
if idx == 0:
''' '''
if idx == (len(col) - 1):
''' '''
if (idx + 1) < len(col):
frame1 = col[idx + 1]
tool1 = searchTool(frame1, tools)
line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape()
lines.append(line)
line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape()
lines.append(line)
if len(lines) > 0:
loft = Part.makeLoft(lines, False, True, False)
lofts.append(loft)
faces = []
for loft in lofts:
faces.extend(loft.Faces)
sh = Part.makeShell(faces)
import Utils.PVPlantUtils as utils
import Mesh
pro = utils.getProjected(sh)
pro = utils.simplifyWire(pro)
#pro = pro.makeOffset2D(20000, 2, False, False, True)
Part.show(sh, "loft")
Part.show(pro, "pro")
pts = [ver.Point for ver in pro.Vertexes]
'''if pts[0] != pts[-1]:
pts.append(pts[0])'''
land.trim(pts, 1)
tmp = []
for face in sh.Faces:
wire = face.Wires[0].copy()
pl = wire.Placement.Base
wire.Placement.Base = wire.Placement.Base - pl
wire = wire.scale(2)
wire.Placement.Base = wire.Placement.Base + pl
#wire = wire.makeOffset2D(10000, 0, False, False, True)
wire.Placement.Base.z = wire.Placement.Base.z - 10000
face1 = Part.makeLoft([face.Wires[0], wire], True, True, False)
Part.show(face1, "tool")
#tmp.append(face.extrude(FreeCAD.Vector(0, 0, -10000)))
#Part.show(tmp[-1], "face-extrude")
sh = sh.extrude(FreeCAD.Vector(0, 0, -10000))
sh = Part.Solid(sh)
Part.show(sh)
import MeshPart as mp
msh = mp.meshFromShape(Shape=sh) # , MaxLength=1)
# msh = msh.smooth("Laplace", 3)
Mesh.show(msh, "tool")
Mesh.show(land, "trim")
'''inner = msh.inner(land)
Mesh.show(inner)
outer = msh.inner(land)
Mesh.show(outer)'''
'''intersec = land.section(msh, MinDist=0.01)
import Draft
for sec in intersec:
Draft.makeWire(sec)'''
FreeCAD.ActiveDocument.commitTransaction()
self.closeForm()
return True
import MeshTools.Triangulation as TriangulateMesh
import MeshTools.MeshGetBoundary as mgb
import Mesh
for ind, points in enumerate(tools):
mesh = TriangulateMesh.Triangulate(points, MaxlengthLE=3000, MaxAngleLE=math.radians(100))
if mesh:
for mesh in mesh.getSeparateComponents():
boundary = mgb.get_boundary(mesh)
Part.show(boundary)
'''if self.form.editOffset.value() != 0:
import Utils.PVPlantUtils as utils
pro = utils.getProjected(boundary)
pro = pro.makeOffset2D(self.form.editOffset.value(), 0, False, False, True)
# TODO: paso intermedio de restar las areas prohibidas
pro = mp.projectShapeOnMesh(pro, land, FreeCAD.Vector(0, 0, 1))
cnt = 0
for lp in pro:
cnt += len(lp)
# points.extend(boundary.Wires[0].discretize(Number=cnt))
points = boundary.Wires[0].discretize(Distance=cnt)
for lp in pro:
points.extend(lp)
mesh1 = TriangulateMesh.Triangulate(points, MaxlengthLE=5000) # , MaxAngleLE=math.pi / 1.334)
import Mesh
Mesh.show(mesh1)
boundary = Part.makeCompound([])
for section in pro:
if len(section) > 0:
try:
boundary.add(Part.makePolygon(section))
except:
pass
Part.show(boundary)'''
#mesh.smooth("Laplace", 3)
#Mesh.show(mesh)
#Part.show(boundary)
vol = makeEarthWorksVolume(ind)
vol.VolumeMesh = mesh.copy()
if ind == 0:
''' put inside fills group '''
else:
''' put inside fills group '''
FreeCAD.ActiveDocument.commitTransaction()
self.closeForm()
return True
def reject(self):
self.closeForm()
return True
def closeForm(self):
FreeCADGui.Control.closeDialog()
def getAngle(vec1, vec2):
dX = vec2.x - vec1.x
dZ = vec2.z - vec1.z
return math.degrees(math.atan2(float(dZ), float(dX)))
def searchFrameInColumns(obj, columns):
for colidx, col in enumerate(columns):
for group in col:
if obj in group:
return group, group.index(obj) #groupidx
def searchTool(obj, tools):
for tool in tools:
if obj in tool:
return tool
class _CommandCalculateEarthworks:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "pico.svg")),
'Accel': "C, E",
'MenuText': QT_TRANSLATE_NOOP("Placement", "Movimiento de tierras"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el movimiento de tierras")}
def Activated(self):
TaskPanel = _EarthWorksTaskPanel()
FreeCADGui.Control.showDialog(TaskPanel)
def IsActive(self):
active = not (FreeCAD.ActiveDocument is None)
if not (FreeCAD.ActiveDocument.getObject("Terrain") is None):
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
return active
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantEarthworks', _CommandCalculateEarthworks())
def accept():
import MeshPart as mp
land = FreeCAD.ActiveDocument.Terrain.Mesh
frames = []
for obj in FreeCADGui.Selection.getSelection():
if hasattr(obj, "Proxy"):
if obj.Proxy.Type == "Tracker":
if not (obj in frames):
frames.append(obj)
elif obj.Proxy.Type == "FrameArea":
for fr in obj.Frames:
if not (fr in frames):
frames.append(fr)
if len(frames) == 0:
return False
FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras")
import PVPlantPlacement
rows, columns = PVPlantPlacement.getRows(frames)
if (rows is None) or (columns is None):
print("Nada que procesar")
return False
tools = []
for group in rows:
lines = []
cont = 0
while cont < len(group):
aw = 0
if cont > 0:
p0 = FreeCAD.Vector(group[cont - 1].Placement.Base)
p1 = FreeCAD.Vector(group[cont].Placement.Base)
aw = getAngle(p0, p1)
ae = 0
if cont < (len(group) - 1):
p1 = FreeCAD.Vector(group[cont].Placement.Base)
p2 = FreeCAD.Vector(group[cont + 1].Placement.Base)
ae = getAngle(p1, p2)
lng = int(group[cont].Setup.Length / 2)
wdt = int(group[cont].Setup.Width / 2)
line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0),
FreeCAD.Vector(lng, 0, 0)).toShape()
line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0),
FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape()
anf = (aw + ae) / 2
if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value:
anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value
zz = wdt * math.sin(math.radians(anf))
li = line.copy()
li.Placement = group[cont].Placement
li.Placement.Rotation = group[cont].Placement.Rotation
li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0))
li.Placement.Base.z -= zz
lines.append(li)
ld = line.copy()
ld.Placement = group[cont].Placement
ld.Placement.Rotation = group[cont].Placement.Rotation
ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0))
ld.Placement.Base.z += zz
lines.append(ld)
tools.append([group[cont], li, ld])
cont += 1
loft = Part.makeLoft(lines, False, True, False)
import MeshPart as mp
msh = mp.meshFromShape(Shape=loft) #, MaxLength=1)
#msh = msh.smooth("Laplace", 3)
import Mesh
Mesh.show(msh)
'''intersec = land.section(msh, MinDist=0.01)
import Draft
for sec in intersec:
Draft.makeWire(sec)'''
for group in rows:
lines = []
for frame in group:
col, idx = searchFrameInColumns(frame, columns)
tool = searchTool(frame, tools)
if idx == 0:
''' '''
if idx == (len(col) - 1):
''' '''
if (idx + 1) < len(col):
frame1 = col[idx + 1]
tool1 = searchTool(frame1, tools)
line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape()
Part.show(line)
lines.append(line)
line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape()
Part.show(line)
lines.append(line)
if len(lines) > 0:
loft = Part.makeLoft(lines, False, True, False)
import MeshPart as mp
msh = mp.meshFromShape(Shape=loft) # , MaxLength=1)
#msh = msh.smooth("Laplace", 3)
import Mesh
Mesh.show(msh)
intersec = land.section(msh, MinDist=0.01)
import Draft
for sec in intersec:
Draft.makeWire(sec)
FreeCAD.ActiveDocument.commitTransaction()
self.closeForm()
return True

180
PVPlantEarthworks.ui Normal file
View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>291</width>
<height>141</height>
</rect>
</property>
<property name="windowTitle">
<string>Calculate Earthworks for frames</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupTolerances">
<property name="title">
<string>Use tolerances</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Tolerancia para el corte:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="editToleranceFill">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>90</width>
<height>16777215</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Tolerancia para el relleno:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editToleranceCut">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>90</width>
<height>16777215</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Distancia offset:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>90</width>
<height>16777215</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>3000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

895
PVPlantFence.py Normal file
View File

@@ -0,0 +1,895 @@
# /**********************************************************************
# * *
# * 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 copy
import math
import ArchComponent
import Draft
import FreeCAD
import Part
import PVPlantFencePost
import PVPlantSite
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import PySide.QtGui as QtGui
from pivy import coin
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
from PVPlantResources import DirIcons as DirIcons
EAST = FreeCAD.Vector(1, 0, 0)
def makeprojection(pathwire):
site = FreeCAD.ActiveDocument.Site
land = site.Terrain.Shape
proj = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))
return proj
def makePVPlantFence(section, post, path):
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence')
_Fence(obj)
obj.Post = post
obj.Base = path
if FreeCAD.GuiUp:
_ViewProviderFence(obj.ViewObject)
hide(section)
hide(post)
hide(path)
FreeCAD.ActiveDocument.recompute()
return obj
def hide(obj):
if hasattr(obj, 'ViewObject') and obj.ViewObject:
obj.ViewObject.Visibility = False
def getAngle(Line1, Line2):
v1 = Line1.Vertexes[1].Point - Line1.Vertexes[0].Point
v2 = Line2.Vertexes[1].Point - Line2.Vertexes[0].Point
return v1.getAngle(v2)
def get_parameter_from_v0(edge, offset):
"""Return parameter at distance offset from edge.Vertexes[0].
sb method in Part.TopoShapeEdge???
"""
import DraftVecUtils
lpt = edge.valueAt(edge.getParameterByLength(0))
vpt = edge.Vertexes[0].Point
if not DraftVecUtils.equals(vpt, lpt):
# this edge is flipped
length = edge.Length - offset
else:
# this edge is right way around
length = offset
return edge.getParameterByLength(length)
def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal=None):
"""Orient shape to tangent at parm offset along edge."""
import functools
import DraftVecUtils
# http://en.wikipedia.org/wiki/Euler_angles
# start with null Placement point so translate goes to right place.
placement = FreeCAD.Placement()
placement.Rotation = globalRotation
placement.move(RefPt + xlate)
if not align:
return placement
# unit +Z Probably defined elsewhere?
z = FreeCAD.Vector(0, 0, 1)
# y = FreeCAD.Vector(0, 1, 0) # unit +Y
x = FreeCAD.Vector(1, 0, 0) # unit +X
nullv = FreeCAD.Vector(0, 0, 0)
# get local coord system - tangent, normal, binormal, if possible
t = edge.tangentAt(get_parameter_from_v0(edge, offset))
t.normalize()
n = normal
b = t.cross(n)
b.normalize()
lnodes = z.cross(b)
try:
# Can't normalize null vector.
lnodes.normalize()
except:
# pathological cases:
pass
print(b, " - ", b.dot(z))
if abs(b.dot(z)) == 1.0: # 2) binormal is || z
# align shape to tangent only
psi = math.degrees(DraftVecUtils.angle(x, t, z))
theta = 0.0
phi = 0.0
FreeCAD.Console.PrintWarning("Draft PathArray.orientShape - Gimbal lock. Infinite lnodes. Change Path or Base.\n")
else: # regular case
psi = math.degrees(DraftVecUtils.angle(x, lnodes, z))
theta = math.degrees(DraftVecUtils.angle(z, b, lnodes))
phi = math.degrees(DraftVecUtils.angle(lnodes, t, b))
rotations = [placement.Rotation]
if psi != 0.0:
rotations.insert(0, FreeCAD.Rotation(z, psi))
if theta != 0.0:
rotations.insert(0, FreeCAD.Rotation(lnodes, theta))
if phi != 0.0:
rotations.insert(0, FreeCAD.Rotation(b, phi))
if len(rotations) == 1:
finalRotation = rotations[0]
else:
finalRotation = functools.reduce(lambda rot1, rot2: rot1.multiply(rot2), rotations)
placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), finalRotation.toEuler()[2])
return placement
def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
""" Calculates the placements of a shape along a given path so that each copy will be distributed evenly
- shapeRotation: rotation offset
- pathwire: path where place the shapes
- count: number of sections
- xlate: transformation offset
"""
import Part
import DraftGeomUtils
closedpath = DraftGeomUtils.isReallyClosed(pathwire)
normal = DraftGeomUtils.getNormal(pathwire)
if normal:
if normal.z < 0: # asegurarse de que siempre se dibuje por encima del suelo
normal.z *= -1
else:
normal = FreeCAD.Vector(0, 0, 1)
path = Part.__sortEdges__(pathwire.Edges)
ends = []
cdist = 0
# -----------------------------------------------------------------------------------------------------------------
totalEdges = len(path)
if not closedpath:
totalEdges -= 1
# -----------------------------------------------------------------------------------------------------------------
for e in path: # find cumulative edge end distance
cdist += e.Length
ends.append(cdist)
placements = []
# place the start shape
pt = path[0].Vertexes[0].Point
placements.append(calculatePlacement(shapeRotation, path[0], 0, pt, xlate, align, normal))
# closed path doesn't need shape on last vertex
if not closedpath:
# place the end shape
pt = path[-1].Vertexes[-1].Point
placements.append(calculatePlacement(shapeRotation, path[-1], path[-1].Length, pt, xlate, align, normal))
if count < 3:
return placements
# place the middle shapes
if closedpath:
stop = count
else:
stop = count - 1
step = float(cdist) / stop
travel = step
for i in range(1, stop):
# which edge in path should contain this shape?
# avoids problems with float math travel > ends[-1]
iend = len(ends) - 1
for j in range(0, len(ends)):
if travel <= ends[j]:
iend = j
break
# place shape at proper spot on proper edge
remains = ends[iend] - travel
offset = path[iend].Length - remains
pt = path[iend].valueAt(get_parameter_from_v0(path[iend], offset))
placements.append(calculatePlacement(shapeRotation, path[iend], offset, pt, xlate, align, normal))
travel += step
return placements
class _Fence(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
self.Posts = []
self.Foundations = []
# Does a IfcType exist?
# obj.IfcType = "Fence"
def setProperties(self, obj):
ArchComponent.Component.setProperties(self, obj)
pl = obj.PropertiesList
if not "Gate" in pl:
obj.addProperty("App::PropertyLinkList",
"Gate",
"Fence",
"A single fence post")
if not "Post" in pl:
obj.addProperty("App::PropertyLink",
"Post",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post"))
if not "Foundation" in pl:
obj.addProperty("App::PropertyLength",
"Foundation",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post"))
if not "Depth" in pl:
obj.addProperty("App::PropertyLength",
"Depth",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Depth = 650
if not "Gap" in pl:
obj.addProperty("App::PropertyLength",
"Gap",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Gap = 4000
if not "Angle" in pl:
obj.addProperty("App::PropertyAngle",
"Angle",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Angle = 60
if not "MeshOffsetZ" in pl:
obj.addProperty("App::PropertyLength",
"MeshOffsetZ",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshOffsetZ = 0
if not "MeshHeight" in pl:
obj.addProperty("App::PropertyLength",
"MeshHeight",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshHeight = 2000
if not "Reinforce" in pl:
obj.addProperty("App::PropertyLength",
"Reinforce",
"Fence",
QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Reinforce = 50000
########## Datos informativos:
if not "NumberOfSections" in pl:
obj.addProperty("App::PropertyQuantity",
"NumberOfSections",
"Output",
QT_TRANSLATE_NOOP("App::Property", "The number of sections the fence is built of"))
obj.setEditorMode("NumberOfSections", 1)
if not "NumberOfPosts" in pl:
obj.addProperty("App::PropertyQuantity",
"NumberOfPosts",
"Output",
QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
obj.setEditorMode("NumberOfPosts", 1)
if not "Length" in pl:
obj.addProperty("App::PropertyLength",
"Length",
"Output",
QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
obj.setEditorMode("Length", 1)
if not "Concrete" in pl:
obj.addProperty("App::PropertyVolume",
"Concrete",
"Output",
"Concrete Volume")
obj.setEditorMode("Concrete", 1)
if not "PlacementList" in pl:
obj.addProperty("App::PropertyPlacementList",
"PlacementList",
"Output",
QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
obj.setEditorMode("Length", 1)
self.Type = "PVPlatFence"
def __getstate__(self):
if hasattr(self, 'sectionFaceNumbers'):
return (self.sectionFaceNumbers)
return None
def __setstate__(self, state):
if state is not None and isinstance(state, tuple):
self.sectionFaceNumbers = state[0]
return None
def execute(self, obj):
pathwire = self.calculatePathWire(obj)
if pathwire is None:
# FreeCAD.Console.PrintLog("ArchFence.execute: path " + obj.Base.Name + " has no edges\n")
return
if not obj.Post:
FreeCAD.Console.PrintLog("ArchFence.execute: Post not set\n")
return
self.Posts = []
self.Foundations = []
site = PVPlantSite.get()
if True: # prueba
import MeshPart as mp
land = FreeCAD.ActiveDocument.Terrain.Mesh
segments = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))
points=[]
for segment in segments:
points.extend(segment)
pathwire = Part.makePolygon(points)
else:
if PVPlantSite.get().Terrain.TypeId == 'Mesh::Feature':
import MeshPart as mp
land = PVPlantSite.get().Terrain.Mesh
pathwire = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))
else:
land = site.Terrain.Shape
pathwire = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))
if pathwire is None:
return
''' no sirve:
if len(pathwire.Wires) > 1:
import draftgeoutils
pathwire = draftgeoutils.wires.superWire(pathwire.Edges, True)
Part.show(pathwire)
'''
''' unir todas en una '''
'''
if len(pathwire.Wires) > 1:
import Utils.PVPlantUtils as utils
wires = pathwire.Wires
new_wire = []
to_compare = utils.getPoints(wires.pop(0))
new_wire.extend(to_compare)
while len(wires)>0:
wire = wires[0]
points = utils.getPoints(wire)
to_remove = None
if points[0] in to_compare:
to_remove = points[0]
if points[-1] in to_compare:
to_remove = points[-1]
if to_remove:
to_compare = points.copy()
points.remove(to_remove)
new_wire.extend(points)
wires.pop()
continue
wires.append(wires.pop())
pathwire = Part.makePolygon(new_wire)
#Part.show(pathwire)
#return
'''
sectionLength = obj.Gap.Value
postLength = 0 #obj.Post.Diameter.Value #considerarlo 0 porque no influye
postPlacements = []
pathsegments = self.calculateSegments(obj, pathwire)
count = 0
drawFirstPost = True
pathLength = 0
for segment in pathsegments:
segwire = Part.Wire(Part.__sortEdges__(segment))
pathLength += segwire.Length
obj.NumberOfSections = self.calculateNumberOfSections(segwire.Length, sectionLength, postLength)
obj.NumberOfPosts = obj.NumberOfSections + 1
count += obj.NumberOfSections
downRotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), -90)
placements = self.calculatePostPlacements(obj, segwire, downRotation)
if drawFirstPost:
drawFirstPost = False
else:
placements.pop(0)
postPlacements.extend(placements)
postShapes, postFoundation = self.calculatePosts(obj, postPlacements)
sections, num = self.calculateSections(obj, postPlacements)
postShapes = Part.makeCompound(postShapes)
postFoundation = Part.makeCompound(postFoundation)
sections = Part.makeCompound(sections)
compound = Part.makeCompound([postShapes, postFoundation, sections])
obj.Shape = compound
# Give information
obj.NumberOfSections = count
obj.NumberOfPosts = obj.NumberOfSections + 1
obj.Length = pathLength
obj.Concrete = count * postFoundation.SubShapes[0].Volume
def calculateSegments(self, obj, pathwire):
''' Calcular los segmentos de la ruta '''
import math
segments = []
segment = [pathwire.Edges[0]]
segments.append(segment)
for ind in range(len(pathwire.Edges) - 1):
ed1 = pathwire.Edges[ind]
ed2 = pathwire.Edges[ind + 1]
vec1 = ed1.Vertexes[1].Point - ed1.Vertexes[0].Point
vec2 = ed2.Vertexes[1].Point - ed2.Vertexes[0].Point
angle = math.degrees(vec1.getAngle(vec2))
if angle > obj.Angle.Value:
segment = []
segments.append(segment)
segment.append(ed2)
return segments
def calculateNumberOfSections(self, pathLength, sectionLength, postLength):
withoutLastPost = pathLength - postLength
realSectionLength = sectionLength + postLength
return math.ceil(withoutLastPost / realSectionLength)
def calculatePostPlacements(self, obj, pathwire, rotation):
postWidth = obj.Post.Diameter.Value
transformationVector = FreeCAD.Vector(0, postWidth / 2, 0)
placements = calculatePlacementsOnPath(rotation, pathwire, int(obj.NumberOfSections) + 1, transformationVector, True)
# The placement of the last object is always the second entry in the list.
# So we move it to the end:
if len(placements) > 1:
placements.append(placements.pop(1))
return placements
def calculatePosts(self, obj, postPlacements):
posts = []
foundations = []
for placement in postPlacements:
postCopy = obj.Post.Shape.copy()
postCopy = Part.Solid(postCopy)
postCopy.Placement = placement
postCopy.Placement.Base.z += 100
posts.append(postCopy)
foundation = Part.makeCylinder(150, 700)
foundation.Placement = placement
foundation.Placement.Base.z -= obj.Depth.Value
foundation = foundation.cut(postCopy)
foundations.append(foundation)
return posts, foundations
def calculateSections(self, obj, postPlacements):
shapes = []
faceNumbers = []
offsetz = obj.MeshOffsetZ.Value
meshHeight = obj.MeshHeight.Value
for i in range(len(postPlacements) - 1):
startPlacement = postPlacements[i]
endPlacement = postPlacements[i + 1]
p1 = startPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
p2 = endPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
p3 = p2 + FreeCAD.Vector(0, 0, meshHeight)
p4 = p1 + FreeCAD.Vector(0, 0, meshHeight)
pointlist = [p1, p2, p3, p4, p1]
try:
pol = Part.makePolygon(pointlist)
face = Part.Face(pol)
shapes.append(face)
faceNumbers.append(1)
except:
print("No es posible crear la cara: ---------------------------------------------------")
print(" +++++ Start: ", startPlacement.Base, " - end: ", endPlacement.Base)
print(" +++++ algo: ", pointlist, "\n")
print("---------------------------------------------------\n")
return (shapes, faceNumbers)
def calculatePathWire(self, obj):
if obj.Base:
wire = None
if hasattr(obj.Base.Shape, 'Wires') and obj.Base.Shape.Wires:
wire = obj.Base.Shape.Wires[0]
elif obj.Base.Shape.Edges:
wire = Part.Wire(obj.Base.Shape.Edges)
return wire
return None
class _ViewProviderFence(ArchComponent.ViewProviderComponent):
"A View Provider for the Fence object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
vobj.Proxy = self
#vobj.addExtension("Gui::ViewProviderGroupExtensionPython")
def getIcon(self):
return str(os.path.join(DirIcons, "fence.svg"))
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
self.Object = vobj.Object
# GeoCoords Node.
self.geo_coords = coin.SoGeoCoordinate()
# Surface features.
self.triangles = coin.SoIndexedFaceSet()
self.face_material = coin.SoMaterial()
self.edge_material = coin.SoMaterial()
self.edge_color = coin.SoBaseColor()
self.edge_style = coin.SoDrawStyle()
self.edge_style.style = coin.SoDrawStyle.LINES
shape_hints = coin.SoShapeHints()
shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
mat_binding = coin.SoMaterialBinding()
mat_binding.value = coin.SoMaterialBinding.PER_FACE
offset = coin.SoPolygonOffset()
# Face root.
faces = coin.SoSeparator()
faces.addChild(shape_hints)
faces.addChild(self.face_material)
faces.addChild(mat_binding)
faces.addChild(self.geo_coords)
faces.addChild(self.triangles)
# Highlight for selection.
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
highlight.style = 'EMISSIVE_DIFFUSE'
faces.addChild(shape_hints)
highlight.addChild(self.edge_material)
highlight.addChild(mat_binding)
highlight.addChild(self.edge_style)
highlight.addChild(self.geo_coords)
highlight.addChild(self.triangles)
def updateData(self, obj, prop):
'''
Update Object visuals when a data property changed.
'''
origin = PVPlantSite.get()
base = copy.deepcopy(origin.Origin)
base.z = 0
#print(" - Propiedad: ", prop)
if prop == "Shape":
shape = obj.getPropertyByName(prop)
# Get GeoOrigin.
points = [ver.Point for ver in shape.Vertexes]
# Set GeoCoords.
geo_system = ["UTM", origin.UtmZone, "FLAT"]
self.geo_coords.geoSystem.setValues(geo_system)
self.geo_coords.point.values = points
def claimChildren(self):
children = []
if self.Object.Post:
children.append(self.Object.Post)
if self.Object.Base:
children.append(self.Object.Base)
if self.Object.Gate:
children.append(self.Object.Gate)
return children
class _FenceTaskPanel:
'''The TaskPanel to setup the fence'''
def __init__(self):
self.section = None
self.post = None
self.path = None
# form: -----------------------------------------------------------------------------------
self.formFence = QtGui.QWidget()
self.formFence.resize(800, 640)
self.formFence.setWindowTitle("Fence setup")
self.formFence.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
self.grid = QtGui.QGridLayout(self.formFence)
# parameters
self.labelPath = QtGui.QLabel()
self.labelPath.setText("Recorrido:")
self.linePath = QtGui.QLineEdit(self.formFence)
self.linePath.setObjectName(_fromUtf8("lineEdit1"))
self.linePath.readOnly = True
self.grid.addWidget(self.labelPath, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.linePath, self.grid.rowCount() - 1, 1, 1, 1)
self.buttonPathSelect = QtGui.QPushButton('Add')
self.grid.addWidget(self.buttonPathSelect, self.grid.rowCount() - 1, 2, 1, 1)
self.line1 = QtGui.QFrame()
self.line1.setFrameShape(QtGui.QFrame.HLine)
self.line1.setFrameShadow(QtGui.QFrame.Sunken)
self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1)
self.label = QtGui.QLabel()
self.label.setText("Separación entre apoyos:")
self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1)
self.labelInterval = QtGui.QLabel()
self.labelInterval.setText("Intervalo:")
self.valueInterval = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueInterval.setText("4,0 m")
self.grid.addWidget(self.labelInterval, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueInterval, self.grid.rowCount() - 1, 1, 1, 2)
self.line1 = QtGui.QFrame()
self.line1.setFrameShape(QtGui.QFrame.HLine)
self.line1.setFrameShadow(QtGui.QFrame.Sunken)
self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1)
self.label = QtGui.QLabel()
self.label.setText("Mayado:")
self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1)
self.labelHeight = QtGui.QLabel()
self.labelHeight.setText("Altura:")
self.valueHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueHeight.setText("2 m")
self.grid.addWidget(self.labelHeight, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueHeight, self.grid.rowCount() - 1, 1, 1, 2)
self.labelOffset = QtGui.QLabel()
self.labelOffset.setText("Separación del suelo:")
self.valueOffset = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueOffset.setText("0 m")
self.grid.addWidget(self.labelOffset, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueOffset, self.grid.rowCount() - 1, 1, 1, 2)
self.buttonPathSelect.clicked.connect(self.addPath)
self.valueInterval.valueChanged.connect(self.SetupGrid)
self.valueHeight.valueChanged.connect(self.SetupGrid)
# self.valueDepth.valueChanged.connect(self.SetupPost)
# Form para configurar el poste: -------------------------------------------------------------------
self.formPost = QtGui.QWidget()
self.formPost.resize(800, 640)
self.formPost.setWindowTitle("Post setup")
self.formPost.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
self.grid = QtGui.QGridLayout(self.formPost)
# parameters
self.labelDiameter = QtGui.QLabel()
self.labelDiameter.setText("Diámetro:")
self.valueDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueDiameter.setText("48 mm")
self.grid.addWidget(self.labelDiameter, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueDiameter, self.grid.rowCount() - 1, 1, 1, 1)
self.labelLength = QtGui.QLabel()
self.labelLength.setText("Longitud:")
self.valueLength = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueLength.setText("3,0 m")
self.grid.addWidget(self.labelLength, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueLength, self.grid.rowCount() - 1, 1, 1, 1)
self.labelDepth = QtGui.QLabel()
self.labelDepth.setText("Profundidad:")
self.valueDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueDepth.setText("700,0 mm")
self.grid.addWidget(self.labelDepth, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueDepth, self.grid.rowCount() - 1, 1, 1, 1)
self.valueDiameter.valueChanged.connect(self.SetupPost)
self.valueLength.valueChanged.connect(self.SetupPost)
self.valueDepth.valueChanged.connect(self.SetupPost)
# Form para configurar la zapata: ----------------------------------------------------------
self.formFoundation = QtGui.QWidget()
self.formFoundation.resize(800, 640)
self.formFoundation.setWindowTitle("Post setup")
self.formFoundation.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
self.grid = QtGui.QGridLayout(self.formFoundation)
# parameters
self.labelFoundationDiameter = QtGui.QLabel()
self.labelFoundationDiameter.setText("Cimentación:")
self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1)
self.labelFoundationDiameter = QtGui.QLabel()
self.labelFoundationDiameter.setText("Diámetro:")
self.valueFoundationDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueFoundationDiameter.setText("200,0 mm")
self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueFoundationDiameter, self.grid.rowCount() - 1, 1, 1, 1)
self.labelFoundationDepth = QtGui.QLabel()
self.labelFoundationDepth.setText("Profundidad:")
self.valueFoundationDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField")
self.valueFoundationDepth.setText("700,0 mm")
self.grid.addWidget(self.labelFoundationDepth, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.valueFoundationDepth, self.grid.rowCount() - 1, 1, 1, 1)
self.form = [self.formFence, self.formPost, self.formFoundation]
# valores iniciales y creación del la valla:
import Draft
self.post = PVPlantFencePost.makeFencePost() # Arch.makePipe()
self.post.Label = "Post"
Draft.autogroup(self.post)
'''
self.section = self.makeGrid()
self.path = self.section.Base
'''
FreeCAD.ActiveDocument.recompute()
self.fence = makePVPlantFence(self.section, self.post, self.path)
def addPath(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.path = sel[0]
self.linePath.setText(self.path.Label)
self.fence.Base = self.path
FreeCAD.ActiveDocument.recompute()
def SetupPost(self):
if self.post.Diameter != FreeCAD.Units.Quantity(self.valueDiameter.text()).Value:
self.post.Diameter = FreeCAD.Units.Quantity(self.valueDiameter.text()).Value
if self.post.Length != FreeCAD.Units.Quantity(self.valueLength.text()).Value:
self.post.Length = FreeCAD.Units.Quantity(self.valueLength.text()).Value
if self.post.Placement.Base.z != -FreeCAD.Units.Quantity(self.valueDepth.text()).Value:
self.post.Placement.Base.z = -FreeCAD.Units.Quantity(self.valueDepth.text()).Value
self.fence.Depth = FreeCAD.Units.Quantity(self.valueDepth.text()).Value
FreeCAD.ActiveDocument.recompute()
def SetupGrid(self):
return
if self.path.End.x != FreeCAD.Units.Quantity(self.valueInterval.text()).Value:
self.path.End.x = FreeCAD.Units.Quantity(self.valueInterval.text()).Value
if self.section.LengthFwd != FreeCAD.Units.Quantity(self.valueHeight.text()).Value:
self.section.LengthFwd = FreeCAD.Units.Quantity(self.valueHeight.text()).Value
FreeCAD.ActiveDocument.recompute()
def makeGrid(self):
return None
import Draft
p1 = FreeCAD.Vector(0, 0, 0)
p2 = FreeCAD.Vector(4000, 0, 0)
line = Draft.makeLine(p1, p2)
section = FreeCAD.ActiveDocument.addObject('Part::Extrusion', 'Extrude')
section.Base = line
section.DirMode = "Custom"
section.Dir = FreeCAD.Vector(0.0, 0.0, 1.0)
section.DirLink = None
section.LengthFwd = 2000.0
section.LengthRev = 0.0
section.Solid = False
section.Reversed = False
section.Symmetric = False
section.TaperAngle = 0.0
section.TaperAngleRev = 0.0
line.Visibility = False
return section
# Commands ---------------------------------------------------------------------------------
class _CommandPVPlantFence:
"the PVPlant Fence command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "fence.svg")),
'Accel': "C, F",
'MenuText': QT_TRANSLATE_NOOP("PVPlantFence", "Fence"),
'ToolTip': QT_TRANSLATE_NOOP("PVPlantFence",
"Creates a fence object from a selected section, post and path")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _FenceTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
if FreeCAD.GuiUp:
class CommandFenceGroup:
def GetCommands(self):
return tuple(['PVPlantFence',
'PVPlantGate',
'PVPlantFencePost'
])
def GetResources(self):
return {'MenuText': QT_TRANSLATE_NOOP("", 'PVPlantFence'),
'ToolTip': QT_TRANSLATE_NOOP("", 'PVPlantFence')
}
def IsActive(self):
return (not (FreeCAD.ActiveDocument is None) and
not (FreeCAD.ActiveDocument.getObject("Site") is None) and
not (FreeCAD.ActiveDocument.getObject("Terrain") is None))
import PVPlantFenceGate
FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence())
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
def movep(obj):
pl = obj.Shape.BoundBox.Center
points = []
for ind in range(len(obj.Shape.Vertexes)):
points.append(obj.Shape.Vertexes[ind].Point - pl)
Draft.makeWire(points)

768
PVPlantFence.ui Normal file
View File

@@ -0,0 +1,768 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>641</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Configuración de la valla</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.960000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Separación entre apoyos (m)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Recorrido:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Configuración del poste</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Columnas (un)</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="editFrontHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.800000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Orientación del módulo</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="editVerticalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editRows">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboFrameType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Fija</string>
</property>
</item>
<item>
<property name="text">
<string>Tracker 1 Eje</string>
</property>
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="editLeftOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Offset borde derecha (m)</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ángulo de inclinación (º)</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="editRightOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="editTilt">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="editInclination">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filas (un)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Distancia al suelo en el frente (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editCols">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelVerticalGap">
<property name="text">
<string>Separación vertical entre módulos (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboModuleOrientation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Landscape</string>
</property>
</item>
<item>
<property name="text">
<string>Portrait</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Separación horizontal entre módulos (m)</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Ängulo máximo de inclinación longitudinal (ª)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="editHorizontalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tipo de estructura</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Offset borde izquierda (m)</string>
</property>
</widget>
</item>
<item row="16" column="0" colspan="2">
<widget class="QWidget" name="widgetTracker" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>5</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="labelVerticalGap_2">
<property name="text">
<string>Separación entre uniones (m)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Separación Motor (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="editInternalGapNumber">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Número de uniones</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editInternalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editMotorGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Cimentación</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Total de módulos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editTotalModules">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Potencia total (wp)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTotalPower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Longitud (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editTotalLength">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editTotalWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

425
PVPlantFenceGate.py Normal file
View File

@@ -0,0 +1,425 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui, QtSvg
from PySide.QtCore import QT_TRANSLATE_NOOP
import PySide.QtGui as QtGui
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makePVPlantFence():
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'FenceGate')
FenceGate(obj)
if FreeCAD.GuiUp:
ViewProviderGate(obj.ViewObject)
return obj
class FenceGate(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
if not ("GateType" in pl):
obj.addProperty("App::PropertyEnumeration",
"GateType",
"Gate",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).GateType = ["Single", "Double"]
if not ("GateWidth" in pl):
obj.addProperty("App::PropertyDistance",
"GateWidth",
"Gate",
"Base wire"
).GateWidth = 3000
if not ("GateHeight" in pl):
obj.addProperty("App::PropertyDistance",
"GateHeight",
"Gate",
"Base wire"
).GateHeight = 2000
if not ("PostWidth" in pl):
obj.addProperty("App::PropertyDistance",
"PostWidth",
"Gate",
"Base wire"
).PostWidth = 60
if not ("PostHeight" in pl):
obj.addProperty("App::PropertyDistance",
"PostHeight",
"Gate",
"Base wire"
).PostHeight = 2000
if not "PostDepth" in pl:
obj.addProperty("App::PropertyLength",
"PostDepth",
"Fence",
"A single fence post").PostDepth = 700
self.Type = "PVPlatFenceGate"
#obj.Type = self.Type
obj.Proxy = self
self.obj = obj
def __getstate__(self):
if hasattr(self, 'sectionFaceNumbers'):
return (self.sectionFaceNumbers)
return None
def __setstate__(self, state):
if state is not None and isinstance(state, tuple):
self.sectionFaceNumbers = state[0]
return None
def onDocumentRestored(self, obj):
"""Method run when the document is restored."""
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
def execute(self, obj):
tubewidth = 50
gap = 20
posts = Part.makeCompound([])
gates = Part.makeCompound([])
pl = obj.Placement
post = Part.makeBox(obj.PostWidth.Value,
obj.PostWidth.Value,
obj.PostHeight.Value + obj.PostDepth.Value)
gate = Part.makeBox(obj.GateWidth.Value,
tubewidth,
obj.GateHeight.Value)
cut = Part.makeBox(obj.GateWidth.Value - tubewidth * 2,
tubewidth,
obj.GateHeight.Value - tubewidth * 2)
cut.Placement.Base += FreeCAD.Vector(tubewidth, 0, tubewidth)
gate = gate.cut(cut)
if obj.GateType == "Single":
post.Placement.Base = FreeCAD.Vector(-(obj.PostWidth.Value + gap + obj.GateWidth.Value / 2),
-obj.PostWidth.Value / 2,
-obj.PostDepth.Value)
posts.add(post)
post = post.copy()
post.Placement.Base.x = -(post.Placement.Base.x + obj.PostWidth.Value)
posts.add(post)
gate.Placement.Base += FreeCAD.Vector(-obj.GateWidth.Value / 2,
obj.PostWidth.Value / 2 - tubewidth,
0)
gates.add(gate)
else:
post.Placement.Base = FreeCAD.Vector(-(obj.PostWidth.Value + gap * 1.5 + obj.GateWidth.Value),
-obj.PostWidth.Value / 2,
-obj.PostDepth.Value)
posts.add(post)
post = post.copy()
post.Placement.Base.x = -(post.Placement.Base.x + obj.PostWidth.Value)
posts.add(post)
gate.Placement.Base += FreeCAD.Vector(-(obj.GateWidth.Value + gap * 0.5),
obj.PostWidth.Value / 2 - tubewidth,
0)
gates.add(gate)
gate = gate.copy()
gate.Placement.Base.x = gap * 0.5
gates.add(gate)
obj.Shape = Part.makeCompound([gates, posts])
obj.Placement = pl
class ViewProviderGate:
def __init__(self, vobj):
''' Set view properties. '''
vobj.Proxy = self
def attach(self, vobj):
self.Object = vobj.Object
def getIcon(self):
''' Return object treeview icon. '''
return str(os.path.join(DirIcons, "gate.svg"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Base:
children.append(self.Object.Base)
return children
class _CommandPVPlantGate:
"the PVPlant Fence command definition"
def __init__(self):
''' p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
self.Thickness = p.GetFloat("WindowThickness", 50)
self.Width = p.GetFloat("WindowWidth", 1000)
self.Height = p.GetFloat("DoorHeight", 2100)'''
self.Width = 0
self.Height = 0
self.Length = 0
self.tracker = None
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "gate.svg")),
'Accel': "C, F",
'MenuText': QT_TRANSLATE_NOOP("PVPlantFence", "Gate"),
'ToolTip': QT_TRANSLATE_NOOP("PVPlantFence",
"Creates a fence object from a selected section, post and path")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.tracker = DraftTrackers.boxTracker()
self.tracker.length(self.Length)
self.tracker.width(self.Width)
self.tracker.height(self.Height)
self.tracker.on()
FreeCAD.Console.PrintMessage("Choose a face on an existing object or select a preset" + "\n")
FreeCADGui.Snapper.getPoint(callback=self.getPoint,
movecallback=self.update,
extradlg=self.taskbox())
def getPoint(self, point=None, obj=None):
"this function is called by the snapper when it has a 3D point"
if obj.Name[:5] == "Fence":
self.tracker.finalize()
if point is None:
return
ver = Part.Vertex(point)
dist = ver.distToShape(obj.Base.Shape)
point1 = dist[1][0][1]
ed = None
if dist[2][0][3] == "Edge":
ed = obj.Base.Shape.Edges[int(dist[-1][0][4])]
vec = ed.Vertexes[1].Point.sub(ed.Vertexes[0].Point)
FreeCAD.ActiveDocument.openTransaction("Create Gate")
gate = makePVPlantFence()
try:
import MeshPart as mp
point1 = mp.projectPointsOnMesh([point1,], FreeCAD.ActiveDocument.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))[0]
except:
FreeCAD.Console.PrintError("No se puede encontrar punto 3D.." + "\n")
pass
gate.Placement.Base = point1
gate.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(-1, 0, 0), vec)
tmp = obj.Gate
tmp.append(gate)
obj.Gate = tmp
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
return
else:
FreeCADGui.Snapper.getPoint(callback=self.getPoint,
movecallback=self.update,
extradlg=self.taskbox())
def update(self, point, info):
''' this function is called by the Snapper when the mouse is moved '''
delta = FreeCAD.Vector(self.Length / 2, self.Width / 2, self.Height / 2)
rot = FreeCAD.Rotation()
#self.tracker.setRotation(rot)
self.tracker.pos(point)
return
delta = FreeCAD.Vector(self.Length / 2, self.Width / 2, self.Height / 2)
delta = delta.add(FreeCAD.Vector(0, 0, self.Sill))
rot = FreeCAD.Rotation()
if info:
if "Face" in info['Component']:
import WorkingPlane
o = FreeCAD.ActiveDocument.getObject(info['Object'])
self.baseFace = [o, int(info['Component'][4:]) - 1]
# print("switching to ",o.Label," face ",self.baseFace[1])
f = o.Shape.Faces[self.baseFace[1]]
p = WorkingPlane.getPlacementFromFace(f, rotated=True)
if p:
rot = p.Rotation
self.tracker.setRotation(rot)
r = self.tracker.trans.rotation.getValue().getValue()
if r != (0, 0, 0, 1):
delta = FreeCAD.Rotation(r[0], r[1], r[2], r[3]).multVec(FreeCAD.Vector(delta.x, -delta.y, -delta.z))
self.tracker.pos(point.add(delta))
def taskbox(self):
"sets up a taskbox widget"
w = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFenceGate.ui")
self.Width = 80
self.Height = 2000
self.Length = 6146
return w
w = QtGui.QWidget()
ui = FreeCADGui.UiLoader()
w.setWindowTitle("Window options")
grid = QtGui.QGridLayout(w)
# include box
include = QtGui.QCheckBox(translate("Arch","Auto include in host object"))
include.setChecked(True)
grid.addWidget(include,0,0,1,2)
QtCore.QObject.connect(include,QtCore.SIGNAL("stateChanged(int)"),self.setInclude)
# sill height
labels = QtGui.QLabel(translate("Arch","Sill height"))
values = ui.createWidget("Gui::InputField")
grid.addWidget(labels,1,0,1,1)
grid.addWidget(values,1,1,1,1)
QtCore.QObject.connect(values,QtCore.SIGNAL("valueChanged(double)"),self.setSill)
# check for Parts library and Arch presets
# because of the use of FreeCADGui.doCommand() backslashes in the
# paths in librarypresets need to be double escaped "\\\\", so let's
# use forward slashes instead...
self.librarypresets = []
librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString("destination", "")
# librarypath should have only forward slashes already, but let's use replace() anyway just to be sure:
librarypath = librarypath.replace("\\", "/") + "/Architectural Parts"
presetdir = FreeCAD.getUserAppDataDir().replace("\\", "/") + "/Arch"
for path in [librarypath, presetdir]:
if os.path.isdir(path):
for wtype in ["Windows", "Doors"]:
wdir = path + "/" + wtype
if os.path.isdir(wdir):
for subtype in os.listdir(wdir):
subdir = wdir + "/" + subtype
if os.path.isdir(subdir):
for subfile in os.listdir(subdir):
if (os.path.isfile(subdir + "/" + subfile)
and subfile.lower().endswith(".fcstd")):
self.librarypresets.append([wtype + " - " + subtype + " - " + subfile[:-6],
subdir + "/" + subfile])
# presets box
labelp = QtGui.QLabel(translate("Arch","Preset"))
valuep = QtGui.QComboBox()
valuep.setMinimumContentsLength(6)
valuep.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
valuep.addItems(WindowPresets)
valuep.setCurrentIndex(self.Preset)
grid.addWidget(labelp,2,0,1,1)
grid.addWidget(valuep,2,1,1,1)
QtCore.QObject.connect(valuep,QtCore.SIGNAL("currentIndexChanged(int)"),self.setPreset)
for it in self.librarypresets:
valuep.addItem(it[0])
# image display
self.pic = QtGui.QLabel()
grid.addWidget(self.pic,3,0,1,2)
self.pic.setFixedHeight(128)
self.pic.hide()
# SVG display
self.im = QtSvg.QSvgWidget(":/ui/ParametersWindowFixed.svg")
self.im.setMaximumWidth(200)
self.im.setMinimumHeight(120)
grid.addWidget(self.im,4,0,1,2)
#self.im.hide()
# parameters
i = 5
for param in self.wparams:
lab = QtGui.QLabel(translate("Arch",param))
setattr(self,"val"+param,ui.createWidget("Gui::InputField"))
wid = getattr(self,"val"+param)
if param == "Width":
wid.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString)
elif param == "Height":
wid.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString)
elif param == "O1":
n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowO1",0.0)
wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString)
setattr(self,param,n)
elif param == "W1":
n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowW1",self.Thickness*2)
wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString)
setattr(self,param,n)
else:
n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("Window"+param,self.Thickness)
wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString)
setattr(self,param,n)
grid.addWidget(lab,i,0,1,1)
grid.addWidget(wid,i,1,1,1)
i += 1
valueChanged = self.getValueChanged(param)
FreeCAD.wid = wid
QtCore.QObject.connect(getattr(self,"val"+param),QtCore.SIGNAL("valueChanged(double)"), valueChanged)
# restore saved states
if self.doormode:
i = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("DoorPreset",0)
d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("DoorSill",0)
else:
i = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("WindowPreset",0)
d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowSill",0)
if i < valuep.count():
valuep.setCurrentIndex(i)
values.setText(FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).UserString)
return w

352
PVPlantFenceGate.ui Normal file
View File

@@ -0,0 +1,352 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formGate</class>
<widget class="QWidget" name="formGate">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>317</width>
<height>289</height>
</rect>
</property>
<property name="windowTitle">
<string>Puerta:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Puerta</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tipo de puerta</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editGateHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>2.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelVerticalGap">
<property name="text">
<string>Ancho (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editGateWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value">
<double>0.850000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Alto (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboFrameType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Simple</string>
</property>
</item>
<item>
<property name="text">
<string>Doble</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Poste</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="3" column="2">
<widget class="QDoubleSpinBox" name="editPostBuried">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>2000.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
</property>
<property name="value">
<double>700.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Alto (m)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Enterramiento (m)</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="editPostHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5000.000000000000000</double>
</property>
<property name="singleStep">
<double>500.000000000000000</double>
</property>
<property name="value">
<double>2000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Ancho (mm)</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QSpinBox" name="editPostWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>80</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Espacio (mm)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="editGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

285
PVPlantFencePost.py Normal file
View File

@@ -0,0 +1,285 @@
import ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
def makeFencePost(diameter=48, length=3000, placement=None, name="Post"):
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
_FencePost(obj)
if FreeCAD.GuiUp:
_ViewProviderFencePost(obj.ViewObject)
obj.Length = length
obj.Diameter = diameter
if placement:
obj.Placement = placement
return obj
def makeFenceReinforcePost(diameter=48, length=3000, placement=None, name="Post"):
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
_FenceReinforcePostPost(obj)
if FreeCAD.GuiUp:
_ViewProviderFencePost(obj.ViewObject)
obj.Length = length
obj.Diameter = diameter
if placement:
obj.Placement = placement
return obj
class _FencePost(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
obj.IfcType = "Pipe Segment"
self.Type = "FencePost"
def setProperties(self, obj):
pl = obj.PropertiesList
if not "Diameter" in pl:
obj.addProperty("App::PropertyLength", "Diameter", "Pipe",
QT_TRANSLATE_NOOP("App::Property", "The diameter of this pipe, if not based on a profile")
).Diameter = 48
if not "Thickness" in pl:
obj.addProperty("App::PropertyLength", "Thickness", "Pipe",
QT_TRANSLATE_NOOP("App::Property", "The Thickness of this pipe, if not based on a profile")
).Thickness = 4
if not "Length" in pl:
obj.addProperty("App::PropertyLength", "Length", "Pipe",
QT_TRANSLATE_NOOP("App::Property", "The length of this pipe, if not based on an edge")
).Length = 3000
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def execute(self, obj):
import Part
pl = obj.Placement
if obj.CloneOf:
obj.Shape = obj.CloneOf.Shape
else:
w = self.getProfile(obj)
try:
# sh = w.makePipeShell([p], True, False, 2)
sh = w.revolve(FreeCAD.Vector(0.0, 0.0, 0.0), FreeCAD.Vector(0.0, 0.0, 1.0), 360)
sh = Part.Solid(sh)
except:
FreeCAD.Console.PrintError("Unable to build the pipe \n")
else:
obj.Shape = sh
obj.Placement = pl
return
# ------------------------- Prueba para apoyos de refuerzo:
import math
L = math.pi / 2 * (obj.Diameter.Value - 2 * obj.Thickness.Value)
v1 = FreeCAD.Vector(L / 2, 0, obj.Thickness.Value)
vc1 = FreeCAD.Vector(L / 2 + obj.Thickness.Value, 0, 0)
v2 = FreeCAD.Vector(L / 2, 0, -obj.Thickness.Value)
v11 = FreeCAD.Vector(-L / 2, 0, obj.Thickness.Value)
vc11 = FreeCAD.Vector(-(L / 2 + obj.Thickness.Value), 0, 0)
v21 = FreeCAD.Vector(-L / 2, 0, -obj.Thickness.Value)
arc1 = Part.Arc(v1, vc1, v2).toShape()
arc11 = Part.Arc(v11, vc11, v21).toShape()
line1 = Part.LineSegment(v11, v1).toShape()
line2 = Part.LineSegment(v21, v2).toShape()
w = Part.Wire([arc1, line2, arc11, line1])
face = Part.Face(w)
pro = face.extrude(FreeCAD.Vector(0, 40, 0))
#Part.Circle(Center, Normal, Radius)
cir1 = Part.Face(Part.Wire(Part.Circle(FreeCAD.Vector(0, -200, 0), FreeCAD.Vector(0, 1, 0), obj.Diameter.Value / 2).toShape()))
ext = cir1.extrude(FreeCAD.Vector(0, 170, 0))
cir2 = Part.Circle(FreeCAD.Vector(0, -30, 0), FreeCAD.Vector(0, 1, 0), obj.Diameter.Value/2).toShape()
loft = Part.makeLoft([cir2, w], True)
ext = ext.fuse([loft, pro])
Part.show(ext)
def getProfile(self, obj):
import Part
sin45 = 0.707106781
radio = obj.Diameter.Value / 2
taph = 20
tapw = radio + 2
chamfer = 5
chamfer2 = chamfer * sin45
edge1 = Part.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(radio, 0, 0))
edge2 = Part.makeLine(FreeCAD.Vector(radio, 0, 0), FreeCAD.Vector(radio, 0, obj.Length.Value - taph))
edge3 = Part.makeLine(FreeCAD.Vector(radio, 0, obj.Length.Value - taph),
FreeCAD.Vector(tapw, 0, obj.Length.Value - taph))
edge4 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - taph),
FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer))
if True:
edge5 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value))
else:
edge5 = Part.Arc(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
FreeCAD.Vector(tapw - chamfer2, 0, obj.Length.Value - chamfer2),
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value)
).toShape()
edge6 = Part.makeLine(FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value),
FreeCAD.Vector(0, 0, obj.Length.Value))
w = Part.Wire([edge1, edge2, edge3, edge4, edge5, edge6])
return w
class _FenceReinforcePost(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
obj.IfcType = "Pipe Segment"
self.Type = "FencePost"
def setProperties(self, obj):
pl = obj.PropertiesList
if not "Diameter" in pl:
obj.addProperty("App::PropertyLength", "Diameter", "Pipe",
QT_TRANSLATE_NOOP("App::Property", "The diameter of this pipe, if not based on a profile")
).Diameter = 48
if not "Length" in pl:
obj.addProperty("App::PropertyLength", "Length", "Pipe",
QT_TRANSLATE_NOOP("App::Property", "The length of this pipe, if not based on an edge")
).Length = 3000
self.Type = "Pipe"
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def execute(self, obj):
pl = obj.Placement
w = self.getWire(obj)
try:
# sh = w.makePipeShell([p], True, False, 2)
sh = w.revolve(FreeCAD.Vector(0.0, 0.0, 0.0), FreeCAD.Vector(0.0, 0.0, 1.0), 360)
except:
FreeCAD.Console.PrintError(translate("Arch", "Unable to build the pipe") + "\n")
else:
obj.Shape = sh
obj.Placement = pl
def getWire(self, obj):
import Part
sin45 = 0.707106781
radio = obj.Diameter.Value / 2
taph = 20
tapw = radio + 2
chamfer = 5
chamfer2 = chamfer * sin45
edge1 = Part.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(radio, 0, 0))
edge2 = Part.makeLine(FreeCAD.Vector(radio, 0, 0), FreeCAD.Vector(radio, 0, obj.Length.Value - taph))
edge3 = Part.makeLine(FreeCAD.Vector(radio, 0, obj.Length.Value - taph),
FreeCAD.Vector(tapw, 0, obj.Length.Value - taph))
edge4 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - taph),
FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer))
if True:
edge5 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value))
else:
edge5 = Part.Arc(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
FreeCAD.Vector(tapw - chamfer2, 0, obj.Length.Value - chamfer2),
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value)
).toShape()
edge6 = Part.makeLine(FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value),
FreeCAD.Vector(0, 0, obj.Length.Value))
w = Part.Wire([edge1, edge2, edge3, edge4, edge5, edge6])
return w
class _ViewProviderFencePost(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return ":/icons/Arch_Pipe_Tree.svg"
class _CommandFencePost:
"the Arch Pipe command definition"
def GetResources(self):
return {'Pixmap': 'Arch_Pipe',
'MenuText': QT_TRANSLATE_NOOP("Arch_Pipe", "Pipe"),
'Accel': "P, I",
'ToolTip': QT_TRANSLATE_NOOP("Arch_Pipe", "Creates a pipe object from a given Wire or Line")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
if True:
makeFencePost()
else:
FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Pipe"))
FreeCADGui.addModule("Arch")
FreeCADGui.doCommand("obj = Arch.makePipe()")
FreeCADGui.addModule("Draft")
FreeCADGui.doCommand("Draft.autogroup(obj)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('FencePost', _CommandFencePost())

877
PVPlantFixed.ui Normal file
View File

@@ -0,0 +1,877 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>1038</height>
</rect>
</property>
<property name="windowTitle">
<string>Tracker:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Módulos:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Altura (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1.990000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.960000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.030000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Potencia (wp)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editModulePower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>150</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>350</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Estructura</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Columnas (un)</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="editFrontHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.800000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Orientación del módulo</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="editVerticalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editRows">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboFrameType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Fija</string>
</property>
</item>
<item>
<property name="text">
<string>Tracker 1 Eje</string>
</property>
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="editLeftOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Offset borde derecha (m)</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ángulo de inclinación (º)</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="editRightOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="editTilt">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="editInclination">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filas (un)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Distancia al suelo en el frente (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editCols">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelVerticalGap">
<property name="text">
<string>Separación vertical entre módulos (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboModuleOrientation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Landscape</string>
</property>
</item>
<item>
<property name="text">
<string>Portrait</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Separación horizontal entre módulos (m)</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Ängulo máximo de inclinación longitudinal (ª)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="editHorizontalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tipo de estructura</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Offset borde izquierda (m)</string>
</property>
</widget>
</item>
<item row="16" column="0" colspan="2">
<widget class="QWidget" name="widgetTracker" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>5</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="labelVerticalGap_2">
<property name="text">
<string>Separación entre uniones (m)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Separación Motor (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="editInternalGapNumber">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Número de uniones</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editInternalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editMotorGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Resultado</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Total de módulos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editTotalModules">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Potencia total (wp)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTotalPower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Longitud (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editTotalLength">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editTotalWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

225
PVPlantFoundation.py Normal file
View File

@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2016 Yorik van Havre <yorik@uncreated.net> *
# * *
# * 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 FreeCAD, ArchComponent
if FreeCAD.GuiUp:
import FreeCADGui, os
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
from PVPlantResources import DirIcons as DirIcons
__title__="FreeCAD Fotovoltaic Power Plant Toolkit"
__author__ = "Javier Braña"
__url__ = "sn"
def makeFoundation(baseobj=None, diameter=200, length=700, placement=None, name="Foundation"):
"makeFoundation([baseobj,diamerter,length,placement,name]):"
"creates an Foundation object from the given base object"
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
_PVPlantFoundation(obj)
if FreeCAD.GuiUp:
_ViewProviderFoundation(obj.ViewObject)
if baseobj:
baseobj.ViewObject.hide()
if baseobj:
obj.Base = baseobj
else:
if length:
obj.Length = length
else:
obj.Length = 1000
if diameter:
obj.Diameter = diameter
else:
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
obj.Diameter = p.GetFloat("PipeDiameter", 50)
if placement:
obj.Placement = placement
return obj
class _PVPlantFoundation(ArchComponent.Component):
"the PVPlant Foundation object"
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.Type = ""
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
if not "Diameter" in pl:
obj.addProperty("App::PropertyLength", "Diameter", "Foundation", QT_TRANSLATE_NOOP("App::Property",
"The diameter of this Foundation, if not based on a profile"))
if not "Length" in pl:
obj.addProperty("App::PropertyLength", "Length", "Foundation", QT_TRANSLATE_NOOP("App::Property",
"The length of this Foundation, if not based on an edge"))
if not "Profile" in pl:
obj.addProperty("App::PropertyLink", "Profile", "Foundation",
QT_TRANSLATE_NOOP("App::Property", "An optional closed profile to base this Foundation on"))
self.Type = "Foundation"
obj.IfcType = "Element"
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def execute(self, obj):
import DraftGeomUtils, math
pl = obj.Placement
w = self.getWire(obj)
if not w:
FreeCAD.Console.PrintError(translate("Arch", "Unable to build the base path") + "\n")
return
p = self.getProfile(obj)
if not p:
FreeCAD.Console.PrintError(translate("Arch", "Unable to build the profile") + "\n")
return
# move and rotate the profile to the first point
delta = w.Vertexes[0].Point - p.CenterOfMass
p.translate(delta)
import Draft
if Draft.getType(obj.Base) == "BezCurve":
v1 = obj.Base.Placement.multVec(obj.Base.Points[1]) - w.Vertexes[0].Point
else:
v1 = w.Vertexes[1].Point - w.Vertexes[0].Point
v2 = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(v2, v1)
p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle))
try:
sh = w.makePipeShell([p], True, False, 2)
except:
FreeCAD.Console.PrintError(translate("Arch", "Unable to build the Foundation -- ") + "\n")
else:
obj.Shape = sh
if obj.Base:
obj.Length = w.Length
else:
obj.Placement = pl
def getWire(self, obj):
import Part
if obj.Base:
if not hasattr(obj.Base, 'Shape'):
FreeCAD.Console.PrintError(translate("Arch", "The base object is not a Part") + "\n")
return
if len(obj.Base.Shape.Wires) != 1:
FreeCAD.Console.PrintError(translate("Arch", "Too many wires in the base shape") + "\n")
return
if obj.Base.Shape.Wires[0].isClosed():
FreeCAD.Console.PrintError(translate("Arch", "The base wire is closed") + "\n")
return
w = obj.Base.Shape.Wires[0]
else:
if obj.Length.Value == 0:
return
w = Part.Wire([Part.LineSegment(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, obj.Length.Value)).toShape()])
return w
def getProfile(self, obj):
import Part
if obj.Profile:
if not obj.Profile.getLinkedObject().isDerivedFrom("Part::Part2DObject"):
FreeCAD.Console.PrintError(translate("Arch", "The profile is not a 2D Part") + "\n")
return
if len(obj.Profile.Shape.Wires) != 1:
FreeCAD.Console.PrintError(translate("Arch", "Too many wires in the profile") + "\n")
return
if not obj.Profile.Shape.Wires[0].isClosed():
FreeCAD.Console.PrintError(translate("Arch", "The profile is not closed") + "\n")
return
p = obj.Profile.Shape.Wires[0]
else:
if obj.Diameter.Value == 0:
return
p = Part.Wire(
[Part.Circle(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), obj.Diameter.Value / 2).toShape()])
return p
class _ViewProviderFoundation(ArchComponent.ViewProviderComponent):
"A View Provider for the Foundation object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return ":/icons/Arch_Pipe_Tree.svg"
def _FoundationTaskPanel():
''''''
return True
class _CommandFoundation:
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")),
'MenuText': "Trench",
'Accel': "C, T",
'ToolTip': "Creates a Trench object from setup dialog."}
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
def Activated(self):
"""Execute when the command is called."""
task = _FoundationTaskPanel();
sel = FreeCADGui.Selection.getSelection()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantFoundation', _CommandFoundation())

314
PVPlantGeoreferencing.py Normal file
View File

@@ -0,0 +1,314 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
import utm
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
from PySide2.QtWebEngineWidgets import QWebEngineView
from PySide2.QtWebChannel import QWebChannel
import os
else:
# \cond
def translate(ctxt,txt):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
from PVPlantResources import DirResources as DirResources
class MapWindow(QtGui.QWidget):
def __init__(self, WinTitle="MapWindow"):
super(MapWindow, self).__init__()
self.raise_()
self.lat = 0
self.lon = 0
self.WinTitle = WinTitle
self.setupUi()
def setupUi(self):
self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self)
self.resize(1200, 800)
self.setWindowTitle(self.WinTitle)
self.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "Location.svg")))
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.layout = QtGui.QHBoxLayout(self)
self.layout.setContentsMargins(4, 4, 4, 4)
LeftWidget = QtGui.QWidget(self)
LeftLayout = QtGui.QVBoxLayout(LeftWidget)
LeftWidget.setLayout(LeftLayout)
LeftLayout.setContentsMargins(0, 0, 0, 0)
RightWidget = QtGui.QWidget(self)
RightWidget.setFixedWidth(350)
RightLayout = QtGui.QVBoxLayout(RightWidget)
RightWidget.setLayout(RightLayout)
RightLayout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(LeftWidget)
self.layout.addWidget(RightWidget)
# Left Widgets:
# -- Search Bar:
self.valueSearch = QtGui.QLineEdit(self)
self.valueSearch.setPlaceholderText("Search")
self.valueSearch.returnPressed.connect(self.onSearch)
searchbutton = QtGui.QPushButton('Search')
searchbutton.setFixedWidth(80)
searchbutton.clicked.connect(self.onSearch)
SearchBarLayout = QtGui.QHBoxLayout(self)
SearchBarLayout.addWidget(self.valueSearch)
SearchBarLayout.addWidget(searchbutton)
LeftLayout.addLayout(SearchBarLayout)
# -- Webbroser:
self.view = QWebEngineView()
self.channel = QWebChannel(self.view.page())
self.view.page().setWebChannel(self.channel)
self.channel.registerObject("MyApp", self)
file = os.path.join(DirResources, "webs", "main.html")
self.view.page().loadFinished.connect(self.onLoadFinished)
self.view.page().load(QtCore.QUrl.fromLocalFile(file))
LeftLayout.addWidget(self.view)
# self.layout.addWidget(self.view, 1, 0, 1, 3)
# -- Latitud y longitud:
self.labelCoordinates = QtGui.QLabel()
self.labelCoordinates.setFixedHeight(21)
LeftLayout.addWidget(self.labelCoordinates)
# self.layout.addWidget(self.labelCoordinates, 2, 0, 1, 3)
# Right Widgets:
labelKMZ = QtGui.QLabel()
labelKMZ.setText("Cargar un archivo KMZ/KML:")
self.kmlButton = QtGui.QPushButton()
self.kmlButton.setFixedSize(32, 32)
self.kmlButton.setIcon(QtGui.QIcon(os.path.join(DirIcons, "googleearth.svg")))
widget = QtGui.QWidget(self)
layout = QtGui.QHBoxLayout(widget)
widget.setLayout(layout)
layout.addWidget(labelKMZ)
layout.addWidget(self.kmlButton)
RightLayout.addWidget(widget)
# -----------------------
self.groupbox = QtGui.QGroupBox("Importar datos desde:")
self.groupbox.setCheckable(True)
self.groupbox.setChecked(True)
radio1 = QtGui.QRadioButton("Google Elevation")
radio2 = QtGui.QRadioButton("Nube de Puntos")
radio3 = QtGui.QRadioButton("Datos GPS")
radio1.setChecked(True)
# buttonDialog = QtGui.QPushButton('...')
# buttonDialog.setEnabled(False)
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(radio1)
vbox.addWidget(radio2)
vbox.addWidget(radio3)
self.groupbox.setLayout(vbox)
RightLayout.addWidget(self.groupbox)
# ------------------------
verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
RightLayout.addItem(verticalSpacer)
self.bAccept = QtGui.QPushButton('Accept')
self.bAccept.clicked.connect(self.onAcceptClick)
RightLayout.addWidget(self.bAccept)
# signals/slots
QtCore.QObject.connect(self.kmlButton, QtCore.SIGNAL("clicked()"), self.importKML)
def onLoadFinished(self):
file = os.path.join(DirResources, "webs", "map.js")
frame = self.view.page()
with open(file, 'r') as f:
frame.runJavaScript(f.read())
def onSearch(self):
if self.valueSearch.text() == "":
return
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="http")
location = geolocator.geocode(self.valueSearch.text())
print(location.raw)
self.valueSearch.setText(location.address)
self.panMap(location.longitude, location.latitude, location.raw['boundingbox'])
def onAcceptClick(self):
frame = self.view.page()
# 1. georeferenciar
frame.runJavaScript(
"MyApp.georeference(drawnItems.getBounds().getCenter().lat, drawnItems.getBounds().getCenter().lng);"
)
# 2. importar todos los elementos dibujados:
frame.runJavaScript(
"var data = drawnItems.toGeoJSON();"
"MyApp.shapes(JSON.stringify(data));"
)
self.close()
@QtCore.Slot(float, float)
def onMapMove(self, lat, lng):
self.lat = lat
self.lon = lng
x, y, zone_number, zone_letter = utm.from_latlon(lat, lng)
self.labelCoordinates.setText('Longitud: {:.5f}, Latitud: {:.5f}'.format(lng, lat) +
' | UTM: ' + str(zone_number) + zone_letter +
', {:.5f}m E, {:.5f}m N'.format(x, y))
@QtCore.Slot(float, float)
def georeference(self, lat, lng):
import PVPlantSite
from geopy.geocoders import Nominatim
Site = PVPlantSite.get(create=True)
Site.Proxy.setLatLon(lat, lng)
geolocator = Nominatim(user_agent="http")
location = geolocator.reverse('{:.5f}, {:.5f}'.format(lat, lng))
if location:
if location.raw["address"].get("road"):
str = location.raw["address"]["road"]
if location.raw["address"].get("house_number"):
str += ' ({0})'.format(location.raw["address"]["house_number"])
Site.Address = str
if location.raw["address"].get("city"):
Site.City = location.raw["address"]["city"]
if location.raw["address"].get("postcode"):
Site.PostalCode = location.raw["address"]["postcode"]
if location.raw["address"].get("address"):
Site.Region = '{0}'.format(location.raw["address"]["province"])
if location.raw["address"].get("state"):
if Site.Region != "":
Site.Region += " - "
Site.Region += '{0}'.format(location.raw["address"]["state"]) # province - state
Site.Country = location.raw["address"]["country"]
@QtCore.Slot(str)
def shapes(self, drawnItems):
import geojson
import PVPlantImportGrid as ImportElevation
import Draft
items = geojson.loads(drawnItems)
for item in items['features']:
if item['geometry']['type'] == "Point": # 1. if the feature is a Point or Circle:
coord = item['geometry']['coordinates']
point = ImportElevation.getElevationFromOE([[coord[0], coord[1]],])
c = FreeCAD.Vector(point[0][0], point[0][1], point[0][2])
if item['properties'].get('radius'):
r = round(item['properties']['radius'] * 1000, 0)
p = FreeCAD.Placement()
p.Base = c
obj = Draft.makeCircle(r, placement=p, face=False)
else:
''' do something '''
obj = Draft.make_point(c * 1000, color=(0.5, 0.3, 0.6), point_size=10)
else: # 2. if the feature is a Polygon or Line:
cw = False
name = "Línea"
lp = item['geometry']['coordinates']
if item['geometry']['type'] == "Polygon":
cw = True
name = "Area"
lp = item['geometry']['coordinates'][0]
pts = []
for cords in lp:
pts.append([cords[1], cords[0]])
tmp = ImportElevation.getElevationFromOE(pts)
pts = []
for p in tmp:
pts.append(p.sub(FreeCAD.ActiveDocument.Site.Origin))
obj = Draft.makeWire(pts, closed=cw, face=False)
obj.Placement.Base = FreeCAD.ActiveDocument.Site.Origin
obj.Label = name
Draft.autogroup(obj)
if item['properties'].get('name'):
obj.Label = item['properties']['name']
FreeCAD.activeDocument().recompute()
FreeCADGui.updateGui()
FreeCADGui.SendMsgToActiveView("ViewFit")
def panMap(self, lng, lat, geometry=""):
frame = self.view.page()
bbox = "[{0}, {1}], [{2}, {3}]".format(float(geometry[0]), float(geometry[2]),
float(geometry[1]), float(geometry[3]))
command = 'map.panTo(L.latLng({lt}, {lg}));'.format(lt=lat, lg=lng)
command += 'map.fitBounds([{box}]);'.format(box=bbox)
frame.runJavaScript(command)
def importKML(self):
file = QtGui.QFileDialog.getOpenFileName(None, "FileDialog", "", "Google Earth (*.kml *.kmz)")[0]
from lib.kml2geojson import kmz_convert
layers = kmz_convert(file, "", )
frame = self.view.page()
for layer in layers:
command = "drawnItems.addLayer(L.geoJSON({0}));".format(layer)
frame.runJavaScript(command)
class _CommandPVPlantGeoreferencing:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "Location.svg")),
'Accel': "G, R",
'MenuText': QT_TRANSLATE_NOOP("Georeferencing","Georeferencing"),
'ToolTip': QT_TRANSLATE_NOOP("Georeferencing","Referenciar el lugar")}
def Activated(self):
self.form = MapWindow()
self.form.show()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantGeoreferencing',_CommandPVPlantGeoreferencing())

191
PVPlantGeoreferencing.ui Normal file
View File

@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Georeferencing</class>
<widget class="QDialog" name="Georeferencing">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>574</width>
<height>350</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Create Surface</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widgetLeft" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widgetSearch" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWebEngineView" name="widget_4" native="true"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widgetRight" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="widget_5" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cargar un archivo KMZ/KML:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Georeferenciar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget_6" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Cancelar</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Aceptar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebEngineView</class>
<extends>QWidget</extends>
<header>qwebengineview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Georeferencing</class>
<widget class="QDialog" name="Georeferencing">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>574</width>
<height>350</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Create Surface</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widgetLeft" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widgetSearch" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWebEngineView" name="widget_4" native="true"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widgetRight" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Configuraciones:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Georeferenciar</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cargar un archivo KMZ/KML:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget">
<item>
<property name="text">
<string>New Item</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget_6" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Cancelar</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Aceptar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebEngineView</class>
<extends>QWidget</extends>
<header>qwebengineview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

660
PVPlantImportGrid.py Normal file
View File

@@ -0,0 +1,660 @@
# /**********************************************************************
# * *
# * 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 json
import urllib.request
import Draft
import FreeCAD
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
from PVPlantResources import DirIcons as DirIcons
import PVPlantSite
def getElevationFromOE(coordinates):
if len(coordinates) == 0:
return None
from requests import get
import utm
str=""
total = len(coordinates) - 1
for i, point in enumerate(coordinates):
str += '{:.6f},{:.6f}'.format(point[0], point[1])
if i != total:
str += '|'
query = 'https://api.open-elevation.com/api/v1/lookup?locations=' + str
r = get(query, timeout=20)
# Only get the json response in case of 200 or 201
points = []
if r.status_code == 200 or r.status_code == 201:
results = r.json()
for point in results["results"]:
c = utm.from_latlon(point["latitude"], point["longitude"])
v = FreeCAD.Vector(round(c[0] * 1000, 0),
round(c[1] * 1000, 0),
round(point["elevation"] * 1000, 0))
points.append(v)
return points
def getSinglePointElevationFromBing(lat, lng):
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
source += str(lat) + "," + str(lng)
source += "&heights=sealevel"
source += "&key=AmsPZA-zRt2iuIdQgvXZIxme2gWcgLaz7igOUy7VPB8OKjjEd373eCnj1KFv2CqX"
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['resourceSets'][0]['resources'][0]['elevations']
import utm
for elevation in res:
c = utm.from_latlon(lat, lng)
v = FreeCAD.Vector(
round(c[0] * 1000, 0),
round(c[1] * 1000, 0),
round(elevation * 1000, 0))
return v
def getGridElevationFromBing(polygon, lat, lng, resolution = 1000):
#http://dev.virtualearth.net/REST/v1/Elevation/Polyline?points=35.89431,-110.72522,35.89393,-110.72578,35.89374,-110.72606,35.89337,-110.72662
# &heights=ellipsoid&samples=10&key={BingMapsAPIKey}
import utm
import math
import requests
geo = utm.from_latlon(lat, lng)
# result = (679434.3578335291, 4294023.585627955, 30, 'S')
# EASTING, NORTHING, ZONE NUMBER, ZONE LETTER
#StepsXX = int((polygon.Shape.BoundBox.XMax - polygon.Shape.BoundBox.XMin) / (resolution*1000))
points = []
yy = polygon.Shape.BoundBox.YMax
while yy > polygon.Shape.BoundBox.YMin:
xx = polygon.Shape.BoundBox.XMin
while xx < polygon.Shape.BoundBox.XMax:
StepsXX = int(math.ceil((polygon.Shape.BoundBox.XMax - xx) / resolution))
if StepsXX > 1000:
StepsXX = 1000
xx1 = xx + 1000 * resolution
else:
xx1 = xx + StepsXX * resolution
point1 = utm.to_latlon(xx / 1000, yy / 1000, geo[2], geo[3])
point2 = utm.to_latlon(xx1 / 1000, yy / 1000, geo[2], geo[3])
source = "http://dev.virtualearth.net/REST/v1/Elevation/Polyline?points="
source += "{lat1},{lng1}".format(lat1=point1[0], lng1=point1[1])
source += ","
source += "{lat2},{lng2}".format(lat2=point2[0], lng2=point2[1])
source += "&heights=sealevel"
source += "&samples={steps}".format(steps=StepsXX)
source += "&key=AmsPZA-zRt2iuIdQgvXZIxme2gWcgLaz7igOUy7VPB8OKjjEd373eCnj1KFv2CqX"
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['resourceSets'][0]['resources'][0]['elevations']
i = 0
for elevation in res:
v = FreeCAD.Vector(xx + resolution * i, yy, round(elevation * 1000, 4))
points.append(v)
i += 1
xx = xx1 + resolution # para no repetir un mismo punto
yy -= resolution
return points
def getSinglePointElevation(lat, lon):
source = "https://maps.googleapis.com/maps/api/elevation/json?locations="
source += str(lat) + "," + str(lon)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
#print (source)
#response = request.urlopen(source)
#ans = response.read()
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
from geopy.distance import geodesic
for r in res:
reference = (0.0, 0.0)
v = FreeCAD.Vector(
round(geodesic(reference, (0.0, r['location']['lng'])).m, 2),
round(geodesic(reference, (r['location']['lat'], 0.0)).m, 2),
round(r['elevation'] * 1000, 2)
)
return v
def _getSinglePointElevation(lat, lon):
source = "https://maps.googleapis.com/maps/api/elevation/json?locations="
source += str(lat) + "," + str(lon)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
#print (source)
#response = request.urlopen(source)
#ans = response.read()
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
import pymap3d as pm
for r in res:
x, y, z = pm.geodetic2ecef(round(r['location']['lng'], 2),
round(r['location']['lat'], 2),
0)
v = FreeCAD.Vector(x,y,z)
return v
def getSinglePointElevation1(lat, lon):
source = "https://maps.googleapis.com/maps/api/elevation/json?locations="
source += str(lat) + "," + str(lon)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
#response = urllib.request.urlopen(source)
#ans = response.read()
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
for r in res:
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0], 4),
round(c[1], 4),
round(r['elevation'] * 1000, 2)
)
return v
def getSinglePointElevationUtm(lat, lon):
source = "https://maps.googleapis.com/maps/api/elevation/json?locations="
source += str(lat) + "," + str(lon)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
print(source)
#response = urllib.request.urlopen(source)
#ans = response.read()
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
print (res)
import utm
for r in res:
c = utm.from_latlon(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0] * 1000, 4),
round(c[1] * 1000, 4),
round(r['elevation'] * 1000, 2))
print (v)
return v
def getElevationUTM(polygon, lat, lng, resolution = 10000):
import utm
geo = utm.from_latlon(lat, lng)
# result = (679434.3578335291, 4294023.585627955, 30, 'S')
# EASTING, NORTHING, ZONE NUMBER, ZONE LETTER
StepsXX = int((polygon.Shape.BoundBox.XMax - polygon.Shape.BoundBox.XMin) / (resolution*1000))
points = []
yy = polygon.Shape.BoundBox.YMax
while yy > polygon.Shape.BoundBox.YMin:
# utm.to_latlon(EASTING, NORTHING, ZONE NUMBER, ZONE LETTER).
# result = (LATITUDE, LONGITUDE)
point1 = utm.to_latlon(polygon.Shape.BoundBox.XMin / 1000, yy / 1000, geo[2], geo[3])
point2 = utm.to_latlon(polygon.Shape.BoundBox.XMax / 1000, yy / 1000, geo[2], geo[3])
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
source += "{a},{b}".format(a = point1[0], b = point1[1])
source += "|"
source += "{a},{b}".format(a = point2[0], b = point2[1])
source += "&samples={a}".format(a = StepsXX)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
for r in res:
c = utm.from_latlon(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0] * 1000, 2),
round(c[1] * 1000, 2),
round(r['elevation'] * 1000, 2)
)
points.append(v)
yy -= (resolution*1000)
FreeCAD.activeDocument().recompute()
return points
def getElevation1(polygon,resolution=10):
StepsXX = int((polygon.Shape.BoundBox.XMax - polygon.Shape.BoundBox.XMin) / (resolution * 1000))
points = []
yy = polygon.Shape.BoundBox.YMax
while yy > polygon.Shape.BoundBox.YMin:
point1 = tm.toGeographic(polygon.Shape.BoundBox.XMin, yy)
point2 = tm.toGeographic(polygon.Shape.BoundBox.XMax, yy)
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
source += "{a},{b}".format(a = point1[0], b = point1[1])
source += "|"
source += "{a},{b}".format(a = point2[0], b = point2[1])
source += "&samples={a}".format(a = StepsXX)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
try:
#response = urllib.request.urlopen(source)
#ans = response.read()
import requests
response = requests.get(source)
ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
except:
continue
#points = []
for r in res:
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0], 2),
round(c[1], 2),
round(r['elevation'] * 1000, 2)
)
points.append(v)
FreeCAD.activeDocument().recompute()
yy -= (resolution*1000)
return points
## download the heights from google:
def getElevation(lat, lon, b=50.35, le=11.17, size=40):
#https://maps.googleapis.com/maps/api/elevation/json?path=36.578581,-118.291994|36.23998,-116.83171&samples=3&key=YOUR_API_KEY
#https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key=YOUR_API_KEY
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
source += str(b-size*0.001) + "," + str(le) + "|" + str(b+size*0.001) + "," + str(le)
source += "&samples=" + str(100)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
response = urllib.request.urlopen(source)
ans = response.read()
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
from geopy.distance import geodesic
points = []
for r in res:
reference = (0.0, 0.0)
v = FreeCAD.Vector(
round(geodesic(reference, (0.0, r['location']['lat'])).m, 2),
round(geodesic(reference, (r['location']['lng'], 0.0)).m, 2),
round(r['elevation'] * 1000, 2) - baseheight
)
points.append(v)
line = Draft.makeWire(points, closed=False, face=False, support=None)
line.ViewObject.Visibility = False
#FreeCAD.activeDocument().recompute()
FreeCADGui.updateGui()
return FreeCAD.activeDocument().ActiveObject
'''
# original::
def getElevation(lat, lon, b=50.35, le=11.17, size=40):
tm.lat = lat
tm.lon = lon
baseheight = 0 #getheight(tm.lat, tm.lon)
center = tm.fromGeographic(tm.lat, tm.lon)
#https://maps.googleapis.com/maps/api/elevation/json?path=36.578581,-118.291994|36.23998,-116.83171&samples=3&key=YOUR_API_KEY
#https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key=YOUR_API_KEY
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
source += str(b-size*0.001) + "," + str(le) + "|" + str(b+size*0.001) + "," + str(le)
source += "&samples=" + str(100)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
response = urllib.request.urlopen(source)
ans = response.read()
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
points = []
for r in res:
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0], 2),
round(c[1], 2),
round(r['elevation'] * 1000, 2) - baseheight
)
points.append(v)
line = Draft.makeWire(points, closed=False, face=False, support=None)
line.ViewObject.Visibility = False
#FreeCAD.activeDocument().recompute()
FreeCADGui.updateGui()
return FreeCAD.activeDocument().ActiveObject
'''
class _ImportPointsTaskPanel:
def __init__(self, obj = None):
self.obj = None
self.Boundary = None
self.select = 0
self.filename = ""
# form:
self.form1 = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/PVPlantImportGrid.ui")
self.form1.radio1.toggled.connect(lambda: self.mainToggle(self.form1.radio1))
self.form1.radio2.toggled.connect(lambda: self.mainToggle(self.form1.radio2))
self.form1.radio1.setChecked(True) # << --------------Poner al final para que no dispare antes de crear los componentes a los que va a llamar
#self.form.buttonAdd.clicked.connect(self.add)
self.form1.buttonDEM.clicked.connect(self.openFileDEM)
self.form2 = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/PVPlantCreateTerrainMesh.ui")
#self.form2.buttonAdd.clicked.connect(self.add)
self.form2.buttonBoundary.clicked.connect(self.addBoundary)
#self.form = [self.form1, self.form2]
self.form = self.form1
''' future:
def retranslateUi(self, dialog):
self.form1.setWindowTitle("Configuracion del Rack")
self.labelModule.setText(QtGui.QApplication.translate("PVPlant", "Modulo:", None))
self.labelModuleLength.setText(QtGui.QApplication.translate("PVPlant", "Longitud:", None))
self.labelModuleWidth.setText(QtGui.QApplication.translate("PVPlant", "Ancho:", None))
self.labelModuleHeight.setText(QtGui.QApplication.translate("PVPlant", "Alto:", None))
self.labelModuleFrame.setText(QtGui.QApplication.translate("PVPlant", "Ancho del marco:", None))
self.labelModuleColor.setText(QtGui.QApplication.translate("PVPlant", "Color del modulo:", None))
self.labelModules.setText(QtGui.QApplication.translate("Arch", "Colocacion de los Modulos", None))
self.labelModuleOrientation.setText(QtGui.QApplication.translate("Arch", "Orientacion del modulo:", None))
self.labelModuleGapX.setText(QtGui.QApplication.translate("Arch", "Separacion Horizontal (mm):", None))
self.labelModuleGapY.setText(QtGui.QApplication.translate("Arch", "Separacion Vertical (mm):", None))
self.labelModuleRows.setText(QtGui.QApplication.translate("Arch", "Filas de modulos:", None))
self.labelModuleCols.setText(QtGui.QApplication.translate("Arch", "Columnas de modulos:", None))
self.labelRack.setText(QtGui.QApplication.translate("Arch", "Configuracion de la estructura", None))
self.labelRackType.setText(QtGui.QApplication.translate("Arch", "Tipo de estructura:", None))
self.labelLevel.setText(QtGui.QApplication.translate("Arch", "Nivel:", None))
self.labelOffset.setText(QtGui.QApplication.translate("Arch", "Offset", None))
'''
def add(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.obj = sel[0]
self.lineEdit1.setText(self.obj.Label)
def addBoundary(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.Boundary = sel[0]
self.form2.editBoundary.setText(self.Boundary.Label)
def openFileDEM(self):
filters = "Esri ASC (*.asc);;CSV (*.csv);;All files (*.*)"
filename = QtGui.QFileDialog.getOpenFileName(None,
"Open DEM,",
"",
filters)
self.filename = filename[0]
self.form1.editDEM.setText(filename[0])
def mainToggle(self, radiobox):
if radiobox is self.form1.radio1:
self.select = 0
self.form1.gbLocalFile.setVisible(True)
elif radiobox is self.form1.radio2:
self.select = 1
self.form1.gbLocalFile.setVisible(True)
def accept(self):
from datetime import datetime
starttime = datetime.now()
site = PVPlantSite.get()
try:
PointGroups = FreeCAD.ActiveDocument.Point_Groups
except:
PointGroups = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Point_Groups')
PointGroups.Label = "Point Groups"
PointGroup = FreeCAD.ActiveDocument.addObject('Points::Feature', "Point_Group")
PointGroup.Label = "Land_Grid_Points"
FreeCAD.ActiveDocument.Point_Groups.addObject(PointGroup)
PointObject = PointGroup.Points.copy()
if self.select == 0: # Google or bing or ...
#for item in self.obj:
#if self.groupbox.isChecked:break
resol = FreeCAD.Units.Quantity(self.valueResolution.text()).Value
Site = FreeCAD.ActiveDocument.Site
pts = getGridElevationFromBing(self.obj, Site.Latitude, Site.Longitude, resol)
PointObject.addPoints(pts)
PointGroup.Points = PointObject
else:
if self.filename == "":
return
import Utils.importDEM as openDEM
if self.select == 1: # DEM.
import numpy as np
root, extension = os.path.splitext(self.filename)
if extension.lower() == ".asc":
x, y, datavals, cellsize, nodata_value = openDEM.openEsri(self.filename)
if self.Boundary:
inc_x = self.Boundary.Shape.BoundBox.XLength * 0.05
inc_y = self.Boundary.Shape.BoundBox.YLength * 0.05
min_x = 0
max_x = 0
comp = (self.Boundary.Shape.BoundBox.XMin - inc_x) / 1000
for i in range(nx):
if x[i] > comp:
min_x = i - 1
break
comp = (self.Boundary.Shape.BoundBox.XMax + inc_x) / 1000
for i in range(min_x, nx):
if x[i] > comp:
max_x = i
break
min_y = 0
max_y = 0
comp = (self.Boundary.Shape.BoundBox.YMax + inc_y) / 1000
for i in range(ny):
if y[i] < comp:
max_y = i
break
comp = (self.Boundary.Shape.BoundBox.YMin - inc_y) / 1000
for i in range(max_y, ny):
if y[i] < comp:
min_y = i
break
x = x[min_x:max_x]
y = y[max_y:min_y]
datavals = datavals[max_y:min_y, min_x:max_x]
pts = []
if True: # faster but more memory 46s - 4,25 gb
x, y = np.meshgrid(x, y)
xx = x.flatten()
yy = y.flatten()
zz = datavals.flatten()
x[:] = 0
y[:] = 0
datavals[:] = 0
pts = []
for i in range(0, len(xx)):
pts.append(FreeCAD.Vector(xx[i], yy[i], zz[i]) * 1000)
xx[:] = 0
yy[:] = 0
zz[:] = 0
else: # 51s 3,2 gb
createmesh = True
if createmesh:
import Part, Draft
lines=[]
for j in range(len(y)):
edges = []
for i in range(0, len(x) - 1):
ed = Part.makeLine(FreeCAD.Vector(x[i], y[j], datavals[j][i]) * 1000,
FreeCAD.Vector(x[i + 1], y[j], datavals[j][i + 1]) * 1000)
edges.append(ed)
#bspline = Draft.makeBSpline(pts)
#bspline.ViewObject.hide()
line = Part.Wire(edges)
lines.append(line)
'''
for i in range(0, len(bsplines), 100):
p = Part.makeLoft(bsplines[i:i + 100], False, False, False)
Part.show(p)
'''
p = Part.makeLoft(lines, False, True, False)
p = Part.Solid(p)
Part.show(p)
else:
pts = []
for j in range(ny):
for i in range(nx):
pts.append(FreeCAD.Vector(x[i], y[j], datavals[j][i]) * 1000)
elif extension.lower() == ".csv" or extension.lower() == ".txt": # x, y, z from gps
pts = openDEM.interpolatePoints(openDEM.openCSV(self.filename))
PointObject.addPoints(pts)
PointGroup.Points = PointObject
FreeCAD.ActiveDocument.recompute()
FreeCADGui.Control.closeDialog()
print("tiempo: ", datetime.now() - starttime)
def reject(self):
FreeCADGui.Control.closeDialog()
## Comandos -----------------------------------------------------------------------------------------------------------
class CommandImportPoints:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "cloud.svg")),
'MenuText': QT_TRANSLATE_NOOP("PVPlant", "Importer Grid"),
'Accel': "B, U",
'ToolTip': QT_TRANSLATE_NOOP("PVPlant", "Creates a cloud of points.")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _ImportPointsTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
if FreeCAD.GuiUp:
class CommandPointsGroup:
def GetCommands(self):
return tuple(['ImportPoints'
])
def GetResources(self):
return { 'MenuText': QT_TRANSLATE_NOOP("",'Cloud of Points'),
'ToolTip': QT_TRANSLATE_NOOP("",'Cloud of Points')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('ImportPoints', CommandImportPoints())
FreeCADGui.addCommand('PointsGroup', CommandPointsGroup())

60
PVPlantImportGrid.ui Normal file
View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formImportData</class>
<widget class="QDialog" name="formImportData">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>408</width>
<height>164</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Surface</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Importar datos desde:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="radio1">
<property name="text">
<string>Google Elevation</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radio2">
<property name="text">
<string>Archivo DEM</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="gbLocalFile" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="editDEM"/>
</item>
<item>
<widget class="QPushButton" name="buttonDEM">
<property name="text">
<string>Sel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

61
PVPlantImportGridAjust.ui Normal file
View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formSlopeAnalisys</class>
<widget class="QDialog" name="formSlopeAnalisys">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>442</width>
<height>109</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Surface</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cloud of points</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="editCloud"/>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="buttonCloud">
<property name="text">
<string>Sel</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Boundary</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="editBoundary"/>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="buttonBoundary">
<property name="text">
<string>Sel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

278
PVPlantManhole.py Normal file
View File

@@ -0,0 +1,278 @@
# /**********************************************************************
# * *
# * 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 ArchComponent
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
import draftguitools.gui_trackers as DraftTrackers
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makeManhole(name="Manhole"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Manhole")
obj.Label = name
_Manhole(obj)
_ViewProviderManhole(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
return obj
class _Manhole(ArchComponent.Component):
"A Manhole Obcject"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
# Dimensions: --------------------------------------------------------------------------------------------------
if not "Height" in pl:
obj.addProperty("App::PropertyLength",
"Height",
"Manhole",
"The height of this object"
).Height = 1000
if not "Width" in pl:
obj.addProperty("App::PropertyLength",
"Width",
"Manhole",
"The width of this object"
).Width = 1000
if not "Length" in pl:
obj.addProperty("App::PropertyLength",
"Length",
"Manhole",
"The height of this object"
).Length = 2000
if not "Thickness" in pl:
obj.addProperty("App::PropertyLength",
"Thickness",
"Manhole",
"The height of this object"
).Thickness = 100
# outputs:
if not "InternalVolume" in pl:
obj.addProperty("App::PropertyVolume",
"InternalVolume",
"Outputs",
"The height of this object"
)
if not "ExternalVolume" in pl:
obj.addProperty("App::PropertyVolume",
"ExternalVolume",
"Outputs",
"The height of this object"
)
self.Type = "Manhole"
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and Arch wall properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
def execute(self, obj):
w_med = obj.Width.Value / 2
l_med = obj.Length.Value / 2
p1 = FreeCAD.Vector(-l_med, -w_med, 0)
p2 = FreeCAD.Vector( l_med, -w_med, 0)
p3 = FreeCAD.Vector( l_med, w_med, 0)
p4 = FreeCAD.Vector(-l_med, w_med, 0)
ext = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1, ]))
ins = ext.makeOffset2D(-obj.Thickness.Value, join=2, openResult=True)
ext_sol = ext.extrude(FreeCAD.Vector(0, 0, -obj.Height.Value))
ins_sol = ins.extrude(FreeCAD.Vector(0, 0, -(obj.Height.Value - obj.Thickness.Value)))
obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(DirIcons, "manhole.svg"))
def setEdit(self, vobj, mode):
"""Method called when the document requests the object to enter edit mode.
Edit mode is entered when a user double clicks on an object in the tree
view, or when they use the menu option [Edit -> Toggle Edit Mode].
Just display the standard Arch component task panel.
Parameters
----------
mode: int or str
The edit mode the document has requested. Set to 0 when requested via
a double click or [Edit -> Toggle Edit Mode].
Returns
-------
bool
If edit mode was entered.
"""
if (mode == 0) and hasattr(self, "Object"):
taskd = _ManholeTaskPanel(self.Object)
taskd.obj = self.Object
# taskd.update()
FreeCADGui.Control.showDialog(taskd)
return True
return False
import draftguitools.gui_tool_utils as gui_tool_utils
class _ManholeTaskPanel:
def __init__(self, obj=None):
self.new = False
if obj is None:
self.new = True
obj = makeManhole()
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantManhole.ui")
self.node = None
self.view = FreeCADGui.ActiveDocument.ActiveView
self.tracker = DraftTrackers.ghostTracker(obj)
self.tracker.on()
self.call = self.view.addEventCallback("SoEvent", self.action)
def action(self, arg):
"""Handle the 3D scene events.
This is installed as an EventCallback in the Inventor view.
Parameters
----------
arg: dict
Dictionary with strings that indicates the type of event received
from the 3D view.
"""
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
self.finish()
elif arg["Type"] == "SoLocation2Event":
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
if info:
self.tracker.move(FreeCAD.Vector(info["x"], info["y"], info["z"]))
else:
self.tracker.move(point)
elif (arg["Type"] == "SoMouseButtonEvent" and
arg["State"] == "DOWN" and
arg["Button"] == "BUTTON1"):
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
if info:
self.obj.Placement.Base = FreeCAD.Vector(info["x"], info["y"], info["z"])
else:
self.obj.Placement.Base = point
self.finish()
def finish(self):
self.accept()
def accept(self):
self.closeForm()
return True
def reject(self):
if self.new:
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
self.closeForm()
return True
def closeForm(self):
self.tracker.finalize()
FreeCADGui.Control.closeDialog()
self.view.removeEventCallback("SoEvent", self.call)
class _CommandManhole:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "manhole.svg")),
'MenuText': "Manhole",
'Accel': "C, M",
'ToolTip': "Creates a Manhole object from setup dialog."}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
if FreeCAD.ActiveDocument is not None:
if FreeCADGui.Selection.getCompleteSelection():
for ob in FreeCAD.ActiveDocument.Objects:
if ob.Name[:4] == "Site":
return True
def Activated(self):
TaskPanel = _ManholeTaskPanel()
FreeCADGui.Control.showDialog(TaskPanel)
return
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantManhole', _CommandManhole())

218
PVPlantManhole.ui Normal file
View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Dimensions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Heigth (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>2000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

218
PVPlantMenuEditor.ui Normal file
View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Dimensions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Heigth (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>2000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

439
PVPlantPad.py Normal file
View File

@@ -0,0 +1,439 @@
# /**********************************************************************
# * *
# * 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 FreeCAD, Part
import BOPTools.SplitAPI as splitter
import ArchComponent
import PVPlantSite
import math
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Trench"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makePad(base=None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Pad")
Pad(obj)
ViewProviderPad(obj.ViewObject)
obj.Base = base
return obj
class Pad(ArchComponent.Component):
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.base = None
self.setProperties(obj)
obj.Proxy = self
obj.IfcType = "Civil Element"
obj.setEditorMode("IfcType", 1)
obj.ViewObject.ShapeColor = (0.305, 0.230, 0.191)
def setProperties(self, obj):
# Definicion de Propiedades:
#TODO: Los parametros width y length desaparecerán. Se tiene que selecionar objeto base
obj.addProperty("App::PropertyLength",
"Width",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).Width = 5000
obj.addProperty("App::PropertyLength",
"Length",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).Length = 10000
obj.addProperty("App::PropertyAngle",
"FillSlope",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).FillSlope = 45.00
obj.addProperty("App::PropertyAngle",
"CutSlope",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).CutSlope = 60.00
obj.addProperty("App::PropertyBool",
"TopsoilCalculation",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).TopsoilCalculation = False
obj.addProperty("App::PropertyLength",
"TopsoilHeight",
"Pad",
QT_TRANSLATE_NOOP("App::Property", "Connection")).TopsoilHeight = 300
# Output values:
obj.addProperty("App::PropertyVolume",
"CutVolume",
"Output",
QT_TRANSLATE_NOOP("App::Property", "Connection"))
obj.setEditorMode("CutVolume", 1)
obj.addProperty("App::PropertyVolume",
"FillVolume",
"Output",
QT_TRANSLATE_NOOP("App::Property", "Connection"))
obj.setEditorMode("FillVolume", 1)
obj.addProperty("App::PropertyArea",
"PadArea",
"Output",
QT_TRANSLATE_NOOP("App::Property", "Connection"))
obj.setEditorMode("PadArea", 1)
obj.addProperty("App::PropertyArea",
"TopSoilArea",
"Output",
QT_TRANSLATE_NOOP("App::Property", "Connection"))
obj.setEditorMode("TopSoilArea", 1)
obj.addProperty("App::PropertyVolume",
"TopSoilVolume",
"Output",
QT_TRANSLATE_NOOP("App::Property", "Connection"))
obj.setEditorMode("TopSoilVolume", 1)
obj.setEditorMode("Placement", 1)
self.Type = "Pad"
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and object properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.obj = obj
self.Type = "Pad"
obj.Proxy = self
def onChanged(self, fp, prop):
'''Do something when a property has changed'''
def execute(self, obj):
from datetime import datetime
starttime = datetime.now()
pb = obj.Placement.Base
land = PVPlantSite.get().Terrain
shapes = []
pad = None
if obj.Base:
if hasattr(obj.Base.Shape, 'Wires') and obj.Base.Shape.Wires:
pad = obj.Base.Shape.Wires[0]
elif obj.Base.Shape.Edges:
pad = Part.Wire(obj.Base.Shape.Edges)
pb = obj.Base.Placement.Base
else:
# Si no hay una base seleccionada se crea una rectangular:
halfWidth = obj.Width.Value / 2
halfLength = obj.Length.Value / 2
p1 = FreeCAD.Vector(-halfLength, -halfWidth, 0)
p2 = FreeCAD.Vector( halfLength, -halfWidth, 0)
p3 = FreeCAD.Vector( halfLength, halfWidth, 0)
p4 = FreeCAD.Vector(-halfLength, halfWidth, 0)
pad = Part.makePolygon([p1, p2, p3, p4, p1])
'''pad = Draft.makeWire([p1, p2, p3, p4, p1])
obj.Base = pad
pad = obj.Base.Shape.Wires[0]'''
# 1. Terraplén (fill):
fill = None
fillcommon = None
if land.Shape.BoundBox.ZMin < pb.z:
tool = self.createSolid(obj, pad, land, -1)
fillcommon, fill = self.calculateFill(obj, tool)
else:
print("- PAD: NOOOO Calculete fill solid:")
# 2. Desmonte (cut):
cut = None
'''cutcommon = None
if land.Shape.BoundBox.ZMax > pb.z:
cut = self.createSolid(obj, pad, land, 1)
cut.Placement.Base += pb
cutcommon, cut = self.calculateCut(obj, cut)
else:
print("- PAD: NOOOO Calcalete cut solid:")'''
topsoilArea = 0
topsoilVolume = 0
if fill:
if obj.TopsoilCalculation:
filltopsoil = fillcommon.extrude(FreeCAD.Vector(0, 0, -obj.TopsoilHeight))
topsoilVolume += filltopsoil.Volume
filltopsoil.Placement.Base -= pb
shapes.append(filltopsoil)
fill.Placement.Base -= pb
shapes.append(fill)
topsoilArea += fill.Area
if cut:
cut.Placement.Base -= pb
shapes.append(cut)
topsoilArea += cut.Area
if obj.TopsoilCalculation:
cuttopsoil = cutcommon.extrude(FreeCAD.Vector(0, 0, -obj.TopsoilHeight))
topsoilVolume += cuttopsoil.Volume
obj.CutVolume = obj.CutVolume.Value - cuttopsoil.Volume
pad = Part.Face(pad)
if len(shapes) == 0:
shapes.append(pad)
shape = Part.makeCompound(shapes)
#shape.Placement.Base = FreeCAD.Vector(0)
obj.Shape = shape
obj.Placement.Base = pb
obj.PadArea = pad.Area
obj.TopSoilArea = topsoilArea
obj.TopSoilVolume = topsoilVolume
total_time = datetime.now() - starttime
print(" -- Tiempo tardado:", total_time)
def createSolid(self, obj, base, land, dir = -1):
base_copy = base.copy()
base_copy.Placement.Base = FreeCAD.Vector(0,0,0)
if dir == -1:
zz = land.Mesh.BoundBox.ZMin
angle = obj.FillSlope.Value
else:
zz = land.Mesh.BoundBox.ZMax
angle = obj.CutSlope.Value
height = abs(zz - base.Placement.Base.z)
offset = base_copy.makeOffset2D(height / math.tan(math.radians(angle)), 0, False, False, True)
offset.Placement.Base.z = dir * height
import DraftGeomUtils
base_fillet = DraftGeomUtils.filletWire(base_copy, 1) #trick to get a nice shape: (fillet of 1 mm)
pad = Part.makeLoft([base_fillet, offset], True)
pad.Placement.Base = base.Placement.Base
return pad
def calculateFill(self, obj, solid):
common = solid.common(PVPlantSite.get().Terrain.Shape)
if common.Area > 0:
sp = splitter.slice(solid, [common, ], "Split")
commoncopy = common.copy()
commoncopy.Placement.Base.z += 10
volume = 0
fills = []
for sol in sp.Solids:
common1 = sol.common(commoncopy)
if common1.Area > 0:
volume += sol.Volume
fills.append(sol)
obj.FillVolume = volume
if len(fills) > 0:
base = fills.pop(0)
if len(fills) > 0:
base = base.fuse(fills)
return common, base
else:
obj.FillVolume = 0
print("--- Fill: no common Area --------------------------")
return None, None
def calculateCut(self, obj, solid):
common = solid.common(PVPlantSite.get().Terrain.Shape)
if common.Area > 0:
sp = splitter.slice(solid, [common, ], "Split")
shells = []
volume = 0
commoncopy = common.copy()
commoncopy.Placement.Base.z -= 1
for sol in sp.Solids:
common1 = sol.common(commoncopy)
if common1.Area > 0:
volume += sol.Volume
shell = sol.Shells[0]
shell = shell.cut(common)
shells.append(shell)
obj.CutVolume = volume
if len(shells) > 0:
base = shells.pop(0)
if len(shells) > 0:
base = base.fuse(shells)
return common, base
else:
obj.CutVolume = 0
print("--- Cut: no common Area --------------------------")
return None, None
class ViewProviderPad(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "pad.svg"))
class _PadTaskPanel:
def __init__(self, obj=None):
if obj is None:
self.new = True
self.obj = makeTrench()
else:
self.new = False
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantTrench.ui"))
def accept(self):
FreeCAD.ActiveDocument.openTransaction("Create Pad")
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
if self.new:
FreeCADGui.Control.closeDialog()
return True
import sys
from PySide.QtCore import QT_TRANSLATE_NOOP
import draftutils.utils as utils
import draftutils.gui_utils as gui_utils
import draftutils.todo as todo
import draftguitools.gui_base_original as gui_base_original
import draftguitools.gui_tool_utils as gui_tool_utils
from draftutils.translate import translate
class _CommandPad(gui_base_original.Creator):
"""Gui command for the Line tool."""
def __init__(self):
# super(_CommandTrench, self).__init__()
gui_base_original.Creator.__init__(self)
self.path = None
self.obj = None
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap': str(os.path.join(DirIcons, "pad.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantPad", "Pad"),
'Accel': "C, P",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlantPad",
"Creates a Pad object from setup dialog.")}
def Activated(self, name=translate("draft", "Line")):
"""Execute when the command is called."""
sel = FreeCADGui.Selection.getSelection()
base = None
needbase = True
if len(sel) > 0:
needbase = False
base = sel[0]
self.obj = makePad(base)
if needbase:
gui_base_original.Creator.Activated(self, name=translate("draft", "Line"))
self.ui.wireUi(name)
self.ui.setTitle("Pad")
#self.obj = self.doc.addObject("Part::Feature", self.featureName)
#gui_utils.format_object(self.obj)
self.call = self.view.addEventCallback("SoEvent", self.action)
def action(self, arg):
"""Handle the 3D scene events.
This is installed as an EventCallback in the Inventor view.
Parameters
----------
arg: dict
Dictionary with strings that indicates the type of event received
from the 3D view.
"""
print(self.obj)
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
self.finish()
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
elif arg["Type"] == "SoLocation2Event":
self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg)
gui_tool_utils.redraw3DView()
self.obj.Placement.Base = FreeCAD.Vector(self.info["x"], self.info["y"], self.info["z"])
elif (arg["Type"] == "SoMouseButtonEvent"
and arg["State"] == "DOWN"
and arg["Button"] == "BUTTON1"):
gui_tool_utils.getSupport(arg)
self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg)
if self.point:
self.point = FreeCAD.Vector(self.info["x"], self.info["y"], self.info["z"])
self.ui.redraw()
self.obj.Placement.Base = FreeCAD.Vector(self.info["x"], self.info["y"], self.info["z"])
self.finish()
FreeCAD.ActiveDocument.recompute()
def finish(self, closed=False, cont=False):
"""Terminate the operation and close the polyline if asked.
Parameters
----------
closed: bool, optional
Close the line if `True`.
"""
# super(_CommandTrench, self).finish()
gui_base_original.Creator.finish(self)
if self.ui and self.ui.continueMode:
self.Activated()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantPad', _CommandPad())

1132
PVPlantPlacement.py Normal file

File diff suppressed because it is too large Load Diff

519
PVPlantPlacement.ui Normal file
View File

@@ -0,0 +1,519 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Javier Braña</author>
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>388</width>
<height>576</height>
</rect>
</property>
<property name="windowTitle">
<string>Park Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="groupCorridor">
<property name="title">
<string>Corridor settings</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Row amount</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Vertical distance</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editRowGap">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string/>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Column amount</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Horizontal distance</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editColGap">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string/>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editRowCount">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editColCount">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="buttonPVArea">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="buttonAddFrame">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRemoveFrame">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Area:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editPVArea"/>
</item>
<item row="5" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Configuración</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="8" column="0">
<widget class="QLabel" name="label_9">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Offset Vertical</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Orientación</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="cbAlignFrames">
<property name="text">
<string>Alinear estructuras</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Espacio entre filas</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Dirección Horizontal</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Offset Horizontal</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Pitch</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_11">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Dirección Vertical</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboOrientation">
<item>
<property name="text">
<string>Norte - Sur</string>
</property>
</item>
<item>
<property name="text">
<string>Este - Oeste</string>
</property>
</item>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editGapCols">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="comboDirH">
<item>
<property name="text">
<string>De izquierda a derecha</string>
</property>
</item>
<item>
<property name="text">
<string>De derecha a izquiera</string>
</property>
</item>
<item>
<property name="text">
<string>De centro a los lados</string>
</property>
</item>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="comboDirV">
<item>
<property name="text">
<string>De arriba a abajo</string>
</property>
</item>
<item>
<property name="text">
<string>De abajo a arriba</string>
</property>
</item>
<item>
<property name="text">
<string>Del centro a los lados</string>
</property>
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="editOffsetHorizontal">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>-10000.000000000000000</double>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QDoubleSpinBox" name="editOffsetVertical">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>-10000.000000000000000</double>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="editGapRows">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignTop">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Estructura:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QListWidget" name="listFrameSetups">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>54</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonAddFrame</tabstop>
<tabstop>buttonRemoveFrame</tabstop>
<tabstop>editPVArea</tabstop>
<tabstop>buttonPVArea</tabstop>
<tabstop>comboOrientation</tabstop>
<tabstop>editGapCols</tabstop>
<tabstop>editGapRows</tabstop>
<tabstop>comboDirH</tabstop>
<tabstop>comboDirV</tabstop>
<tabstop>editOffsetHorizontal</tabstop>
<tabstop>editOffsetVertical</tabstop>
<tabstop>cbAlignFrames</tabstop>
<tabstop>groupCorridor</tabstop>
<tabstop>editColCount</tabstop>
<tabstop>editColGap</tabstop>
<tabstop>editRowCount</tabstop>
<tabstop>editRowGap</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

161
PVPlantPlacementAdjust.ui Normal file
View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Javier Braña</author>
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>210</height>
</rect>
</property>
<property name="windowTitle">
<string>Park Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Configuración</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="comboDirH">
<item>
<property name="text">
<string>De izquierda a derecha</string>
</property>
</item>
<item>
<property name="text">
<string>De derecha a izquiera</string>
</property>
</item>
<item>
<property name="text">
<string>De centro a los lados</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboMethod">
<item>
<property name="text">
<string>Individual</string>
</property>
</item>
<item>
<property name="text">
<string>Conjunto</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Dirección Vertical</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Dirección Horizontal</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Método:</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QComboBox" name="comboDirV">
<item>
<property name="text">
<string>De arriba a abajo</string>
</property>
</item>
<item>
<property name="text">
<string>De abajo a arriba</string>
</property>
</item>
<item>
<property name="text">
<string>Del centro a los lados</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>GroupBox</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QSpinBox" name="editStepSize">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>250</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Escalón</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="checkTrenck">
<property name="text">
<string>Ajustal a la media de la curva</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>comboMethod</tabstop>
<tabstop>comboDirH</tabstop>
<tabstop>comboDirV</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>Convertir a...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="buttonTo">
<property name="text">
<string>Sel</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>1. Primero seleccióna el objeto al que quieres transformar
2. Selecciiona todos los objetos que quieres tansfomrar</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Objeto al que quieres transformar</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTo"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

93
PVPlantRackChecking.py Normal file
View File

@@ -0,0 +1,93 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Frames"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import os
from PVPlantResources import DirIcons as DirIcons
def checkSingleTracker(frame, val):
doc = FreeCAD.ActiveDocument
if val:
if frame.AngleY < doc.MaximumTiltNegative.Value:
frame.ViewObject.ShapeColor = doc.MaximumTiltNegativeColor
elif frame.AngleY > doc.MaximumTiltPositive.Value:
frame.ViewObject.ShapeColor = doc.MaximumTiltPositiveColor
else:
frame.ViewObject.ShapeColor = frame.Setup.ViewObject.ShapeColor
else:
frame.ViewObject.ShapeColor = frame.Setup.ViewObject.ShapeColor
def checkTrackers(val):
from Utils.PVPlantUtils import findObjects
tlist = findObjects("Tracker")
for obj in tlist:
checkSingleTracker(obj, val)
class CommandRackCheck:
def GetResources(self):
checked = False
if hasattr(FreeCAD.ActiveDocument, "FramesChecking"):
checked = FreeCAD.ActiveDocument.FramesChecking
return {'Pixmap': str(os.path.join(DirIcons, "checked.png")),
'MenuText': "Tracker slope checker",
'Accel': "R, C",
'ToolTip': "Tracker slope checker.",
'CmdType': "NoTransaction",
'Checkable': checked}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self, val):
val = bool(val)
if not hasattr(FreeCAD.ActiveDocument, "FramesChecking"):
''' Abrir dialogo '''
else:
FreeCAD.ActiveDocument.FramesChecking = val
checkTrackers(val)
return
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantRackCheck', CommandRackCheck())

274
PVPlantRackFixedPiling.ui Normal file
View File

@@ -0,0 +1,274 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>363</width>
<height>596</height>
</rect>
</property>
<property name="windowTitle">
<string>Piling</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="groupBreadthways">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>210</height>
</size>
</property>
<property name="title">
<string>Distancias a lo ancho</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Número de postes:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editBreadthwaysNumOfPost">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="tableBreadthwaysPosts">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<row>
<property name="text">
<string>1</string>
</property>
</row>
<row>
<property name="text">
<string>2</string>
</property>
</row>
<row>
<property name="text">
<string>3</string>
</property>
</row>
<column>
<property name="text">
<string>Tramo</string>
</property>
</column>
<column>
<property name="text">
<string>Distancia</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string notr="true">Side - 1</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>NoItemFlags</set>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string notr="true"/>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>1 - 2</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>2 - Side</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string/>
</property>
<property name="flags">
<set>NoItemFlags</set>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupAlong">
<property name="minimumSize">
<size>
<width>0</width>
<height>350</height>
</size>
</property>
<property name="title">
<string>Distancias a lo largo</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Número de postes:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editAlongNumOfPost">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="tableAlongPosts">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<row>
<property name="text">
<string>1</string>
</property>
</row>
<row>
<property name="text">
<string>2</string>
</property>
</row>
<row>
<property name="text">
<string>3</string>
</property>
</row>
<column>
<property name="text">
<string>Tramo</string>
</property>
</column>
<column>
<property name="text">
<string>Distancia</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string notr="true">Side - 1</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>NoItemFlags</set>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string notr="true"/>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>1 - 2</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>2 - Side</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string/>
</property>
<property name="flags">
<set>NoItemFlags</set>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

11
PVPlantResources.py Normal file
View File

@@ -0,0 +1,11 @@
import os
import FreeCAD
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), 'Mod', 'PVPlant')
DirResources = os.path.join(__dir__, 'Resources')
DirIcons = os.path.join(DirResources, 'Icons')
DirImages = os.path.join(DirResources, 'Images')
DirDocuments = os.path.join(DirResources, 'Documents')
DirDDDDocuments = os.path.join(DirResources, '3dObjects')

616
PVPlantRoad.py Normal file
View File

@@ -0,0 +1,616 @@
import FreeCAD
import ArchComponent
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import Part
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Road"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def makeRoad(base=None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Road")
_Road(obj)
_ViewProviderRoad(obj.ViewObject)
obj.Base = base
from Project.Area import PVPlantArea
offset = PVPlantArea.makeOffsetArea(obj, 4000)
PVPlantArea.makeProhibitedArea(offset)
return obj
class _Road(ArchComponent.Component):
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setProperties(obj)
self.Type = "Road"
obj.Proxy = self
self.route = False
obj.IfcType = "Civil Element" ## puede ser: Cable Carrier Segment
obj.setEditorMode("IfcType", 1)
self.count = 0
def setProperties(self, obj):
# Definicion de Propiedades:
'''[
'App::PropertyBool',
'App::PropertyBoolList',
'App::PropertyFloat',
'App::PropertyFloatList',
'App::PropertyFloatConstraint',
'App::PropertyPrecision',
'App::PropertyQuantity',
'App::PropertyQuantityConstraint',
'App::PropertyAngle',
'App::PropertyDistance',
'App::PropertyLength',
'App::PropertyArea',
'App::PropertyVolume',
'App::PropertyFrequency',
'App::PropertySpeed',
'App::PropertyAcceleration',
'App::PropertyForce',
'App::PropertyPressure',
'App::PropertyVacuumPermittivity',
'App::PropertyInteger',
'App::PropertyIntegerConstraint',
'App::PropertyPercent',
'App::PropertyEnumeration',
'App::PropertyIntegerList',
'App::PropertyIntegerSet',
'App::PropertyMap',
'App::PropertyString',
'App::PropertyPersistentObject',
'App::PropertyUUID',
'App::PropertyFont',
'App::PropertyStringList',
'p::PropertyLink',
'App::PropertyLinkChild',
'App::PropertyLinkGlobal',
'App::PropertyLinkHidden',
'App::PropertyLinkSub',
'App::PropertyLinkSubChild',
'App::PropertyLinkSubGlobal',
'App::PropertyLinkSubHidden',
'App::PropertyLinkList',
'App::PropertyLinkListChild',
'App::PropertyLinkListGlobal',
'App::PropertyLinkListHidden',
'App::PropertyLinkSubList',
'App::PropertyLinkSubListChild',
'App::PropertyLinkSubListGlobal',
'App::PropertyLinkSubListHidden',
'App::PropertyXLink',
'App::PropertyXLinkSub',
'App::PropertyXLinkSubList',
'App::PropertyXLinkList',
'App::PropertyMatrix',
'App::PropertyVector',
'App::PropertyVectorDistance',
'App::PropertyPosition',
'App::PropertyDirection',
'App::PropertyVectorList',
'App::PropertyPlacement',
'App::PropertyPlacementList',
'App::PropertyPlacementLink',
'App::PropertyColor',
'App::PropertyColorList',
'App::PropertyMaterial',
'App::PropertyMaterialList',
'App::PropertyPath',
'App::PropertyFile',
'App::PropertyFileIncluded',
'App::PropertyPythonObject',
'App::PropertyExpressionEngine',
'Part::PropertyPartShape',
'Part::PropertyGeometryList',
'Part::PropertyShapeHistory',
'Part::PropertyFilletEdges',
'Mesh::PropertyNormalList',
'Mesh::PropertyCurvatureList',
'Mesh::PropertyMeshKernel',
'Sketcher::PropertyConstraintList'
]'''
obj.addProperty("App::PropertyPercent",
"SurfaceSlope",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).SurfaceSlope = 2
obj.addProperty("App::PropertyPercent",
"SurfaceDrainSlope",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).SurfaceDrainSlope = int(3 / 2 * 100)
obj.addProperty("App::PropertyPercent",
"SubbaseDrainSlope",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).SubbaseDrainSlope = int(2 / 3 * 100)
obj.addProperty("App::PropertyLength",
"Width",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).Width = 4000
obj.addProperty("App::PropertyLength",
"Height",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).Height = 250
obj.addProperty("App::PropertyLength",
"Subbase",
"Road",
QT_TRANSLATE_NOOP("App::Property", "Connection")).Subbase = 400
def onDocumentRestored(self, obj):
"""Method run when the document is restored.
Re-adds the Arch component, and object properties."""
ArchComponent.Component.onDocumentRestored(self, obj)
self.obj = obj
self.Type = "Road"
obj.Proxy = self
def execute(self, obj):
import Part, math
w = obj.Base.Shape
profiles = []
SurfaceDrainSlope = obj.SurfaceDrainSlope / 100
SubbaseDrainSlope = obj.SubbaseDrainSlope / 100
vec_up_left = FreeCAD.Vector(-obj.Width.Value / 2, 0, obj.Height.Value)
vec_up_center = FreeCAD.Vector(0, 0, obj.SurfaceSlope * obj.Width.Value / 200 + obj.Height.Value)
vec_up_right = FreeCAD.Vector(obj.Width.Value / 2, 0, obj.Height.Value)
vec_down_left = FreeCAD.Vector(-(obj.Width.Value / 2 + obj.Height.Value / SurfaceDrainSlope), 0, 0)
vec_down_right = FreeCAD.Vector((obj.Width.Value / 2 + obj.Height.Value / SurfaceDrainSlope), 0, 0)
vec_sand_left = FreeCAD.Vector(-(obj.Width.Value / 2 + obj.Height.Value * (1 / SurfaceDrainSlope + SubbaseDrainSlope)), 0, - obj.Subbase.Value)
vec_sand_right = FreeCAD.Vector((obj.Width.Value / 2 + obj.Height.Value * (1 / SurfaceDrainSlope + SubbaseDrainSlope)), 0, - obj.Subbase.Value)
edge1 = Part.makeLine(vec_down_left, vec_down_right)
edge2 = Part.makeLine(vec_down_right, vec_up_right)
edge3 = Part.makeLine(vec_up_right, vec_up_center)
edge4 = Part.makeLine(vec_up_center, vec_up_left)
edge5 = Part.makeLine(vec_up_left, vec_down_left)
edge6 = Part.makeLine(vec_sand_left, vec_sand_right)
edge7 = Part.makeLine(vec_sand_left, vec_down_left)
edge8 = Part.makeLine(vec_sand_right, vec_down_right)
p = Part.Wire([edge1, edge2, edge3, edge4, edge5])
profiles.append(p)
p = Part.Wire([edge6, edge8, edge1, edge7])
profiles.append(p)
shapes = self.makeSolids(obj, profiles, w, (vec_down_right + vec_down_left) / 2)
angle = 30
height = FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax - obj.Height.Value
offset = height / math.tan(math.radians(angle))
'''cutProfile = Part.makePolygon([vec_sand_left, vec_sand_right, vec_sand_right + FreeCAD.Vector(offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax),
vec_sand_left + FreeCAD.Vector(-offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax), vec_sand_left])
height = obj.Height.Value - FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin
offset = height / math.tan(math.radians(angle))
fillProfile = Part.makePolygon([vec_sand_left, vec_sand_right, vec_sand_right + FreeCAD.Vector(offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin),
vec_sand_left + FreeCAD.Vector(-offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin), vec_sand_left])
cutshapes, fillshapes = self.makeSolids(obj, [cutProfile, fillProfile], w, (vec_up_right + vec_up_left) / 2)
cuts = self.calculateCut(obj, cutshapes)
fills = self.calculateFill(obj, fillshapes)
if cuts:
for cut in cuts:
Part.show(cut, "RoadCut")
if fills:
for fill in fills:
Part.show(fill, "RoadFill")'''
obj.Shape = Part.makeCompound(shapes)
def makeSolids(self, obj, profiles, w, origen):
import Draft
import DraftGeomUtils
shapes = []
for p in profiles:
if hasattr(p, "CenterOfMass"):
c = p.CenterOfMass
else:
c = p.BoundBox.Center
c = origen
delta = w.Vertexes[0].Point - c
p.translate(delta)
if Draft.getType(obj.Base) == "BezCurve":
v1 = obj.Base.Placement.multVec(obj.Base.Points[1]) - w.Vertexes[0].Point
else:
v1 = w.Vertexes[1].Point - w.Vertexes[0].Point
v2 = DraftGeomUtils.getNormal(p)
rot = FreeCAD.Rotation(v2, v1)
#p.rotate(w.Vertexes[0].Point, rot.Axis, math.degrees(rot.Angle))
ang = rot.toEuler()[0]
p.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), ang)
if p.Faces:
for f in p.Faces:
sh = w.makePipeShell([f.OuterWire], True, False, 2)
for shw in f.Wires:
if shw.hashCode() != f.OuterWire.hashCode():
sh2 = w.makePipeShell([shw], True, False, 2)
sh = sh.cut(sh2)
shapes.append(sh)
elif p.Wires:
for pw in p.Wires:
sh = w.makePipeShell([pw], True, False, 2)
shapes.append(sh)
return shapes
def calculateFill(self, obj, solid):
import BOPTools.SplitAPI as splitter
common = solid.common(FreeCAD.ActiveDocument.Site.Terrain.Shape)
if common.Area > 0:
sp = splitter.slice(solid, [common, ], "Split")
common.Placement.Base.z += 1
solids = []
for sol in sp.Solids:
common1 = sol.common(common)
if common1.Area > 0:
solids.append(sol)
if len(solids) > 0:
return solids
return None
def calculateCut(self, obj, solid):
import BOPTools.SplitAPI as splitter
common = solid.common(FreeCAD.ActiveDocument.Site.Terrain.Shape)
if common.Area > 0:
sp = splitter.slice(solid, [common, ], "Split")
shells = []
commoncopy = common.copy()
commoncopy.Placement.Base.z -= 1
for sol in sp.Solids:
common1 = sol.common(commoncopy)
if common1.Area > 0:
shell = sol.Shells[0]
shell = shell.cut(common)
shells.append(shell)
if len(shells) > 0:
return shells
return None
def makeLoft(self, profile):
return
class _ViewProviderRoad(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(PVPlantResources.DirIcons, "road.svg"))
class _RoadTaskPanel:
def __init__(self, obj=None):
if obj is None:
self.new = True
self.obj = makeRoad()
else:
self.new = False
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantRoad.ui"))
def accept(self):
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
if self.new:
FreeCADGui.Control.closeDialog()
return True
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
import FreeCADGui as Gui
import DraftVecUtils
import draftutils.utils as utils
import draftutils.gui_utils as gui_utils
import draftutils.todo as todo
import draftguitools.gui_base_original as gui_base_original
import draftguitools.gui_tool_utils as gui_tool_utils
from draftutils.messages import _msg
from draftutils.translate import translate
class _CommandRoad(gui_base_original.Creator):
"""Gui command for the Line tool."""
def __init__(self):
# super(_CommandRoad, self).__init__()
gui_base_original.Creator.__init__(self)
self.path = None
def GetResources(self):
"""Set icon, menu and tooltip."""
return {'Pixmap': str(os.path.join(DirIcons, "road.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantRoad", "Road"),
'Accel': "C, R",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlantRoad",
"Creates a Road object from setup dialog.")}
def Activated(self, name=translate("draft", "Line")):
"""Execute when the command is called."""
gui_base_original.Creator.Activated(self, name=translate("draft", "Line"))
self.obj = None # stores the temp shape
self.oldWP = None # stores the WP if we modify it
sel = FreeCADGui.Selection.getSelection()
done = False
self.existing = []
if len(sel) > 0:
print("Crear una carretera a lo largo de un trayecto")
# TODO: chequear que el objeto seleccionado sea un "wire"
import Draft
if Draft.getType(sel[0]) == "Wire":
self.path = sel[0]
done = True
if not done:
self.ui.wireUi(name)
self.ui.setTitle("Road")
self.obj = self.doc.addObject("Part::Feature", self.featureName)
gui_utils.format_object(self.obj)
self.call = self.view.addEventCallback("SoEvent", self.action)
_msg(translate("draft", "Pick first point"))
def action(self, arg):
"""Handle the 3D scene events.
This is installed as an EventCallback in the Inventor view.
Parameters
----------
arg: dict
Dictionary with strings that indicates the type of event received
from the 3D view.
"""
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
self.finish()
elif arg["Type"] == "SoLocation2Event":
self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg)
gui_tool_utils.redraw3DView()
elif (arg["Type"] == "SoMouseButtonEvent"
and arg["State"] == "DOWN"
and arg["Button"] == "BUTTON1"):
if arg["Position"] == self.pos:
return self.finish(False, cont=True)
if (not self.node) and (not self.support):
gui_tool_utils.getSupport(arg)
self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg)
if self.point:
self.point = FreeCAD.Vector(self.info["x"], self.info["y"], self.info["z"])
self.ui.redraw()
self.pos = arg["Position"]
self.node.append(self.point)
self.drawSegment(self.point)
if len(self.node) > 2:
# The wire is closed
if (self.point - self.node[0]).Length < utils.tolerance():
self.undolast()
if len(self.node) > 2:
self.finish(True, cont=True)
else:
self.finish(False, cont=True)
def finish(self, closed=False, cont=False):
"""Terminate the operation and close the polyline if asked.
Parameters
----------
closed: bool, optional
Close the line if `True`.
"""
self.removeTemporaryObject()
if self.oldWP:
App.DraftWorkingPlane = self.oldWP
if hasattr(Gui, "Snapper"):
Gui.Snapper.setGrid()
Gui.Snapper.restack()
self.oldWP = None
if len(self.node) > 1:
if False:
Gui.addModule("Draft")
# The command to run is built as a series of text strings
# to be committed through the `draftutils.todo.ToDo` class.
if (len(self.node) == 2
and utils.getParam("UsePartPrimitives", False)):
# Insert a Part::Primitive object
p1 = self.node[0]
p2 = self.node[-1]
_cmd = 'FreeCAD.ActiveDocument.'
_cmd += 'addObject("Part::Line", "Line")'
_cmd_list = ['line = ' + _cmd,
'line.X1 = ' + str(p1.x),
'line.Y1 = ' + str(p1.y),
'line.Z1 = ' + str(p1.z),
'line.X2 = ' + str(p2.x),
'line.Y2 = ' + str(p2.y),
'line.Z2 = ' + str(p2.z),
'Draft.autogroup(line)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Line"),
_cmd_list)
else:
# Insert a Draft line
rot, sup, pts, fil = self.getStrings()
_base = DraftVecUtils.toString(self.node[0])
_cmd = 'Draft.makeWire'
_cmd += '('
_cmd += 'points, '
_cmd += 'placement=pl, '
_cmd += 'closed=' + str(closed) + ', '
_cmd += 'face=' + fil + ', '
_cmd += 'support=' + sup
_cmd += ')'
_cmd_list = ['pl = FreeCAD.Placement()',
'pl.Rotation.Q = ' + rot,
'pl.Base = ' + _base,
'points = ' + pts,
'line = ' + _cmd,
'Draft.autogroup(line)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Wire"),
_cmd_list)
else:
import Draft
self.path = Draft.makeWire(self.node, closed=False, face=False)
# super(_CommandRoad, self).finish()
gui_base_original.Creator.finish(self)
if self.ui and self.ui.continueMode:
self.Activated()
self.makeRoad()
def makeRoad(self):
makeRoad(self.path)
def removeTemporaryObject(self):
"""Remove temporary object created."""
if self.obj:
try:
old = self.obj.Name
except ReferenceError:
# object already deleted, for some reason
pass
else:
todo.ToDo.delay(self.doc.removeObject, old)
self.obj = None
def undolast(self):
"""Undoes last line segment."""
import Part
if len(self.node) > 1:
self.node.pop()
# last = self.node[-1]
if self.obj.Shape.Edges:
edges = self.obj.Shape.Edges
if len(edges) > 1:
newshape = Part.makePolygon(self.node)
self.obj.Shape = newshape
else:
self.obj.ViewObject.hide()
# DNC: report on removal
# _msg(translate("draft", "Removing last point"))
_msg(translate("draft", "Pick next point"))
def drawSegment(self, point):
"""Draws new line segment."""
import Part
if self.planetrack and self.node:
self.planetrack.set(self.node[-1])
if len(self.node) == 1:
_msg(translate("draft", "Pick next point"))
elif len(self.node) == 2:
last = self.node[len(self.node) - 2]
newseg = Part.LineSegment(last, point).toShape()
self.obj.Shape = newseg
self.obj.ViewObject.Visibility = True
_msg(translate("draft", "Pick next point"))
else:
currentshape = self.obj.Shape.copy()
last = self.node[len(self.node) - 2]
if not DraftVecUtils.equals(last, point):
newseg = Part.LineSegment(last, point).toShape()
newshape = currentshape.fuse(newseg)
self.obj.Shape = newshape
_msg(translate("draft", "Pick next point"))
def wipe(self):
"""Remove all previous segments and starts from last point."""
if len(self.node) > 1:
# self.obj.Shape.nullify() # For some reason this fails
self.obj.ViewObject.Visibility = False
self.node = [self.node[-1]]
if self.planetrack:
self.planetrack.set(self.node[0])
_msg(translate("draft", "Pick next point"))
def orientWP(self):
"""Orient the working plane."""
import DraftGeomUtils
if hasattr(App, "DraftWorkingPlane"):
if len(self.node) > 1 and self.obj:
n = DraftGeomUtils.getNormal(self.obj.Shape)
if not n:
n = App.DraftWorkingPlane.axis
p = self.node[-1]
v = self.node[-2].sub(self.node[-1])
v = v.negative()
if not self.oldWP:
self.oldWP = App.DraftWorkingPlane.copy()
App.DraftWorkingPlane.alignToPointAndAxis(p, n, upvec=v)
if hasattr(Gui, "Snapper"):
Gui.Snapper.setGrid()
Gui.Snapper.restack()
if self.planetrack:
self.planetrack.set(self.node[-1])
def numericInput(self, numx, numy, numz):
"""Validate the entry fields in the user interface.
This function is called by the toolbar or taskpanel interface
when valid x, y, and z have been entered in the input fields.
"""
self.point = App.Vector(numx, numy, numz)
self.node.append(self.point)
self.drawSegment(self.point)
self.ui.setNextFocus()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantRoad', _CommandRoad())

105
PVPlantSetMatrixManual.ui Normal file
View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Javier Braña</author>
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>466</width>
<height>568</height>
</rect>
</property>
<property name="windowTitle">
<string>Park Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Columnas:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="buttonFrame">
<property name="text">
<string>Detectar columnas automáticamente</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Columnas:</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
<item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>down</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Filas</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QListWidget" name="listWidget_2"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

1192
PVPlantSite.py Normal file

File diff suppressed because it is too large Load Diff

212
PVPlantStringSetup.ui Normal file
View File

@@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>form</class>
<widget class="QDialog" name="form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>470</width>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
<string>Terrain Analisys</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="1">
<widget class="QLineEdit" name="editFrame">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="buttonSelFrame">
<property name="text">
<string>sel</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Frame information: </string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>5</number>
</property>
<item row="1" column="3">
<widget class="QLineEdit" name="editLeft">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QListWidget" name="listStrings"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="buttonAddString">
<property name="text">
<string>Add String</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Delete String</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="2" rowspan="2" colspan="2">
<widget class="QListWidget" name="listModules"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTotal">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="editUsed">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Totales</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Usados</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Restantes</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Paneles:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Frame type: </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>String Name:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

593
PVPlantStringing.py Normal file
View File

@@ -0,0 +1,593 @@
# /**********************************************************************
# * *
# * 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 math
import ArchComponent
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")
def makeStringSetup():
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringSetup")
_StringSetup(obj)
_ViewProviderStringSetup(obj.ViewObject)
try:
if FreeCAD.ActiveDocument.StringsSetup:
FreeCAD.ActiveDocument.StringsSetup.addObject(obj)
except:
pass
return obj
class _StringSetup:
def __init__(self, obj):
self.setCommonProperties(obj)
self.obj = obj
self.StringCount = 1
def setCommonProperties(self, obj):
pl = obj.PropertiesList
if not ("NumberOfStrings" in pl):
obj.addProperty("App::PropertyInteger",
"NumberOfStrings",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).NumberOfStrings = 0
obj.setEditorMode("NumberOfStrings", 1)
self.Type = "StringSetup"
obj.Proxy = self
def onDocumentRestored(self, obj):
self.setProperties(obj)
def addString(self, modulelist):
stringName = "String" + str(self.StringCount)
self.obj.addProperty("App::PropertyIntegerList",
stringName,
"Setup",
"String: " + stringName
)
setattr(self.obj, stringName, modulelist)
'''
self.obj.addProperty("App::PropertyInteger",
stringName + "_Power",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
setattr(self.obj, stringName + "_Power", len(modulelist) * 450)
'''
self.obj.NumberOfStrings = self.StringCount
self.StringCount += 1
class _ViewProviderStringSetup:
def __init__(self, vobj):
'''
Set view properties.
'''
self.Object = vobj.Object
vobj.Proxy = self
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
self.Object = vobj.Object
return
def getIcon(self):
'''
Return object treeview icon.
'''
return str(os.path.join(DirIcons, "stringsetup.svg"))
'''
def claimChildren(self):
"""
Provides object grouping
"""
return self.Object.Group
'''
def setEdit(self, vobj, mode=0):
"""
Enable edit
"""
return True
def unsetEdit(self, vobj, mode=0):
"""
Disable edit
"""
return False
def doubleClicked(self, vobj):
"""
Detect double click
"""
pass
def setupContextMenu(self, obj, menu):
"""
Context menu construction
"""
pass
def edit(self):
"""
Edit callback
"""
pass
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self,state):
"""
Get variables from file.
"""
return None
class _SelObserver:
def __init__(self, form):
self.form = form
def addSelection(self, doc, obj, sub, pnt):
rack = FreeCAD.ActiveDocument.getObjectsByLabel(obj)[0].Shape
modules = rack.SubShapes[0].SubShapes[0].SubShapes
if sub[0:4] == 'Face':
numFace = int(sub.replace('Face', '')) - 1
selFace = rack.Faces[numFace]
for module in modules:
for num, face in enumerate(module.Faces):
if selFace.isSame(face):
self.form.setModule(modules.index(module))
FreeCADGui.Selection
return True
elif sub[0:4] == 'Edge':
numEdge = int(sub.replace('Edge', '')) - 1
selEdge = rack.Edge[numEdge]
for module in modules:
for num, edge in enumerate(module.Edges):
if selEdge.isSame(edge):
print("Encontrado: ", modules.index(module))
return True
return True
def removeSelection(self, doc, obj, sub): # Delete the selected object
'''print("FSO-RemSel:" + str(obj) + ":" + str(sub) + "\n")'''
return True
def setSelection(self, doc): # Selection in ComboView
'''print("FSO-SetSel:" + "\n")'''
return True
def clearSelection(self, doc): # If click on the screen, clear the selection
'''print("FSO-ClrSel:" + "\n")'''
return True
class _StringSetupPanel:
def __init__(self, obj=None):
self.obj = obj
self.new = False
if obj is None:
self.new = True
self.obj = makeStringSetup()
self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantStringSetup.ui")
self.form.buttonSelFrame.clicked.connect(self.selFrame)
self.form.buttonAddString.clicked.connect(self.addString)
self.form.listStrings.currentRowChanged.connect(self.listStringsCurrentRowChanged)
self.form.editName.editingFinished.connect(self.setStringName)
self.selobserver = _SelObserver(self)
self.stringslist = []
self.currentstring = 0
self.totalModules = 0
self.left = 0
self.frameColor = None
def selFrame(self):
sel = FreeCADGui.Selection.getSelection()
frame = None
if len(sel) == 0:
# TODO: lanzar un error
return
elif len(sel) == 1:
frame = sel[0]
else:
for obj in sel:
if not hasattr(obj, 'Proxy'):
continue
print(obj.Proxy.__class__)
print(issubclass(obj.Proxy.__class__, PVPlantRack._Frame))
if issubclass(obj.Proxy.__class__, PVPlantRack._Frame):
frame = obj
break
if frame == None:
# TODO: lanzar un error
print("No frame selected")
return
self.frame = frame
self.form.editFrame.setText(frame.Label)
self.totalModules = frame.ModuleCols * frame.ModuleRows
self.left = self.totalModules
self.form.editTotal.setText(str(int(self.totalModules)))
self.form.editUsed.setText("0")
self.form.editLeft.setText(str(int(self.left)))
FreeCADGui.Selection.removeObserver(self.selobserver)
FreeCADGui.Selection.addObserver(self.selobserver)
self.frameColor = self.obj.ViewObject.ShapeColor
self.colorlist = []
for face in self.frame.Shape.Faces:
self.colorlist.append((1.0, 0.50, 0.40, 0.25))
self.frame.ViewObject.DiffuseColor = self.colorlist
def addString(self):
FreeCADGui.Selection.clearSelection()
self.stringslist.append([])
self.currentstring = len(self.stringslist) - 1
self.form.listStrings.addItem("String" + str(self.form.listStrings.count()))
self.form.listStrings.setCurrentRow(self.form.listStrings.count() - 1)
def listStringsCurrentRowChanged(self, row):
self.currentstring = row
self.form.listModules.clear()
for num in self.stringslist[row]:
self.form.listModules.addItem(str(num))
def setModule(self, numModule):
if len(self.stringslist) == 0:
return
if self.left == 0:
return
if numModule in self.stringslist[self.currentstring]:
'''remove module ?? '''
self.stringslist[self.currentstring].remove(int(numModule))
item = self.form.listModules.findItems(str(numModule), QtCore.Qt.MatchExactly)[0]
self.form.listModules.takeItem(self.form.listModules.row(item))
for moduleFace in self.frame.Shape.Solids[numModule].Faces:
for i, face in enumerate(self.frame.Shape.Faces):
if moduleFace.isEqual(face):
self.colorlist[i] = (1.0, 0.50, 0.40, 0.25)
self.frame.ViewObject.DiffuseColor = self.colorlist
break
else:
self.stringslist[self.currentstring].append(int(numModule))
self.form.listModules.addItem(str(numModule))
for moduleFace in self.frame.Shape.Solids[numModule].Faces:
for i, face in enumerate(self.frame.Shape.Faces):
if moduleFace.isEqual(face):
self.colorlist[i] = (0.0, 0.0, 1.0, 0.0)
self.frame.ViewObject.DiffuseColor = self.colorlist
break
self.calculateModules()
def calculateModules(self):
num = 0
for string in self.stringslist:
num += len(string)
self.left = int(self.totalModules - num)
self.form.editUsed.setText(str(num))
self.form.editLeft.setText(str(self.left))
def setStringName(self):
self.obj.Label = self.form.editName.text()
def accept(self):
for string in self.stringslist:
self.obj.Proxy.addString(string.copy())
self.FormClosing()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
self.FormClosing()
return True
def FormClosing(self):
FreeCADGui.Selection.removeObserver(self.selobserver)
FreeCADGui.Control.closeDialog()
self.frame.ViewObject.DiffuseColor = self.frameColor
def makeString(base=None):
if base is None:
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "String")
_String(obj)
obj.Frame = base
_ViewProviderString(obj.ViewObject)
if FreeCAD.ActiveDocument.Strings:
FreeCAD.ActiveDocument.Strings.addObject(obj)
FreeCAD.ActiveDocument.recompute()
return obj
class _String(ArchComponent.Component):
def __init__(self, obj):
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
obj.Proxy = self
obj.IfcType = "Cable Segment"
obj.setEditorMode("IfcType", 1)
def setProperties(self, obj):
pl = obj.PropertiesList
if not ("Frame" in pl):
obj.addProperty("App::PropertyLink",
"Frame",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Frame = None
if not ("StringSetup" in pl):
obj.addProperty("App::PropertyLink",
"StringSetup",
"Setup",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).StringSetup = None
if not ("StringPoles" in pl):
obj.addProperty("App::PropertyVectorList",
"StringPoles",
"StringOutput",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).StringPoles = []
obj.setEditorMode("StringPoles", 1)
self.Type = "String"
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
obj.Proxy = self
def __getstate__(self):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
def onBeforeChange(self, obj, prop):
''''''
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
def getDownFace(module):
area_max = max([face.Area for face in module.Faces])
faces = []
for face in module.Faces:
if face.Area == area_max:
faces.append(face)
return faces[0] if faces[0].Placement.Base.z > faces[1].Placement.Base.z else faces[1]
if (prop == "Frame") or (prop == "StringSetup"):
if not (obj.Frame is None) and not (obj.StringSetup is None):
JuntionBoxPosition = 200
portrait = obj.Frame.ModuleOrientation == "Portrait"
cableLength = 1200
if hasattr(obj.Frame, "PoleCableLength"):
cableLength = obj.Frame.PoleCableLength.Value
moduleWidth = obj.Frame.ModuleWidth.Value
moduleHeight = obj.Frame.ModuleHeight.Value
dist_x = JuntionBoxPosition + obj.Frame.ModuleColGap.Value + (
moduleWidth if portrait else moduleHeight) / 2
dist_y = obj.Frame.ModuleRowGap.Value + (moduleHeight if portrait else moduleWidth)
PolePosition = 0
if portrait:
PolePosition = moduleWidth / 2 - JuntionBoxPosition
FrameModules = obj.Frame.Shape.SubShapes[0].SubShapes[0].SubShapes
cableProfile = Part.Face(Part.Wire(Part.Circle(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(1, 0, 0), 6).toShape()))
positiveMC4 = Part.Shape()
positiveMC4.read(os.path.join(Dir3dObjects, "MC4 POSITIVE.IGS"))
negativeMC4 = Part.Shape()
negativeMC4.read(os.path.join(Dir3dObjects, "MC4 NEGATIVE.IGS"))
vec = None
if obj.Frame.Route:
vertexes = obj.Frame.Route.Shape.Vertexes
vec = vertexes[1].Point - vertexes[0].Point
else:
poles = obj.Frame.Shape.SubShapes[1].SubShapes
vec = poles[1].BoundBox.Center - poles[0].BoundBox.Center
vecp= FreeCAD.Vector(-vec.y, vec.x, vec.z)
# V1:
for stringnum in range(obj.StringSetup.NumberOfStrings):
''''''
return
# V0:
for stringnum in range(obj.StringSetup.NumberOfStrings):
string = obj.StringSetup.getPropertyByName("String" + str(stringnum + 1))
total = len(string) - 1
dir = 0
ndir = 0
# dirvec = FrameModules(string[-]).Placement.Base - FrameModules(string[0]).Placement.Base
for i, num in enumerate(string):
face = getDownFace(FrameModules[num])
if i < total:
dir = string[i + 1] - num
dir /= abs(dir)
ndir = -dir
positivePolePosition = FreeCAD.Vector(0, ndir * PolePosition, 0) + face.CenterOfMass
negativePolePosition = FreeCAD.Vector(0, dir * PolePosition, 0) + face.CenterOfMass
# dibujar +:
p1 = positivePolePosition
p2 = p1 + ndir * FreeCAD.Vector(0, 50, 0)
p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0)
p4 = p3 + ndir * FreeCAD.Vector(0, dist_x - 50, 0)
w = Part.makePolygon([p1, p2, p3, p4])
#cableProfile.Placement.Base = p1
#cable = w.makePipeShell([cableProfile], True, False, 2)
Part.show(w)
mc4copy = positiveMC4.copy()
mc4copy.Placement.Base = p4 + FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0)
positiveMC4.Placement.Rotation.Angle = math.radians(ndir * 90)
Part.show(mc4copy)
# dibujar -:
p1 = negativePolePosition
p2 = p1 + dir * FreeCAD.Vector(0, 50, 0)
p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0)
p4 = p3 + dir * FreeCAD.Vector(0, dist_x - 50, 0)
w = Part.makePolygon([p1, p2, p3, p4])
Part.show(w)
mc4copy = negativeMC4.copy()
mc4copy.Placement.Base = p4 - FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0)
mc4copy.Placement.Rotation.Angle = math.radians(dir * 90)
Part.show(mc4copy)
if i == 0 or i == total:
list = obj.StringPoles.copy()
list.append(p3)
obj.StringPoles = list
break
def execute(self, obj):
'''Do something when recompute'''
class _ViewProviderString(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(DirIcons, "string.svg"))
class _CommandStringSetup:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "stringsetup.svg")),
'Accel': "E, C",
'MenuText': "String Setup",
'ToolTip': "Configure strings of a Frame"}
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
def Activated(self):
self.TaskPanel = _StringSetupPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return
class _CommandStringing:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "string.svg")),
'Accel': "E, S",
'MenuText': QT_TRANSLATE_NOOP("Placement", "String"),
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Make string on a Frame")}
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
def Activated(self):
# issubclass(Car, Vehicles)
# isinstance(Car, Vehicles)
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
for obj in sel:
if obj.Name.__contains__("Tracker"):
makeString(obj)
if FreeCAD.GuiUp:
class _CommandStringingGroup:
def GetCommands(self):
return tuple(['PVPlantStringSetup',
'PVPlantStringing'
])
def GetResources(self):
return {'MenuText': 'Stringing',
'ToolTip': 'Tools to setup and make strings'
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('PVPlantStringSetup', _CommandStringSetup())
FreeCADGui.addCommand('PVPlantStringing', _CommandStringing())
FreeCADGui.addCommand('Stringing', _CommandStringingGroup())

292
PVPlantSurface.py Normal file
View File

@@ -0,0 +1,292 @@
import copy
import random
import FreeCAD
import Mesh
from freecad.trails import ICONPATH, geo_origin
from pivy import coin
def create(name='Surface'):
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Surface")
obj.Label = name
Surface(obj)
ViewProviderSurface(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
return obj
#class Surface(SurfaceFunc):
class Surface(ArchComponent.Component):
"""
This class is about Surface Object data features.
"""
def __init__(self, obj):
'''
Set data properties.
'''
ArchComponent.Component.__init__(self, obj)
self.Type = 'PVPlant::Surface'
self.setproperties()
obj.Proxy = self
# Does a IfcType exist?
obj.IfcType = "Civil Element"
obj.setEditorMode("IfcType", 1)
def setproperties(self):
# Triangulation properties.
obj.addProperty(
'App::PropertyLinkList', "PointGroups", "Base",
"List of Point Groups").PointGroups = []
obj.addProperty(
"App::PropertyIntegerList", "Delaunay", "Base",
"Index of Delaunay vertices", 4).Delaunay = []
obj.addProperty(
"Mesh::PropertyMeshKernel", "Mesh", "Base",
"Mesh object of triangulation").Mesh = Mesh.Mesh()
obj.addProperty(
"App::PropertyLength", "MaxLength", "Base",
"Maximum length of triangle edge").MaxLength = 50000
obj.addProperty(
"App::PropertyAngle", "MaxAngle", "Base",
"Maximum angle of triangle edge").MaxAngle = 170
# Contour properties.
obj.addProperty(
"App::PropertyFloatConstraint", "ContourInterval", "Contour",
"Size of the point group").ContourInterval = (1.0, 0.0, 100.0, 1.0)
obj.addProperty(
"App::PropertyVectorList", "ContourPoints", "Contour",
"Points of contours", 4).ContourPoints = []
obj.addProperty(
"App::PropertyIntegerList", "ContourVertices", "Contour",
"Vertices of contours.", 4).ContourVertices = []
def onChanged(self, obj, prop):
'''
Do something when a data property has changed.
'''
points = []
pgs = obj.getPropertyByName("PointGroups")
for pg in pgs:
points.extend(pg.Points)
if points:
origin = geo_origin.get(points[0])
else:
origin = geo_origin.get()
if prop == "PointGroups":
if len(points) > 2:
obj.Delaunay = self.triangulate(points)
else:
obj.Mesh = Mesh.Mesh()
if prop == "Delaunay" or prop == "MaxLength" or prop == "MaxAngle":
delaunay = obj.getPropertyByName("Delaunay")
lmax = obj.getPropertyByName("MaxLength")
amax = obj.getPropertyByName("MaxAngle")
if delaunay:
obj.Mesh = self.test_delaunay(
origin.Origin, points, delaunay, lmax, amax)
if prop == "Mesh" or prop == "ContourInterval":
deltaH = obj.getPropertyByName("ContourInterval")
mesh = obj.getPropertyByName("Mesh")
coords, num_vert = self.contour_points(origin.Origin, mesh, deltaH)
obj.ContourPoints = coords
obj.ContourVertices = num_vert
def execute(self, obj):
'''
Do something when doing a recomputation.
'''
pass
class ViewProviderSurface:
"""
This class is about Surface Object view features.
"""
def __init__(self, vobj):
'''
Set view properties.
'''
(r, g, b) = (random.random(), random.random(), random.random())
vobj.addProperty(
"App::PropertyColor", "TriangleColor", "Surface Style",
"Color of the point group").TriangleColor = (r, g, b)
vobj.Proxy = self
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
# GeoCoords Node.
self.geo_coords = coin.SoGeoCoordinate()
# Surface features.
self.triangles = coin.SoIndexedFaceSet()
shape_hints = coin.SoShapeHints()
shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
self.mat_color = coin.SoMaterial()
mat_binding = coin.SoMaterialBinding()
mat_binding.value = coin.SoMaterialBinding.OVERALL
edge_color = coin.SoBaseColor()
edge_color.rgb = (0.5, 0.5, 0.5)
offset = coin.SoPolygonOffset()
# Line style.
line_style = coin.SoDrawStyle()
line_style.style = coin.SoDrawStyle.LINES
line_style.lineWidth = 2
# Contour features.
cont_color = coin.SoBaseColor()
cont_color.rgb = (1, 1, 0)
self.cont_coords = coin.SoGeoCoordinate()
self.cont_lines = coin.SoLineSet()
# Contour root.
contours = coin.SoSeparator()
contours.addChild(cont_color)
contours.addChild(line_style)
contours.addChild(self.cont_coords)
contours.addChild(self.cont_lines)
# Face root.
faces = coin.SoSeparator()
faces.addChild(shape_hints)
faces.addChild(self.mat_color)
faces.addChild(mat_binding)
faces.addChild(self.geo_coords)
faces.addChild(self.triangles)
# Highlight for selection.
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
highlight.style = 'EMISSIVE_DIFFUSE'
highlight.addChild(edge_color)
highlight.addChild(line_style)
highlight.addChild(self.geo_coords)
highlight.addChild(self.triangles)
# Surface root.
surface_root = coin.SoSeparator()
surface_root.addChild(contours)
surface_root.addChild(offset)
surface_root.addChild(faces)
surface_root.addChild(offset)
surface_root.addChild(highlight)
vobj.addDisplayMode(surface_root, "Surface")
# Take features from properties.
self.onChanged(vobj, "TriangleColor")
def onChanged(self, vobj, prop):
'''
Update Object visuals when a view property changed.
'''
try:
if prop == "TriangleColor":
color = vobj.getPropertyByName("TriangleColor")
self.mat_color.diffuseColor = (color[0], color[1], color[2])
except Exception:
pass
def updateData(self, obj, prop):
'''
Update Object visuals when a data property changed.
'''
if prop == "Mesh":
mesh = obj.getPropertyByName("Mesh")
topo_points = mesh.Topology[0]
topo_tri = mesh.Topology[1]
# Get GeoOrigin.
points = []
triangles = []
origin = geo_origin.get()
base = copy.deepcopy(origin.Origin)
base.z = 0
for i in topo_points:
point = copy.deepcopy(i)
points.append(point.add(base))
for i in topo_tri:
triangles.extend(list(i))
triangles.append(-1)
# Set GeoCoords.
geo_system = ["UTM", origin.UtmZone, "FLAT"]
self.geo_coords.geoSystem.setValues(geo_system)
self.geo_coords.point.values = points
# Set contour system.
self.cont_coords.geoSystem.setValues(geo_system)
self.triangles.coordIndex.values = triangles
if prop == "Mesh" or prop == "ContourInterval":
cont_points = obj.getPropertyByName("ContourPoints")
cont_vert = obj.getPropertyByName("ContourVertices")
self.cont_coords.point.values = cont_points
self.cont_lines.numVertices.values = cont_vert
def getDisplayModes(self, vobj):
'''
Return a list of display modes.
'''
modes = []
modes.append("Surface")
return modes
def getDefaultDisplayMode(self):
'''
Return the name of the default display mode.
'''
return "Surface"
def setDisplayMode(self, mode):
'''
Map the display mode defined in attach with
those defined in getDisplayModes.
'''
return mode
def getIcon(self):
'''
Return object treeview icon.
'''
return ICONPATH + '/icons/Surface.svg'
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self, state):
"""
Get variables from file.
"""
return None

695
PVPlantTerrain.py Normal file
View File

@@ -0,0 +1,695 @@
# /**********************************************************************
# * *
# * 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 copy
import ArchComponent
import FreeCAD
import Part
import numpy as np
import math
import PVPlantSite
if FreeCAD.GuiUp:
import FreeCADGui
from pivy import coin
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "FreeCAD Fixed Rack"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
DirResources = os.path.join(__dir__, "Resources")
DirIcons = os.path.join(DirResources, "Icons")
DirImages = os.path.join(DirResources, "Images")
line_patterns = {
"Continues _______________________________": 0xFFFF,
"Border __ . __ __ . __ __ . __ __ . __": 0x3CF2,
"Border (.5x) __.__.__.__.__.__.__.__.__.__._": 0x3939,
"Border (2x) ____ ____ . ____ ____ . _": 0xFDFA,
"Center ____ _ ____ _ ____ _ ____ _ ___": 0xFF3C,
"Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ _": 0xFC78,
"Center (2x) ________ __ ________ __ ___": 0xFFDE,
"Dash dot __ . __ . __ . __ . __ . __ . _": 0xE4E4,
"Dash dot (.5x) _._._._._._._._._._._._._._._._": 0xEBAE,
"Dash dot (2x) ____ . ____ . ____ . ____": 0xFF08,
"Dashed __ __ __ __ __ __ __ __ __ __ _": 0x739C,
"Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _": 0xDB6E,
"Dashed (2x) ____ ____ ____ ____ ____ _": 0xFFE0,
"Divide ____ . . ____ . . ____ . . ____": 0xFF24,
"Divide (.5x) __..__..__..__..__..__..__..__.": 0xEAEA,
"Divide (2x) ________ . . ________ . . ": 0xFFEA,
"Dot . . . . . . . . . . . . . . . .": 0x4924,
"Dot (.5x) ...............................": 0x5555,
"Dot (2x) . . . . . . . . . . .": 0x8888}
def makeTerrain(name="Terrain"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Terrain")
obj.Label = name
Terrain(obj)
ViewProviderTerrain(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
return obj
class Terrain(ArchComponent.Component):
"A Shadow Terrain Obcject"
def __init__(self, obj):
# Definición de variables:
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
self.obj = obj
# Does a IfcType exist?
# obj.IfcType = "Fence"
# obj.MoveWithHost = False
self.site = PVPlantSite.get()
self.site.Terrain = obj
obj.ViewObject.ShapeColor = (0.0000, 0.6667, 0.4980)
obj.ViewObject.LineColor = (0.0000, 0.6000, 0.4392)
def setProperties(self, obj):
# Definicion de Propiedades:
pl = obj.PropertiesList
if not ("CuttingBoundary" in pl):
obj.addProperty("App::PropertyLink",
"CuttingBoundary",
"Surface",
"A boundary line to delimit the surface")
if not ("DEM" in pl):
obj.addProperty("App::PropertyFile",
"DEM",
"Surface",
"Load a ASC file to generate the surface")
if not ("PointsGroup" in pl):
obj.addProperty("App::PropertyLink",
"PointsGroup",
"Surface",
"Use a Point Group to generate the surface")
if not ("Mesh" in pl):
obj.addProperty("Mesh::PropertyMeshKernel",
"Mesh",
"Surface",
"Mesh")
obj.setEditorMode("Mesh", 1)
if not ("InitialMesh" in pl):
obj.addProperty("Mesh::PropertyMeshKernel",
"InitialMesh",
"Surface",
"Mesh")
obj.setEditorMode("InitialMesh", 1)
'''
#obj.setEditorMode("Volume", 1)
if not "AllowedAreas" in pl:
obj.addProperty("App::PropertyLinkList",
"AllowedAreas",
"Areas",
"A boundary to delimitated the terrain").AllowedAreas = []
if not "ProhibitedAreas" in pl:
obj.addProperty("App::PropertyLinkList",
"ProhibitedAreas",
"Areas",
"A boundary to delimitated the terrain").ProhibitedAreas = []
'''
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
if prop == "InitialMesh":
obj.Mesh = obj.InitialMesh.copy()
if prop == "DEM" or prop == "CuttingBoundary":
from datetime import datetime
if obj.DEM and obj.CuttingBoundary:
'''
Parámetro Descripción Requisitos
NCOLS: Cantidad de columnas de celdas Entero mayor que 0.
NROWS: Cantidad de filas de celdas Entero mayor que 0.
XLLCENTER o XLLCORNER: Coordenada X del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada y.
YLLCENTER o YLLCORNER: Coordenada Y del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada x.
CELLSIZE: Tamaño de celda Mayor que 0.
NODATA_VALUE: Los valores de entrada que serán NoData en el ráster de salida Opcional. El valor predeterminado es -9999
'''
grid_space = 1
file = open(obj.DEM, "r")
templist = [line.split() for line in file.readlines()]
file.close()
del file
# Read meta data:
meta = templist[0:6]
nx = int(meta[0][1]) # NCOLS
ny = int(meta[1][1]) # NROWS
xllref = meta[2][0] # XLLCENTER / XLLCORNER
xllvalue = round(float(meta[2][1]), 3)
yllref = meta[3][0] # YLLCENTER / XLLCORNER
yllvalue = round(float(meta[3][1]), 3)
cellsize = round(float(meta[4][1]), 3) # CELLSIZE
nodata_value = float(meta[5][1]) # NODATA_VALUE
# set coarse_factor
coarse_factor = max(round(grid_space / cellsize), 1)
# Get z values
templist = templist[6:(6 + ny)]
templist = [templist[i][0::coarse_factor] for i in np.arange(0, len(templist), coarse_factor)]
datavals = np.array(templist).astype(float)
del templist
# create xy coordinates
import PVPlantSite
offset = PVPlantSite.get().Origin
x = 1000 * (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) - offset.x
y = 1000 * (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) - offset.y
datavals = 1000 * datavals # - offset.z
# remove points out of area
# 1. coarse:
if obj.CuttingBoundary:
inc_x = obj.CuttingBoundary.Shape.BoundBox.XLength * 0.0
inc_y = obj.CuttingBoundary.Shape.BoundBox.YLength * 0.0
tmp = np.where(np.logical_and(x >= (obj.CuttingBoundary.Shape.BoundBox.XMin - inc_x),
x <= (obj.CuttingBoundary.Shape.BoundBox.XMax + inc_x)))[0]
print(tmp)
x_max = np.ndarray.max(tmp)
x_min = np.ndarray.min(tmp)
tmp = np.where(np.logical_and(y >= (obj.CuttingBoundary.Shape.BoundBox.YMin - inc_y),
y <= (obj.CuttingBoundary.Shape.BoundBox.YMax + inc_y)))[0]
y_max = np.ndarray.max(tmp)
y_min = np.ndarray.min(tmp)
del tmp
x = x[x_min:x_max+1]
y = y[y_min:y_max+1]
datavals = datavals[y_min:y_max+1, x_min:x_max+1]
# Create mesh - surface:
import MeshTools.Triangulation as Triangulation
import Mesh
stepsize = 75
stepx = math.ceil(nx / stepsize)
stepy = math.ceil(ny / stepsize)
mesh = Mesh.Mesh()
for indx in range(stepx):
inix = indx * stepsize - 1
finx = min([stepsize * (indx + 1), len(x)-1])
for indy in range(stepy):
iniy = indy * stepsize - 1
finy = min([stepsize * (indy + 1), len(y) - 1])
pts = []
for i in range(inix, finx):
for j in range(iniy, finy):
if datavals[j][i] != nodata_value:
if obj.CuttingBoundary:
if obj.CuttingBoundary.Shape.isInside(FreeCAD.Vector(x[i], y[j], 0), 0, True):
pts.append([x[i], y[j], datavals[j][i]])
else:
pts.append([x[i], y[j], datavals[j][i]])
if len(pts) > 3:
try:
mesh.addMesh(Triangulation.Triangulate(pts))
#Mesh.show(mesh)
except TypeError:
print("error al procesar: {0} puntos".format(len(pts)))
mesh.removeDuplicatedPoints()
mesh.removeFoldsOnSurface()
obj.InitialMesh = mesh.copy()
Mesh.show(mesh)
if prop == "PointsGroup" or prop == "CuttingBoundary":
if obj.PointsGroup and obj.CuttingBoundary:
bnd = obj.CuttingBoundary.Shape
if len(bnd.Faces) == 0:
pts = [ver.Point for ver in bnd.Vertexes]
pts.append(pts[0])
bnd = Part.makePolygon(pts)
# TODO: not use the first point, else the Origin in "Site".
# It is standard for everything.
firstPoint = self.obj.PointsGroup.Points.Points[0]
nbase = FreeCAD.Vector(firstPoint.x, firstPoint.y, firstPoint.z)
data = []
for point in self.obj.PointsGroup.Points.Points:
tmp = FreeCAD.Vector(0, 0, 0).add(point)
tmp.z = 0
if bnd.isInside(tmp, 0, True):
p = point - nbase
data.append([float(p.x), float(p.y), float(p.z)])
Data = np.array(data)
data.clear()
import MeshTools.Triangulation as Triangulation
mesh = Triangulation.Triangulate(Data)
'''shape = PVPlantCreateTerrainMesh.MeshToShape(mesh)
shape.Placement.move(nbase)'''
obj.Shape = shape
if obj.DEM:
obj.DEM = None
def execute(self, obj):
''''''
#print(" ----- Terrain - EXECUTE ----------")
def __getstate__(self):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
class ViewProviderTerrain:
"A View Provider for the Pipe object"
def __init__(self, vobj):
self.Object = vobj.Object
self.boundary_color = None
self.edge_style = None
self.edge_color = None
self.edge_material = None
self.face_material = None
self.triangles = None
self.geo_coords = None
self.setProperties(vobj)
def setProperties(self, vobj):
# Triangulation properties.
pl = vobj.PropertiesList
if not ("Transparency" in pl):
vobj.addProperty("App::PropertyIntegerConstraint",
"Transparency",
"Surface Style",
"Set triangle face transparency").Transparency = (50, 0, 100, 1)
if not ("ShapeColor" in pl):
vobj.addProperty("App::PropertyColor",
"ShapeColor",
"Surface Style",
"Set triangle face color").ShapeColor = (r, g, b, vobj.Transparency / 100)
if not ("ShapeMaterial" in pl):
vobj.addProperty("App::PropertyMaterial",
"ShapeMaterial",
"Surface Style",
"Triangle face material").ShapeMaterial = FreeCAD.Material()
if not ("LineTransparency" in pl):
vobj.addProperty("App::PropertyIntegerConstraint",
"LineTransparency",
"Surface Style",
"Set triangle edge transparency").LineTransparency = (50, 0, 100, 1)
if not ("LineColor" in pl):
vobj.addProperty("App::PropertyColor",
"LineColor",
"Surface Style",
"Set triangle face color").LineColor = (0.5, 0.5, 0.5, vobj.LineTransparency / 100)
if not ("LineMaterial" in pl):
vobj.addProperty("App::PropertyMaterial",
"LineMaterial",
"Surface Style",
"Triangle face material").LineMaterial = FreeCAD.Material()
if not ("LineWidth" in pl):
vobj.addProperty("App::PropertyFloatConstraint",
"LineWidth",
"Surface Style",
"Set triangle edge line width").LineWidth = (0.0, 1.0, 20.0, 1.0)
# Boundary properties.
if not ("BoundaryColor" in pl):
vobj.addProperty("App::PropertyColor",
"BoundaryColor",
"Boundary Style",
"Set boundary contour color").BoundaryColor = (0.0, 0.75, 1.0, 0.0)
if not ("BoundaryWidth" in pl):
vobj.addProperty("App::PropertyFloatConstraint",
"BoundaryWidth",
"Boundary Style",
"Set boundary contour line width").BoundaryWidth = (3.0, 1.0, 20.0, 1.0)
if not ("BoundaryPattern" in pl):
vobj.addProperty("App::PropertyEnumeration",
"BoundaryPattern",
"Boundary Style",
"Set a line pattern for boundary").BoundaryPattern = [*line_patterns]
if not ("PatternScale" in pl):
vobj.addProperty("App::PropertyIntegerConstraint",
"PatternScale",
"Boundary Style",
"Scale the line pattern").PatternScale = (3, 1, 20, 1)
# Contour properties.
if not ("MajorColor" in pl):
vobj.addProperty("App::PropertyColor",
"MajorColor",
"Contour Style",
"Set major contour color").MajorColor = (1.0, 0.0, 0.0, 0.0)
if not ("MajorWidth" in pl):
vobj.addProperty("App::PropertyFloatConstraint",
"MajorWidth",
"Contour Style",
"Set major contour line width").MajorWidth = (4.0, 1.0, 20.0, 1.0)
if not ("MinorColor" in pl):
vobj.addProperty("App::PropertyColor",
"MinorColor",
"Contour Style",
"Set minor contour color").MinorColor = (1.0, 1.0, 0.0, 0.0)
if not ("MinorWidth" in pl):
vobj.addProperty("App::PropertyFloatConstraint",
"MinorWidth",
"Contour Style",
"Set major contour line width").MinorWidth = (2.0, 1.0, 20.0, 1.0)
vobj.Proxy = self
vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor
def onDocumentRestored(self, vobj):
self.setProperties(vobj)
def onChanged(self, vobj, prop):
''' Update Object visuals when a view property changed. '''
if prop == "ShapeColor" or prop == "Transparency":
if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"):
color = vobj.getPropertyByName("ShapeColor")
transparency = vobj.getPropertyByName("Transparency")
color = (color[0], color[1], color[2], transparency / 100)
vobj.ShapeMaterial.DiffuseColor = color
if prop == "ShapeMaterial":
if hasattr(vobj, "ShapeMaterial"):
material = vobj.getPropertyByName("ShapeMaterial")
self.face_material.diffuseColor.setValue(material.DiffuseColor[:3])
self.face_material.transparency = material.DiffuseColor[3]
if prop == "LineColor" or prop == "LineTransparency":
if hasattr(vobj, "LineColor") and hasattr(vobj, "LineTransparency"):
color = vobj.getPropertyByName("LineColor")
transparency = vobj.getPropertyByName("LineTransparency")
color = (color[0], color[1], color[2], transparency / 100)
vobj.LineMaterial.DiffuseColor = color
if prop == "LineMaterial":
material = vobj.getPropertyByName(prop)
self.edge_material.diffuseColor.setValue(material.DiffuseColor[:3])
self.edge_material.transparency = material.DiffuseColor[3]
if prop == "LineWidth":
width = vobj.getPropertyByName(prop)
self.edge_style.lineWidth = width
if prop == "BoundaryColor":
color = vobj.getPropertyByName(prop)
self.boundary_color.rgb = color[:3]
if prop == "BoundaryWidth":
width = vobj.getPropertyByName(prop)
self.boundary_style.lineWidth = width
if prop == "BoundaryPattern":
if hasattr(vobj, "BoundaryPattern"):
pattern = vobj.getPropertyByName(prop)
self.boundary_style.linePattern = line_patterns[pattern]
if prop == "PatternScale":
if hasattr(vobj, "PatternScale"):
scale = vobj.getPropertyByName(prop)
self.boundary_style.linePatternScaleFactor = scale
if prop == "MajorColor":
color = vobj.getPropertyByName(prop)
self.major_color.rgb = color[:3]
if prop == "MajorWidth":
width = vobj.getPropertyByName(prop)
self.major_style.lineWidth = width
if prop == "MinorColor":
color = vobj.getPropertyByName(prop)
self.minor_color.rgb = color[:3]
if prop == "MinorWidth":
width = vobj.getPropertyByName(prop)
self.minor_style.lineWidth = width
def attach(self, vobj):
''' Create Object visuals in 3D view.'''
# GeoCoords Node.
self.geo_coords = coin.SoGeoCoordinate()
# Surface features.
self.triangles = coin.SoIndexedFaceSet()
self.face_material = coin.SoMaterial()
self.edge_material = coin.SoMaterial()
self.edge_color = coin.SoBaseColor()
self.edge_style = coin.SoDrawStyle()
self.edge_style.style = coin.SoDrawStyle.LINES
shape_hints = coin.SoShapeHints()
shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
mat_binding = coin.SoMaterialBinding()
mat_binding.value = coin.SoMaterialBinding.PER_FACE
offset = coin.SoPolygonOffset()
offset.styles = coin.SoPolygonOffset.LINES
offset.factor = -2.0
# Boundary features.
'''self.boundary_color = coin.SoBaseColor()
self.boundary_coords = coin.SoGeoCoordinate()
self.boundary_lines = coin.SoLineSet()
self.boundary_style = coin.SoDrawStyle()
self.boundary_style.style = coin.SoDrawStyle.LINES'''
# Boundary root.
'''boundaries = coin.SoType.fromName('SoFCSelection').createInstance()
boundaries.style = 'EMISSIVE_DIFFUSE'
boundaries.addChild(self.boundary_color)
boundaries.addChild(self.boundary_style)
boundaries.addChild(self.boundary_coords)
boundaries.addChild(self.boundary_lines)'''
# Major Contour features.
'''self.major_color = coin.SoBaseColor()
self.major_coords = coin.SoGeoCoordinate()
self.major_lines = coin.SoLineSet()
self.major_style = coin.SoDrawStyle()
self.major_style.style = coin.SoDrawStyle.LINES'''
# Major Contour root.
'''major_contours = coin.SoSeparator()
major_contours.addChild(self.major_color)
major_contours.addChild(self.major_style)
major_contours.addChild(self.major_coords)
major_contours.addChild(self.major_lines)'''
# Minor Contour features.
'''self.minor_color = coin.SoBaseColor()
self.minor_coords = coin.SoGeoCoordinate()
self.minor_lines = coin.SoLineSet()
self.minor_style = coin.SoDrawStyle()
self.minor_style.style = coin.SoDrawStyle.LINES'''
# Minor Contour root.
'''minor_contours = coin.SoSeparator()
minor_contours.addChild(self.minor_color)
minor_contours.addChild(self.minor_style)
minor_contours.addChild(self.minor_coords)
minor_contours.addChild(self.minor_lines)'''
# Highlight for selection.
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
highlight.style = 'EMISSIVE_DIFFUSE'
highlight.addChild(shape_hints)
highlight.addChild(mat_binding)
highlight.addChild(self.geo_coords)
highlight.addChild(self.triangles)
highlight.addChild(boundaries)
# Face root.
face = coin.SoSeparator()
face.addChild(self.face_material)
face.addChild(highlight)
# Edge root.
edge = coin.SoSeparator()
edge.addChild(self.edge_material)
edge.addChild(self.edge_style)
edge.addChild(highlight)
# Surface root.
surface_root = coin.SoSeparator()
surface_root.addChild(face)
surface_root.addChild(offset)
surface_root.addChild(edge)
surface_root.addChild(major_contours)
surface_root.addChild(minor_contours)
vobj.addDisplayMode(surface_root, "Surface")
# Boundary root.
boundary_root = coin.SoSeparator()
boundary_root.addChild(boundaries)
vobj.addDisplayMode(boundary_root, "Boundary")
# Elevation/Shaded root.
'''shaded_root = coin.SoSeparator()
shaded_root.addChild(face)
vobj.addDisplayMode(shaded_root, "Elevation")
vobj.addDisplayMode(shaded_root, "Slope")
vobj.addDisplayMode(shaded_root, "Shaded")'''
# Flat Lines root.
flatlines_root = coin.SoSeparator()
flatlines_root.addChild(face)
flatlines_root.addChild(offset)
flatlines_root.addChild(edge)
vobj.addDisplayMode(flatlines_root, "Flat Lines")
# Wireframe root.
wireframe_root = coin.SoSeparator()
wireframe_root.addChild(edge)
wireframe_root.addChild(major_contours)
wireframe_root.addChild(minor_contours)
vobj.addDisplayMode(wireframe_root, "Wireframe")
# Take features from properties.
self.onChanged(vobj, "ShapeColor")
self.onChanged(vobj, "LineColor")
self.onChanged(vobj, "LineWidth")
#self.onChanged(vobj, "BoundaryColor")
#self.onChanged(vobj, "BoundaryWidth")
#self.onChanged(vobj, "BoundaryPattern")
#self.onChanged(vobj, "PatternScale")
#self.onChanged(vobj, "MajorColor")
#self.onChanged(vobj, "MajorWidth")
#self.onChanged(vobj, "MinorColor")
#self.onChanged(vobj, "MinorWidth")
def updateData(self, obj, prop):
''' Update Object visuals when a data property changed. '''
# Set geosystem.
geo_system = ["UTM", FreeCAD.ActiveDocument.Site.UtmZone, "FLAT"]
self.geo_coords.geoSystem.setValues(geo_system)
'''
self.boundary_coords.geoSystem.setValues(geo_system)
self.major_coords.geoSystem.setValues(geo_system)
self.minor_coords.geoSystem.setValues(geo_system)
'''
if prop == "Mesh":
print("update terrain mesh")
mesh = obj.Mesh
copy_mesh = mesh.copy()
# copy_mesh.Placement.move(origin.Origin)
triangles = []
for i in copy_mesh.Topology[1]:
triangles.extend(list(i))
triangles.append(-1)
self.geo_coords.point.values = copy_mesh.Topology[0]
self.triangles.coordIndex.values = triangles
del copy_mesh
def getDisplayModes(self, vobj):
''' Return a list of display modes. '''
modes = ["Surface", "Boundary"]
return modes
def getDefaultDisplayMode(self):
'''
Return the name of the default display mode.
'''
return "Surface"
def claimChildren(self):
return [self.Object.CuttingBoundary, ]
def getIcon(self):
return str(os.path.join(DirIcons, "terrain.svg"))
def __getstate__(self):
""" Save variables to file. """
return None
def __setstate__(self, state):
""" Get variables from file. """
return None
class _CommandTerrain:
"the PVPlant Terrain command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "terrain.svg")),
'MenuText': "Terrain",
'Accel': "S, T",
'ToolTip': "Creates a Terrain object from setup dialog."}
def IsActive(self):
return (not (FreeCAD.ActiveDocument is None) and
not (FreeCAD.ActiveDocument.getObject("Site") is None) and
(FreeCAD.ActiveDocument.getObject("Terrain") is None))
def Activated(self):
makeTerrain()
# task = _TerrainTaskPanel()
# FreeCADGui.Control.showDialog(task)
return
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Terrain', _CommandTerrain())

806
PVPlantTerrainAnalisys.py Normal file
View File

@@ -0,0 +1,806 @@
import Draft
import FreeCAD
import Part
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import os
from PVPlantResources import DirIcons as DirIcons
def Mest2FemMesh(obj):
import Fem
fm = Fem.FemMesh()
#i = 0
#nodes = []
for mp in obj.Mesh.Points:
fm.addNode(mp.Vector.x, mp.Vector.y, mp.Vector.z)
# nodes.append(mp)
# i += 1
for mf in obj.Mesh.Facets:
fm.addFace([mf.PointIndices[0] + 1, mf.PointIndices[1] + 1, mf.PointIndices[2] + 1])
obj2 = FreeCAD.ActiveDocument.addObject("Fem::FemMeshObject")
obj2.FemMesh = fm
obj2.ViewObject.DisplayMode = "Faces"
FreeCAD.activeDocument().recompute()
return obj2
def makeContours(land, minor = 1000, mayor = 5000,
minorColor=(0.0, 0.00, 0.80), mayorColor=(0.00, 0.00, 1.00),
minorThickness = 2, mayorThickness = 5,
filter_size = 5):
if not land:
return
if land.TypeId == 'Mesh::Feature':
Contours_Mesh(land.Mesh, minor, mayor, minorColor, mayorColor, minorThickness, mayorThickness, filter_size)
else:
Contours_Part(land, minor, mayor, minorColor, mayorColor, minorThickness, mayorThickness, filter_size)
FreeCAD.ActiveDocument.recompute()
def Contours_Mesh(Mesh, minor, mayor,
minorColor, mayorColor,
minorLineWidth, mayorLineWidth,
filter_size): #filter_size de 3 a 21 y siempre impar
def calculateSection(cuts):
for inc in cuts:
CrossSections = Mesh.crossSections([((0, 0, inc), (0, 0, 1))], 0.000001)
for PointList in CrossSections[0]:
if len(PointList) > 1:
if PointList[0].sub(PointList[-1]).Length <= 10000.0:
PointList.append(PointList[0])
# 1. Smooth the points
if (len(PointList) > filter_size) and (filter_size > 0):
for a in range(len(PointList)):
x = 0
y = 0
for p in range(-filter_radius, filter_radius + 1):
point_id = a + p
if point_id < 0:
point_id = 0
if point_id >= len(PointList):
point_id = len(PointList) - 1
x += PointList[point_id].x
y += PointList[point_id].y
x /= filter_size
y /= filter_size
PointList[a].x = x
PointList[a].y = y
for a in reversed(range(len(PointList))):
x = 0
y = 0
for p in range(-filter_radius, filter_radius + 1):
point_id = a + p
if point_id < 0:
point_id = 0
if point_id >= len(PointList):
point_id = len(PointList) - 1
x += PointList[point_id].x
y += PointList[point_id].y
x /= filter_size
y /= filter_size
PointList[a].x = x
PointList[a].y = y
# 2. Make lines
#Contour = Draft.makeWire(PointList, closed=False, face=None)
#Contour.Label = str(int(inc / 1000)) + "m"
Contour = Part.makePolygon(PointList)
Part.show(Contour, str(int(inc / 1000)) + "m")
'''Contours.addObject(Contour)
if inc % mayor == 0:
Contour.ViewObject.LineWidth = mayorLineWidth
Contour.ViewObject.LineColor = mayorColor
else:
Contour.ViewObject.LineWidth = minorLineWidth
Contour.ViewObject.LineColor = minorColor'''
del Contour, PointList
del CrossSections
filter_radius = int(filter_size / 2)
try:
Contours = FreeCAD.ActiveDocument.Contours
except:
Contours = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Contours')
ZMin = Mesh.BoundBox.ZMin // 10000
ZMin *= 10000
ZMax = Mesh.BoundBox.ZMax
import numpy as np
minor_array = np.arange(ZMin, ZMax, minor)
mayor_array = np.arange(ZMin, ZMax, mayor)
minor_array = np.array(list(filter(lambda x: x not in mayor_array, minor_array)))
calculateSection(minor_array)
calculateSection(mayor_array)
def Contours_Part(Terrain, minor, mayor,
minorColor, mayorColor,
minorLineWidth, mayorLineWidth,
filter_size): #filter_size de 3 a 21 y siempre impar
def calculateSection(cuts):
cnt = 0
for inc in cuts:
CrossSections = Terrain.Shape.slice(FreeCAD.Vector(0, 0, 1), inc)
for wire in CrossSections:
PointList = []
for vertex in wire.Vertexes:
PointList.append(vertex.Point)
if len(PointList) > 1:
# 1. Smooth the points
if (len(PointList) > filter_size) and (filter_size > 0):
for a in range(len(PointList)):
x = 0
y = 0
for p in range(-filter_radius, filter_radius + 1):
point_id = a + p
if point_id < 0:
point_id = 0
if point_id >= len(PointList):
point_id = len(PointList) - 1
x += PointList[point_id].x
y += PointList[point_id].y
x /= filter_size
y /= filter_size
PointList[a].x = x
PointList[a].y = y
for a in reversed(range(len(PointList))):
x = 0
y = 0
for p in range(-filter_radius, filter_radius + 1):
point_id = a + p
if point_id < 0:
point_id = 0
if point_id >= len(PointList):
point_id = len(PointList) - 1
x += PointList[point_id].x
y += PointList[point_id].y
x /= filter_size
y /= filter_size
PointList[a].x = x
PointList[a].y = y
# 2. TODO: close wire
# 3. Make lines
Contour = Draft.makeWire(PointList, closed=False, face=None, support=None)
Contour.MakeFace = False
Contour.Label = str(int(inc / 1000)) + "m"
Contours.addObject(Contour)
if inc % mayor == 0:
Contour.ViewObject.LineWidth = mayorLineWidth
Contour.ViewObject.LineColor = mayorColor
else:
Contour.ViewObject.LineWidth = minorLineWidth
Contour.ViewObject.LineColor = minorColor
cnt += 1
if cnt == 10:
return
filter_radius = int(filter_size / 2)
try:
Contours = FreeCAD.ActiveDocument.Contours
except:
Contours = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Contours')
ZMin = Terrain.Shape.BoundBox.ZMin // 10000
ZMin *= 10000
ZMax = Terrain.Shape.BoundBox.ZMax
import numpy as np
minor_array = np.arange(ZMin, ZMax, minor)
mayor_array = np.arange(ZMin, ZMax, mayor)
minor_array = np.array(list(filter(lambda x: x not in mayor_array, minor_array)))
calculateSection(minor_array)
calculateSection(mayor_array)
# Base widget for task panel terrain analisys
class _generalTaskPanel:
'''The TaskPanel for Slope setup'''
def __init__(self):
self.ranges = []
self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/PVPlantTerrainAnalisys.ui")
self.tableWidget = self.form.tableWidget
self.form.editSteps.valueChanged.connect(self.changeDivision)
self.tableWidget.itemChanged.connect(self.cellChanged)
def cellChanged(self, item):
if (item.column() == 1) and (item.row() != (self.tableWidget.rowCount() - 1)):
item2 = self.tableWidget.item(item.row() + 1, 0)
item2.setText(item.text())
def updateTableValues(self):
maxval = self.form.editTo.value() - self.form.editFrom.value()
ran = maxval / self.tableWidget.rowCount()
for i in range(self.tableWidget.rowCount()):
for j in range(2):
item = self.tableWidget.item(i, j)
val = ran * (i + j) + self.form.editFrom.value()
item.setText('{:.1f}'.format(val))
self.ranges[i][j] = val
#self.tableWidget.item(0, 0).setText('{:.1f}'.format(self.form.editFrom.value()))
#self.tableWidget.item(self.tableWidget.rowCount() - 1, 1).setText('{:.1f}'.format(self.form.editTo.value()))
def changeDivision(self):
self.tableWidget.blockSignals(True)
rows = self.tableWidget.rowCount()
to = self.form.editSteps.value()
if to > rows:
for i in range(0, to - rows):
self.ranges.append([0.0, 0.0, (0.0, 0.0, 0.0)])
self.createRow()
elif to < rows:
self.tableWidget.setRowCount(to)
self.ranges = self.ranges[:to]
self.updateTableValues()
self.tableWidget.blockSignals(False)
def createRow(self):
row = self.tableWidget.rowCount()
self.tableWidget.setRowCount(self.tableWidget.rowCount() + 1)
newItem = QtGui.QTableWidgetItem("item")
self.tableWidget.setItem(row, 0, newItem)
newItem.setTextAlignment(QtCore.Qt.AlignCenter)
newItem.setText("0.0")
newItem.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsDragEnabled|
QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsUserCheckable)
newItem = QtGui.QTableWidgetItem("item")
self.tableWidget.setItem(row, 1, newItem)
newItem.setTextAlignment(QtCore.Qt.AlignCenter)
newItem.setText("0.0")
#import random
# r = random.randint(0, 255)
# g = random.randint(0, 255)
# b = random.randint(0, 255)
val = int(127 * row / (self.form.editSteps.value() / 2))
g = (127 + val) if val <= 127 else 0
r = (328 - val if val >= 127 else 0)
color = QtGui.QColor(r, g, 0)
colorPix = QtGui.QPixmap(16, 16)
colorPix.fill(color)
self.ranges[row][2] = (color.red()/255, color.green()/255, color.blue()/255)
buttonColor = QtGui.QPushButton('')
buttonColor.setIcon(QtGui.QIcon(colorPix))
buttonColor.setMaximumSize(QtCore.QSize(20, 20))
buttonColor.clicked.connect(lambda: self.selColor(buttonColor))
self.tableWidget.setCellWidget(row, 2, buttonColor)
def selColor(self, button):
color = QtGui.QColorDialog.getColor()
if color.isValid():
print("añadir color")
colorPix = QtGui.QPixmap(16, 16)
colorPix.fill(color)
button.setIcon(QtGui.QIcon(colorPix))
curentIndex = self.tableWidget.currentIndex()
self.ranges[curentIndex.row()][2] = (color.red()/255, color.green()/255, color.blue()/255)
# Contours Analisys: ---------------------------------------------------------------------------------
class _ContourTaskPanel():
'''The editmode TaskPanel for contours generator'''
def __init__(self):
self.MinorColor = (0.5, 0.5, 0.5)
self.MayorColor = (0.5, 0.5, 0.5)
land = None
self.intervals = ["0.1 m", "0.5 m", "1 m", "5 m", "10 m", "50 m", "100 m", "500 m"]
self.intervalvalues = [0.1, 0.5, 1, 5, 10, 50, 100, 500]
# form:
self.form = QtGui.QWidget()
self.form.resize(800,640)
self.form.setWindowTitle("Curvas de nivel")
self.form.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
self.grid = QtGui.QGridLayout(self.form)
# parameters
self.labelTerrain = QtGui.QLabel()
self.labelTerrain.setText("Terreno:")
self.lineEdit1 = QtGui.QLineEdit(self.form)
self.lineEdit1.setObjectName(_fromUtf8("lineEdit1"))
self.lineEdit1.readOnly = True
self.grid.addWidget(self.labelTerrain, self.grid.rowCount(), 0, 1, 1)
self.grid.addWidget(self.lineEdit1, self.grid.rowCount() - 1, 1, 1, 1)
self.buttonAdd = QtGui.QPushButton('Seleccionar Terreno')
self.buttonAdd.clicked.connect(self.add)
self.grid.addWidget(self.buttonAdd, self.grid.rowCount(), 1, 1, 1)
###----------------------
self.widgetDivisions = QtGui.QGroupBox(self.form)
self.grid.addWidget(self.widgetDivisions, self.grid.rowCount(), 0, 1, -1)
self.gridDivisions = QtGui.QGridLayout(self.widgetDivisions)
self.labelTitle1 = QtGui.QLabel()
self.labelTitle1.setText("Intervalo")
self.labelTitle2 = QtGui.QLabel()
self.labelTitle2.setText("Grosor")
self.labelTitle3 = QtGui.QLabel()
self.labelTitle3.setText("Color")
self.gridDivisions.addWidget(self.labelTitle1, self.gridDivisions.rowCount(), 1, 1, -1)
self.gridDivisions.addWidget(self.labelTitle2, self.gridDivisions.rowCount() - 1, 2, 1, -1)
self.gridDivisions.addWidget(self.labelTitle3, self.gridDivisions.rowCount() - 1, 3, 1, -1)
self.labelMinorContour = QtGui.QLabel(self.form)
self.labelMinorContour.setText("Menor:")
self.inputMinorContourMargin = QtGui.QComboBox(self.form)
self.inputMinorContourMargin.addItems(self.intervals)
self.inputMinorContourMargin.setCurrentIndex(2)
self.inputMinorContourThickness = QtGui.QSpinBox(self.form)
self.inputMinorContourThickness.setRange(1, 10)
self.inputMinorContourThickness.setValue(2)
self.gridDivisions.addWidget(self.labelMinorContour, self.gridDivisions.rowCount(), 0, 1, 1)
self.gridDivisions.addWidget(self.inputMinorContourMargin, self.gridDivisions.rowCount() - 1, 1, 1, 1)
self.gridDivisions.addWidget(self.inputMinorContourThickness, self.gridDivisions.rowCount() - 1, 2, 1, 1)
self.buttonMinorContourColor = QtGui.QPushButton('')
self.color = QtGui.QColor(128, 128, 128)
colorPix = QtGui.QPixmap(16, 16)
colorPix.fill(self.color)
self.buttonMinorContourColor.setIcon(QtGui.QIcon(colorPix))
self.buttonMinorContourColor.clicked.connect(lambda: self.selColor(self.buttonMinorContourColor))
self.gridDivisions.addWidget(self.buttonMinorContourColor, self.gridDivisions.rowCount() - 1, 3, 1, 1)
###----------------------
self.labelMayorContour = QtGui.QLabel(self.form)
self.labelMayorContour.setText("Mayor:")
self.inputMayorContourMargin = QtGui.QComboBox(self.form)
self.inputMayorContourMargin.addItems(self.intervals)
self.inputMayorContourMargin.setCurrentIndex(3)
self.inputMayorContourThickness = QtGui.QSpinBox(self.form)
self.inputMayorContourThickness.setRange(1, 10)
self.inputMayorContourThickness.setValue(5)
self.gridDivisions.addWidget(self.labelMayorContour, self.gridDivisions.rowCount(), 0, 1, 1)
self.gridDivisions.addWidget(self.inputMayorContourMargin, self.gridDivisions.rowCount() - 1, 1, 1, 1)
self.gridDivisions.addWidget(self.inputMayorContourThickness, self.gridDivisions.rowCount() - 1, 2, 1, 1)
self.buttonMayorContourColor = QtGui.QPushButton('')
self.color = QtGui.QColor(128, 128, 128)
colorPix = QtGui.QPixmap(16, 16)
colorPix.fill(self.color)
self.buttonMayorContourColor.setIcon(QtGui.QIcon(colorPix))
self.buttonMayorContourColor.clicked.connect(lambda: self.selColor(self.buttonMayorContourColor))
self.gridDivisions.addWidget(self.buttonMayorContourColor, self.gridDivisions.rowCount() - 1, 3, 1, 1)
def add(self):
sel = FreeCADGui.Selection.getSelection()
if len(sel) > 0:
self.land = sel[0]
self.lineEdit1.setText(self.land.Label)
def selColor(self, button):
color = QtGui.QColorDialog.getColor()
colorPix = QtGui.QPixmap(16, 16)
colorPix.fill(color)
button.setIcon(QtGui.QIcon(colorPix))
r = float(color.red()/255.0)
g = float(color.green()/255.0)
b = float(color.blue()/255.0)
col = (r, g, b)
if button is self.buttonMinorContourColor:
self.MinorColor = col
elif button is self.buttonMayorContourColor:
self.MayorColor = col
def accept(self):
from datetime import datetime
starttime = datetime.now()
if self.land is None:
print("No hay objetos para procesar")
return False
else:
minor = FreeCAD.Units.Quantity(self.inputMinorContourMargin.currentText()).Value
mayor = FreeCAD.Units.Quantity(self.inputMayorContourMargin.currentText()).Value
i = 2
if i == 0:
makeContours(self.land, minor, mayor, self.MinorColor, self.MayorColor,
self.inputMinorContourThickness.value(), self.inputMayorContourThickness.value())
elif i == 1:
import multiprocessing
p = multiprocessing.Process(target=makeContours,
args=(self.land, minor, mayor,
self.MinorColor, self.MayorColor,
self.inputMinorContourThickness.value(),
self.inputMayorContourThickness.value(), ))
p.start()
p.join()
else:
import threading
hilo = threading.Thread(target = makeContours,
args = (self.land, minor, mayor,
self.MinorColor, self.MayorColor,
self.inputMinorContourThickness.value(),
self.inputMayorContourThickness.value()))
hilo.daemon = True
hilo.start()
total_time = datetime.now() - starttime
print(" -- Tiempo tardado:", total_time)
FreeCADGui.Control.closeDialog()
return True
# Height Analisys: ---------------------------------------------------------------------------------
class _HeightTaskPanel(_generalTaskPanel):
'''The TaskPanel for Slope setup'''
def __init__(self):
_generalTaskPanel.__init__(self)
# Initial set-up:
land = FreeCAD.ActiveDocument.Site.Terrain
self.form.editFrom.setSuffix(" m")
self.form.editFrom.setValue(land.Shape.BoundBox.ZMin / 1000)
self.form.editTo.setSuffix(" m")
self.form.editTo.setValue(land.Shape.BoundBox.ZMax / 1000)
self.form.editSteps.setValue(10)
self.form.editFrom.valueChanged.connect(self.updateTableValues)
self.form.editTo.valueChanged.connect(self.updateTableValues)
def accept(self):
land = FreeCAD.ActiveDocument.Site.Terrain
if land.isDerivedFrom("Part::Feature"):
colorlist = []
for face in land.Shape.Faces:
zz = face.CenterOfMass.z / 1000
color = (.0, .0, .0)
for i in range(1,len(self.ranges)):
if self.ranges[i][0] <= zz <= self.ranges[i][1]:
color = self.ranges[i][2]
break
colorlist.append(color)
land.ViewObject.DiffuseColor = colorlist
FreeCAD.activeDocument().recompute()
return True
# Slope Analisys: ---------------------------------------------------------------------------------
class _SlopeTaskPanel(_generalTaskPanel):
'''The TaskPanel for Slope setup'''
def __init__(self):
_generalTaskPanel.__init__(self)
self.angles = self.getAngles()
# Initial set-up:
self.form.editFrom.setSuffix(" º")
self.form.editFrom.setValue(0)
self.form.editTo.setSuffix(" º")
self.form.editTo.setValue(max(self.angles))
self.form.editSteps.setValue(10)
self.form.editFrom.valueChanged.connect(self.updateTableValues)
self.form.editTo.valueChanged.connect(self.updateTableValues)
def getAngles(self):
import math
land = FreeCAD.ActiveDocument.Site.Terrain
angles = []
for face in land.Shape.Faces:
normal = face.normalAt(0, 0)
rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
angle = math.degrees(rad)
angles.append(angle)
return angles
def getPointSlope(self, ranges = None):
from datetime import datetime
starttime = datetime.now()
import math
land = FreeCAD.ActiveDocument.Site.Terrain
if land.isDerivedFrom("Part::Feature"):
colorlist = []
for face in land.Shape.Faces:
normal = face.normalAt(0, 0)
rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
angle = math.degrees(rad)
if(angle > 90):
angle -= 90
color = (1.0, 1.0, 1.0)
for i in range(0, len(ranges)):
if ranges[i][0] <= angle <= ranges[i][1]:
color = ranges[i][2]
break
colorlist.append(color)
print(len(land.Shape.Faces) == len(colorlist))
land.ViewObject.DiffuseColor = colorlist
# TODO: check this code:
elif obj.isDerivedFrom("Mesh::Feature"):
fMesh = Mest2FemMesh(land)
import math
setColors = []
i = 1
normals = land.Mesh.getPointNormals()
for normal in normals:
rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
angle = math.degrees(rad)
# Cambiar esto a la lista de colores configurable
if angle < 5:
setColors[i] = (0.0, 0.5, 0.0)
elif angle < 7.5:
setColors[i] = (0.0, 1.0, 0.5)
elif (angle < 10):
setColors[i] = (0.0, 1.0, 1.0)
elif (angle < 12.5):
setColors[i] = (0.0, 0.0, 0.5)
elif (angle < 14):
setColors[i] = (0.0, 0.0, 1.0)
elif (angle > 20):
setColors[i] = (1.0, 0.0, 0.0)
else:
setColors[i] = (1.0, 1.0, 1.0)
i += 1
fMesh.ViewObject.NodeColor = setColors
FreeCAD.activeDocument().recompute()
print("Everything OK (", datetime.now() - starttime, ")")
def accept(self):
# self.getPointSlope()
import threading
hilo = threading.Thread(target=self.getPointSlope(self.ranges))
hilo.start()
return True
# Orientation Analisys: ---------------------------------------------------------------------------------
class _OrientationTaskPanel(_generalTaskPanel):
'''The TaskPanel for Orientation setup'''
def __init__(self):
_generalTaskPanel.__init__(self)
self.getAngles()
# Initial set-up:
self.form.editFrom.setSuffix(" º")
self.form.editFrom.setValue(0.0)
self.form.editTo.setSuffix(" º")
self.form.editTo.setMaximum(360.0)
self.form.editTo.setValue(360.0)
self.form.editSteps.setValue(15)
self.form.editFrom.valueChanged.connect(self.updateTableValues)
self.form.editTo.valueChanged.connect(self.updateTableValues)
def getAngles(self):
import math
land = FreeCAD.ActiveDocument.Site.Terrain
anglesx = []
anglesy = []
for face in land.Shape.Faces:
normal = face.normalAt(0, 0)
normal.z = 0
anglesx.append(math.degrees(normal.getAngle(FreeCAD.Vector(1, 0, 0))))
anglesy.append(math.degrees(normal.getAngle(FreeCAD.Vector(0, 1, 0))))
print("Min x: ", min(anglesx), " y: ", min(anglesy))
print("Max x: ", max(anglesx), " y: ", max(anglesy))
return anglesx, anglesy
def accept(self):
import math
from datetime import datetime
starttime = datetime.now()
land = FreeCAD.ActiveDocument.Site.Terrain
if land.isDerivedFrom("Part::Feature"):
colorlist = []
j = 0
minx = 99999
miny = 99999
for face in land.Shape.Faces:
normal = face.normalAt(0, 0)
normal.z = 0
anglex = math.degrees(normal.getAngle(FreeCAD.Vector(1, 0, 0)))
angley = math.degrees(normal.getAngle(FreeCAD.Vector(0, 1, 0)))
minx = min([minx, anglex])
miny = min([miny, angley])
if angley >= 90:
anglex = 360.0 - anglex
#print(anglex, " ", angley)
for i in range(1, len(self.ranges)):
if self.ranges[i][0] <= anglex <= self.ranges[i][1]:
colorlist.append(self.ranges[i][2])
break
j += 1
if j == 100:
break
land.ViewObject.DiffuseColor = colorlist
print("Angulos: ", math.degrees(minx), " - ", miny)
# TODO: check this code:
elif land.isDerivedFrom("Mesh::Feature"):
fMesh = Mest2FemMesh(land)
fMesh = Mest2FemMesh(land)
setColors = {}
i = 1
normals = land.Mesh.getPointNormals()
print(" --------- time to FEMMESH: (", datetime.now() - starttime, ")")
for normal in normals:
rad = normal.getAngle(FreeCAD.Vector(1, 1, 0))
angle = math.degrees(rad)
# Cambiar esto a la lista de colores configurable
if angle < 45:
setColors[i] = (0.0, 1.0, 0.0)
elif (angle < 90):
setColors[i] = (0.0, 1.0, 1.0)
elif (angle < 135):
setColors[i] = (0.0, 0.0, 1.0)
elif (angle <= 180):
setColors[i] = (1.0, 0.0, 1.0)
#else:
# setColors[i] = (1.0, 0.0, 0.0)
i += 1
if i== 1000:
break
fMesh.ViewObject.NodeColor = setColors
FreeCAD.activeDocument().recompute()
print("Everything OK (", datetime.now() - starttime, ")")
return True
## Commands ----------------------------------------------------------------------------------------------------------
## 1. Contours:
class _CommandContours:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "TerrainContours.svg")),
'Accel': "T, C",
'MenuText': 'Curvas de nivel',
'ToolTip': 'Curvas de nivel'
}
def IsActive(self):
# return not FreeCAD.ActiveDocument is None
if FreeCAD.ActiveDocument is None:
return False
return True
if FreeCADGui.Selection.getSelection() is not None:
selection = FreeCADGui.Selection.getSelection()[-1]
if selection.TypeId == 'Mesh::Feature':
return True
return False
def Activated(self):
self.TaskPanel = _ContourTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
## 2. Aspect:
class _CommandSlopeAnalisys:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "TerrainSlope.svg")),
'Accel': "T, S",
'MenuText': 'Analisis de Pendiente',
'ToolTip': 'Analisis de Pendiente'
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _SlopeTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
## 3. Height:
class _CommandHeightAnalisys:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "TerrainHeight.svg")),
'Accel': "T, H",
'MenuText': 'Analisis de Altura',
'ToolTip': 'Analisis de Altura'
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _HeightTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
## 4. Orientation:
class _CommandOrientationAnalisys:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "TerrainOrientation.svg")),
'Accel': "T, H",
'MenuText': 'Analisis de Orientación',
'ToolTip': 'Analisis de Orientación'
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
self.TaskPanel = _OrientationTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
## 5. Commands
if FreeCAD.GuiUp:
class CommandTerrainAnalisysGroup:
def GetCommands(self):
return tuple(['Contours',
'HeightAnalisys',
'SlopeAnalisys',
'OrientationAnalisys'
])
def GetResources(self):
return { 'MenuText': QT_TRANSLATE_NOOP("",'Terrain Analisys'),
'ToolTip': QT_TRANSLATE_NOOP("",'Terrain Analisys')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('Contours', _CommandContours())
FreeCADGui.addCommand('SlopeAnalisys', _CommandSlopeAnalisys())
FreeCADGui.addCommand('HeightAnalisys', _CommandHeightAnalisys())
FreeCADGui.addCommand('OrientationAnalisys', _CommandOrientationAnalisys())
FreeCADGui.addCommand('TerrainAnalisys', CommandTerrainAnalisysGroup())

172
PVPlantTerrainAnalisys.ui Normal file
View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>form</class>
<widget class="QDialog" name="form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>386</width>
<height>511</height>
</rect>
</property>
<property name="windowTitle">
<string>Terrain Analisys</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Table setup</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>20</number>
</property>
<property name="spacing">
<number>2</number>
</property>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editTo">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>999999.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editFrom">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<double>-999999.000000000000000</double>
</property>
<property name="maximum">
<double>999999.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Valor final</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Valor inicial</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Pasos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="editSteps">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="tableWidget">
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Valor min</string>
</property>
</column>
<column>
<property name="text">
<string>Valor max</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

435
PVPlantTree.ui Normal file
View File

@@ -0,0 +1,435 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>222</width>
<height>280</height>
</rect>
</property>
<property name="windowTitle">
<string>Tracker:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Canopy</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Radius</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Divisiones</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editCrownExpansion">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Crown Expansion</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="editLeftUmbrellaEffect">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editSpikiness">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Umbrella effect</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="editLeafCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Spikiness</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editCanopyRadius">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
<property name="value">
<double>1.500000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editCanopyHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Trunk</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Radius</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editTrunkHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="maximum">
<double>50.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>2.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editTrunkRadius">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>50.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.150000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Faces</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editTrunkFaces">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="value">
<number>6</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

368
PVPlantTreeGenerator.py Normal file
View File

@@ -0,0 +1,368 @@
import math
import ArchComponent
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
import Part
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "FreeCAD Fixed Rack"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
DirResources = os.path.join(__dir__, "Resources")
DirIcons = os.path.join(DirResources, "Icons")
DirImages = os.path.join(DirResources, "Images")
def makeTree():
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tree")
Tree(obj)
ViewProviderTree(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
try:
folder = FreeCAD.ActiveDocument.Vegetation
except:
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Vegetation')
folder.Label = "Vegetation"
folder.addObject(obj)
return obj
class Tree(ArchComponent.Component):
""" A Shadow Tree Obcject """
def __init__(self, obj):
# Definición de variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setProperties(obj)
def setProperties(self, obj):
# Definicion de Propiedades:
pl = obj.PropertiesList
# CANOPY: ---------------------------------------------------------
if not ("CanopyHeight" in pl):
obj.addProperty("App::PropertyLength",
"CanopyHeight",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).CanopyHeight = 4000
if not ("CanopyRadius" in pl):
obj.addProperty("App::PropertyLength",
"CanopyRadius",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).CanopyRadius = 1500
if not ("Spikiness" in pl):
obj.addProperty("App::PropertyFloatConstraint",
"Spikiness",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).Spikiness = (0.5, 0.0, 1.0, 0.05) # (Default, Start, Finish, Step)
'''
if not ("Lumpiness" in pl):
obj.addProperty("App::PropertyFloatConstraint",
"Lumpiness",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).Lumpiness = (0.0, 0.0, 1.0, 0.05) #(Default, Start, Finish, Step)'''
if not ("CrownExpansion" in pl):
obj.addProperty("App::PropertyFloatConstraint",
"CrownExpansion",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).CrownExpansion = (1.0, 0.0, 2.0, 0.05) # (Default, Start, Finish, Step)
if not ("UmbrellaEffect" in pl):
obj.addProperty("App::PropertyFloatConstraint",
"UmbrellaEffect",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).UmbrellaEffect = (0.0, 0.0, 1.0, 0.05) # (Default, Start, Finish, Step)
if not ("LeafCount" in pl):
obj.addProperty("App::PropertyQuantity",
"LeafCount",
"Canopy",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).LeafCount = 20
# TRUNK: ------------------------------------------------------------------------------------------------------
if not ("TrunkHeight" in pl):
obj.addProperty("App::PropertyLength",
"TrunkHeight",
"Trunk",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).TrunkHeight = 2000
if not ("TrunkRadius" in pl):
obj.addProperty("App::PropertyLength",
"TrunkRadius",
"Trunk",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).TrunkRadius = 150
if not ("TrunkFaces" in pl):
obj.addProperty("App::PropertyQuantity",
"TrunkFaces",
"Trunk",
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
).TrunkFaces = 6
if not ("Type" in pl):
obj.addProperty("App::PropertyString",
"Type",
"Base",
"Type").Type = "Vegetable-Tree"
obj.setEditorMode("Type", 1)
self.Type = obj.Type
obj.Proxy = self
obj.IfcType = "Shading Device"
obj.setEditorMode("IfcType", 1)
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onChanged(self, obj, prop):
'''if prop in ["CanopyHeight", "CanopyHeight", "Spikiness", "CrownExpansion", "UmbrellaEffect",
"LeafCount"]:
self.canopy = self.createCanopy(obj)
if prop in ["TrunkHeight", "TrunkRadius", "TrunkFaces"]:
self.trunk = self.createTrunk(obj)'''
def createTrunk(self, obj):
import Part
angle = (math.pi * 2) / obj.TrunkFaces.Value
delta = obj.TrunkRadius.Value
pts = [FreeCAD.Vector(delta, 0, 0)]
for i in range(int(obj.TrunkFaces.Value) - 1):
ang = (i + 1) * angle
pts.append(FreeCAD.Vector(delta * math.cos(ang),
delta * math.sin(ang),
0))
pts.append(pts[0])
p1 = Part.makePolygon(pts)
p0 = p1.makeOffset2D(90, 2, False, False, True)
p2 = p1.makeOffset2D(-50, 2, False, False, True)
p0.Placement.Base.z = -150
p1.Placement.Base.z = 150
p2.Placement.Base.z = obj.TrunkHeight.Value - 250
return Part.makeLoft([p0, p1, p2], True, True, False)
def createCanopy(self, obj):
import Part
import random
import Mesh
import numpy as np
ncircles = int(obj.LeafCount.Value)
if ncircles % 2 == 0:
ncircles += 1
half_ncircles = int(ncircles / 2)
ncirclesumbrella = int(half_ncircles/2)
ncirclestop = ncircles - ncirclesumbrella
# 1. Create circles to define the sphere
circles = []
dist = 2 * obj.CanopyRadius.Value / (ncircles - 1)
margin = dist * 0.01
'''for i in range(half_ncircles + 1):
if i > 0:
d = (obj.CanopyRadius.Value - dist * i)
else:
d = obj.CanopyRadius.Value - margin
r = (obj.CanopyRadius.Value ** 2 - d ** 2) ** 0.5
c = Part.makeCircle(r)
circles.append(c)
ctmp = [c.copy() for c in circles]
ctmp.pop()
ctmp.reverse()
circles.extend(ctmp)'''
d = - obj.CanopyRadius.Value - dist
b = (obj.CanopyRadius.Value ** 2 - (dist * (ncirclesumbrella - 1)) ** 2) ** 0.5
for i in range(ncircles):
d += dist
r = (obj.CanopyRadius.Value ** 2 - d ** 2) ** 0.5
if i > ncirclesumbrella:
if obj.CrownExpansion < 1:
r = r * obj.CrownExpansion
if r == 0:
r = obj.CanopyRadius.Value * 0.01
c = Part.makeCircle(r)
circles.append(c)
# 2. Place circles
dist = obj.CanopyHeight.Value / ncircles
z = 0
#zmax = dist * half_ncircles
for idx in range(1, half_ncircles):
z += dist
circles[idx].Placement.Base.z = (1 - obj.UmbrellaEffect) * z
#c.Placement.Base.z = obj.UmbrellaEffect * (zmax - z) + z
dist1 = (obj.CanopyHeight.Value - z) / (half_ncircles + 1)
for idx in range(half_ncircles, ncircles):
c = circles[idx]
z += dist1
c.Placement.Base.z = z
# 3. noise generator
pts = []
val = (dist / 2 - margin) * obj.Spikiness
for c in circles:
tmppts = c.discretize(ncircles)
for j in range(len(tmppts)):
point = tmppts[j]
point.x += random.uniform(-val, val)
point.y += random.uniform(-val, val)
point.z += random.uniform(-val, val)
pts.append(point)
# 4. generate the mesh / solid
from scipy import spatial as sp_spatial
pts = np.array(pts)
hull = sp_spatial.ConvexHull(pts)
indices = hull.simplices
faces = pts[indices]
mesh = Mesh.Mesh(faces.tolist())
if len(mesh.Facets) == 0:
return None
mesh.harmonizeNormals()
'''if mesh.Facets[0].Normal.z < 0:
mesh.flipNormals()'''
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology, 0.1)
return Part.makeSolid(shape)
def execute(self, obj):
pl = obj.Placement
trunk = self.createTrunk(obj)
canopy = self.createCanopy(obj)
canopy.Placement.Base.z = obj.TrunkHeight.Value - 250 # - obj.CanopyRadius.Value / 2
obj.Shape = Part.makeCompound([trunk, canopy])
obj.Placement = pl
color = [(0.2510, 0.1255, 0.0)] * len(trunk.Faces)
color.extend([(0.0, 0.3922, 0.0)] * len(canopy.Faces))
obj.ViewObject.DiffuseColor = color
class ViewProviderTree(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
def getIcon(self):
return str(os.path.join(DirIcons, "tree(1).svg"))
class TreeTaskPanel(QtGui.QWidget):
def __init__(self, obj=None):
QtGui.QWidget.__init__(self)
self.obj = obj
if self.obj is None:
self.obj = makeTree()
self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantTree.ui")
self.layout = QtGui.QHBoxLayout(self)
self.layout.setContentsMargins(4, 4, 4, 4)
self.layout.addWidget(self.form)
self.form.editCanopyHeight.valueChanged.connect(self.Canopy)
self.form.editCanopyRadius.valueChanged.connect(self.Canopy)
self.form.editSpikiness.valueChanged.connect(self.Canopy)
self.form.editCrownExpansion.valueChanged.connect(self.Canopy)
self.form.editLeftUmbrellaEffect.valueChanged.connect(self.Canopy)
self.form.editLeafCount.valueChanged.connect(self.Canopy)
def Canopy(self):
self.obj.CanopyHeight = FreeCAD.Units.Quantity(self.form.editCanopyHeight.text()).Value
self.obj.CanopyRadius = FreeCAD.Units.Quantity(self.form.editCanopyRadius.text()).Value
self.obj.Spikiness = self.form.editSpikiness.value()
self.obj.CrownExpansion = self.form.editCrownExpansion.value()
self.obj.UmbrellaEffect = self.form.editLeftUmbrellaEffect.value()
self.obj.LeafCount = self.form.editLeafCount.value()
FreeCAD.ActiveDocument.recompute()
def accept(self):
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCADGui.Control.closeDialog()
return True
class _CommandTree:
"the PVPlant Tree command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "tree(1).svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTree", "Tree"),
'Accel': "S, T",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTree",
"Creates a Tree object from setup dialog.")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
import draftguitools.gui_trackers as DraftTrackers
self.tree = makeTree()
FreeCADGui.Snapper.getPoint(callback=self.getPoint,
movecallback=self.mousemove,
extradlg=self.taskbox(),
title="Position of the tree:")
def getPoint(self, point=None, obj=None):
self.tree.Placement.Base = point
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
self.tracker.finalize()
def mousemove(self, pt, snapInfo):
self.tree.Placement.Base = pt
def taskbox(self):
self.form = TreeTaskPanel(self.tree)
return self.form
if FreeCAD.GuiUp:
FreeCADGui.addCommand('PVPlantTree', _CommandTree())

1111
PVPlantTrench.py Normal file

File diff suppressed because it is too large Load Diff

302
PVPlantTrench.ui Normal file
View File

@@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>395</height>
</rect>
</property>
<property name="windowTitle">
<string>Tracker:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Shape</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Altura (m)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1.990000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox">
<item>
<property name="text">
<string>Rectangle</string>
</property>
</item>
<item>
<property name="text">
<string>Trapeze</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.030000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Layers</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="listLayers">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonUp">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDown">
<property name="text">
<string>Down</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="buttonAddLayer">
<property name="text">
<string>Add Layer</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDeleteLayer">
<property name="text">
<string>Delete Layer</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

803
Project/Area/PVPlantArea.py Normal file
View File

@@ -0,0 +1,803 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
import Part
import PVPlantSite
import Utils.PVPlantUtils as utils
import MeshPart as mp
if FreeCAD.GuiUp:
import FreeCADGui
from DraftTools import translate
else:
# \cond
def translate(ctxt,txt):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
import os
from PVPlantResources import DirIcons as DirIcons
__title__ = "PVPlant Areas"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")
''' Default Area: '''
def makeArea(points = None, type = 0):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Area")
if type == 0:
_Area(obj)
_ViewProviderArea(obj.ViewObject)
elif type == 1:
_ForbiddenArea(obj)
_ViewProviderForbiddenArea(obj.ViewObject)
if points:
obj.Points = points
return obj
class _Area:
def __init__(self, obj):
''' Initialize the Area object '''
self.Type = None
self.obj = None
def setProperties(self, obj):
pl = obj.PropertiesList
if not ("Base" in pl):
obj.addProperty("App::PropertyLink",
"Base",
"Area",
"Base wire"
).Base = None
if not ("Type" in pl):
obj.addProperty("App::PropertyString",
"Type",
"Area",
"Points that define the area"
).Type = "Area"
obj.setEditorMode("Type", 1)
self.Type = obj.Type
obj.Proxy = self
def onDocumentRestored(self, obj):
""" Method run when the document is restored """
self.setProperties(obj)
class _ViewProviderArea:
def __init__(self, vobj):
self.Object = vobj.Object
vobj.Proxy = self
def attach(self, vobj):
'''
Create Object visuals in 3D view.
'''
self.Object = vobj.Object
return
def getIcon(self):
'''
Return object treeview icon.
'''
return str(os.path.join(DirIcons, "area.svg"))
'''
def claimChildren(self):
"""
Provides object grouping
"""
return self.Object.Group
'''
def setEdit(self, vobj, mode=0):
"""
Enable edit
"""
return True
def unsetEdit(self, vobj, mode=0):
"""
Disable edit
"""
return False
def doubleClicked(self, vobj):
"""
Detect double click
"""
pass
def setupContextMenu(self, obj, menu):
"""
Context menu construction
"""
pass
def edit(self):
"""
Edit callback
"""
pass
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self, state):
"""
Get variables from file.
"""
return None
''' Frame Area '''
def makeFramedArea(base = None, select = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "FrameArea")
FrameArea(obj)
ViewProviderFrameArea(obj.ViewObject)
if base:
obj.Base = base
if select:
frames = []
for o in select:
if hasattr(o, "Proxy") and (o.Proxy.Type == "Tracker"):
if not (o in frames):
frames.append(o)
if len(frames) > 0:
print(frames)
obj.Frames = frames
try:
group = FreeCAD.ActiveDocument.FrameZones
except:
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'FrameZones')
group.Label = "FrameZones"
group.addObject(obj)
return obj
class FrameArea(_Area):
def __init__(self, obj):
_Area.__init__(self, obj)
self.setProperties(obj)
self.obj = None
def setProperties(self, obj):
_Area.setProperties(self, obj)
pl = obj.PropertiesList
if not ("Frames" in pl):
obj.addProperty("App::PropertyLinkList",
"Frames",
"Area",
"All the frames inside this area."
)
if not ("FrameNumber" in pl):
obj.addProperty("App::PropertyInteger",
"FrameNumber",
"Area",
"The number of frames inside this area."
)
obj.setEditorMode("FrameNumber", 1)
if not ("Type" in pl):
obj.addProperty("App::PropertyString",
"Type",
"Base",
"The facemaker type to use to build the profile of this object"
).Type = "FrameArea"
obj.setEditorMode("Type", 1)
self.Type = "FrameArea"
obj.Proxy = self
self.obj = obj
def onDocumentRestored(self, obj):
"""Method run when the document is restored."""
self.setProperties(obj)
def onBeforeChange(self, obj, prop):
''' '''
pass
def onChanged(self, obj, prop):
if prop == "Base":
if obj.Base.Shape is None:
obj.Shape = Part.Shape()
return
import Utils.PVPlantUtils as utils
base = obj.Base.Shape
land = PVPlantSite.get().Terrain.Mesh
vec = FreeCAD.Vector(0,0,1)
wire = utils.getProjected(base, vec)
tmp = mp.projectShapeOnMesh(wire, land, vec)
shape = Part.makeCompound([])
for section in tmp:
shape.add(Part.makePolygon(section))
obj.Shape = shape
if prop == "Frames":
lf = []
for o in obj.Frames:
if not hasattr(o, "Proxy"):
continue
if o.Proxy.Type == "Tracker":
lf.append(obj)
obj.Frames = lf
obj.FramesNumber = len(obj.Frames)
def addFrame(self, frame):
list = self.obj.Frames.copy()
list.append(frame)
self.obj.Frames = sorted(list, key=lambda x: x.Name)
def execute(self, obj):
''' execute '''
#_Area.execute(self, obj)
obj.FrameNumber = len(obj.Frames)
class ViewProviderFrameArea(_ViewProviderArea):
def __init__(self, vobj):
''' Set view properties. '''
self.Object = vobj.Object
vobj.Proxy = self
def getIcon(self):
''' Return object treeview icon '''
return str(os.path.join(DirIcons, "FrameArea.svg"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Base:
children.append(self.Object.Base)
return children
''' offsets '''
def makeOffsetArea(base = None, val=None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "OffsetArea")
OffsetArea(obj)
obj.Base = base
ViewProviderOffsetArea(obj.ViewObject)
if val:
obj.Distance = val
offsets = None
try:
offsetsgroup = FreeCAD.ActiveDocument.Offsets
except:
offsetsgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Offsets')
offsetsgroup.Label = "Offsets"
offsetsgroup.addObject(obj)
return obj
class OffsetArea(_Area):
def __init__(self, obj):
_Area.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
_Area.setProperties(self, obj)
pl = obj.PropertiesList
if not ("OffsetDistance" in pl):
obj.addProperty("App::PropertyDistance",
"OffsetDistance",
"OffsetArea",
"Base wire"
)
self.Type = obj.Type = "Area_Offset"
def onDocumentRestored(self, obj):
"""Method run when the document is restored."""
self.setProperties(obj)
def execute(self, obj):
import Utils.PVPlantUtils as utils
base = obj.Base.Shape
land = PVPlantSite.get().Terrain.Mesh
vec = FreeCAD.Vector(0, 0, 1)
wire = utils.getProjected(base, vec)
wire = wire.makeOffset2D(obj.OffsetDistance.Value, 2, False, False, True)
tmp = mp.projectShapeOnMesh(wire, land, vec)
pts = []
for section in tmp:
pts.extend(section)
obj.Shape = Part.makePolygon(pts)
class ViewProviderOffsetArea(_ViewProviderArea):
def getIcon(self):
''' Return object treeview icon. '''
return str(os.path.join(DirIcons, "offset.svg"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Base:
children.append(self.Object.Base)
return children
''' Forbidden Area: '''
def makeProhibitedArea(base = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ProhibitedArea")
ProhibitedArea(obj)
ViewProviderForbiddenArea(obj.ViewObject)
if base:
obj.Base = base
try:
group = FreeCAD.ActiveDocument.Exclusion
except:
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Exclusion')
group.Label = "Exclusions"
group.addObject(obj)
return obj
class ProhibitedArea(OffsetArea):
def __init__(self, obj):
OffsetArea.__init__(self, obj)
self.setProperties(obj)
def setProperties(self, obj):
OffsetArea.setProperties(self, obj)
self.Type = obj.Type = "ProhibitedArea"
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Method run when the document is restored."""
self.setProperties(obj)
class ViewProviderForbiddenArea(_ViewProviderArea):
def getIcon(self):
''' Return object treeview icon '''
return str(os.path.join(DirIcons, "area_forbidden.svg"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Base:
children.append(self.Object.Base)
return children
''' PV Area: '''
def makePVSubplant():
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PVSubplant")
PVSubplant(obj)
ViewProviderPVSubplant(obj.ViewObject)
return obj
class PVSubplant:
def __init__(self, obj):
self.setProperties(obj)
self.Type = None
self.obj = None
def setProperties(self, obj):
pl = obj.PropertiesList
if not "Setup" in pl:
obj.addProperty("App::PropertyEnumeration",
"Setup",
"PVSubplant",
"The facemaker type to use to build the profile of this object"
).Setup = ["String Inverter", "Central Inverter"]
if not ("Frames" in pl):
obj.addProperty("App::PropertyLinkList",
"Frames",
"PVSubplant",
"List of frames"
)
if not ("Inverters" in pl):
obj.addProperty("App::PropertyLinkList",
"Inverters",
"PVSubplant",
"List of Inverters"
)
if not ("Cables" in pl):
obj.addProperty("App::PropertyLinkList",
"Cables",
"PVSubplant",
"List of Cables"
)
if not "TotalPVModules" in pl:
obj.addProperty("App::PropertyQuantity",
"TotalPVModules",
"PVSubplant",
"The facemaker type to use to build the profile of this object"
)
if not "TotalPowerDC" in pl:
obj.addProperty("App::PropertyQuantity",
"TotalPowerDC",
"PVSubplant",
"The facemaker type to use to build the profile of this object"
)
if not "Color" in pl:
obj.addProperty("App::PropertyColor",
"Color",
"PVSubplant",
"Color"
)
if not "Type" in pl:
obj.addProperty("App::PropertyString",
"Type",
"Base",
"The facemaker type to use to build the profile of this object"
).Type = "PVSubplant"
obj.setEditorMode("Type", 1)
self.Type = obj.Type
self.obj = obj
obj.Proxy = self
def onDocumentRestored(self, obj):
"""Method run when the document is restored."""
self.setProperties(obj)
def onChanged(self, obj, prop):
if prop == "Color":
obj.ViewObject.LineColor = obj.Color
obj.ViewObject.PointColor = obj.Color
if prop == "Setup":
if obj.Setup == "Central Inverter":
if not ("StringBoxes" in obj.PropertiesList):
obj.addProperty("App::PropertyLinkList",
"StringBoxes",
"PVSubplant",
"List of String-Boxes"
)
else:
if hasattr(obj, "StringBoxes"):
obj.removeProperty("StringBoxes")
if prop == "Frames":
import numpy as np
# 1. Dibujar contorno:
maxdist = 6000
if len(obj.Frames) > 0:
onlyframes = []
for object in obj.Frames:
if object.Name.startswith("Tracker"):
onlyframes.append(object)
obj.Frames = onlyframes
pts = []
for frame in obj.Frames:
for panel in frame.Shape.SubShapes[0].SubShapes[0].SubShapes:
zm = panel.BoundBox.ZMax
for i in range(8):
pt = panel.BoundBox.getPoint(i)
if pt.z == zm:
pts.append(pt)
import MeshTools.Triangulation as Triangulation
import MeshTools.MeshGetBoundary as mgb
m = Triangulation.Triangulate(np.array(pts), MaxlengthLE=maxdist, use3d=False)
b = mgb.get_boundary(m)
obj.Shape = b
# 2. rellenar información
power = 0
modulenum = 0
for frame in obj.Frames:
modules = frame.Setup.ModuleColumns * frame.Setup.ModuleRows
power += frame.Setup.ModulePower.Value * modules
modulenum += modules
obj.TotalPowerDC = power
obj.TotalPVModules = modulenum
obj.ViewObject.LineWidth = 5
def execute(self, obj):
''' '''
pass
class ViewProviderPVSubplant:
def __init__(self, vobj):
''' Set view properties. '''
self.Object = None
vobj.Proxy = self
def attach(self, vobj):
''' Create Object visuals in 3D view. '''
self.Object = vobj.Object
return
def getIcon(self):
''' Return object treeview icon. '''
return str(os.path.join(DirIcons, "subplant.svg"))
def claimChildren(self):
""" Provides object grouping """
children = []
if self.Object.Frames:
children.extend(self.Object.Frames)
return children
def __getstate__(self):
return None
'''def onDelete(self, feature, subelements):
try:
for obj in self.claimChildren():
obj.ViewObject.show()
except Exception as err:
FreeCAD.Console.PrintError("Error in onDelete: " + str(err))
return True
def canDragObjects(self):
return True
def canDropObjects(self):
return True
def canDragObject(self, dragged_object):
return True
def canDropObject(self, incoming_object):
return hasattr(incoming_object, 'Shape')
def dragObject(self, selfvp, dragged_object):
objs = self.Object.Objects
objs.remove(dragged_object)
self.Object.Objects = objs
def dropObject(self, selfvp, incoming_object):
self.Object.Objects = self.Object.Objects + [incoming_object]
def setEdit(self, vobj, mode=0):
"""
Enable edit
"""
return True
def unsetEdit(self, vobj, mode=0):
"""
Disable edit
"""
return False
def doubleClicked(self, vobj):
"""
Detect double click
"""
pass
def setupContextMenu(self, obj, menu):
"""
Context menu construction
"""
pass
def edit(self):
"""
Edit callback
"""
pass
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self,state):
"""
Get variables from file.
"""
return None'''
# Comandos: -----------------------------------------------------------------------------------------------------------
class CommandDivideArea:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area.svg")),
'Accel': "A, D",
'MenuText': "Divide Area",
'ToolTip': "Allowed Area"}
def Activated(self):
sel = FreeCADGui.Selection.getSelection()[0]
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class CommandBoundary:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area.svg")),
'Accel': "A, B",
'MenuText': "Area",
'ToolTip': "Allowed Area"}
def Activated(self):
sel = FreeCADGui.Selection.getSelection()[0]
obj = makeArea([ver.Point for ver in sel.Shape.Vertexes])
#taskd = _PVPlantPlacementTaskPanel()
#FreeCADGui.Control.showDialog(taskd)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class CommandFrameArea:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "FrameArea.svg")),
'Accel': "A, F",
'MenuText': "Frame Area",
'ToolTip': "Frame Area"}
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
makeFramedArea(None, sel)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class CommandProhibitedArea:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area_forbidden.svg")),
'Accel': "A, F",
'MenuText': "Prohibited Area",
'ToolTip': "Prohibited Area"}
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
makeProhibitedArea(sel[0])
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class CommandPVSubplant:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "subplant.svg")),
'Accel': "A, P",
'MenuText': "PV Subplant",
'ToolTip': "PV Subplant"}
def Activated(self):
area = makePVSubplant()
sel = FreeCADGui.Selection.getSelection()
for obj in sel:
if obj.Name[:7] == "Tracker":
frame_list = area.Frames
frame_list.append(obj)
area.Frames = frame_list
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class CommandOffsetArea:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "offset.svg")),
'Accel': "A, O",
'MenuText': "OffsetArea",
'ToolTip': "OffsetArea"}
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
base = None
if sel:
base = sel[0]
obj = makeOffsetArea(base)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
class CommandAreaGroup:
def GetCommands(self):
return tuple([#'Area',
'FrameArea',
'ForbiddenArea',
'PVSubplant',
'OffsetArea'
])
def GetResources(self):
return {'MenuText': 'Areas',
'ToolTip': 'Areas'
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
#FreeCADGui.addCommand('Area', CommandBoundary())
FreeCADGui.addCommand('FrameArea', CommandFrameArea())
FreeCADGui.addCommand('ForbiddenArea', CommandProhibitedArea())
FreeCADGui.addCommand('PVSubplant', CommandPVSubplant())
FreeCADGui.addCommand('OffsetArea', CommandOffsetArea())
FreeCADGui.addCommand('PVPlantAreas', CommandAreaGroup())

View File

@@ -0,0 +1,167 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Area Utils"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import PVPlantResources
def splitArea(area, tool):
if (not area) or (not tool):
return None
import Part, TechDraw
import BOPTools.SplitAPI as splitter
tool = tool.Shape
if tool.BoundBox.ZMax != tool.BoundBox.ZMin:
tool = TechDraw.projectEx(tool, FreeCAD.Vector(0, 0, 1))[0]
tool = tool.extrude(FreeCAD.Vector(0, 0, 1000))
tool.Placement.Base.z -= 500
shape = area.Shape
if shape.BoundBox.ZMax != shape.BoundBox.ZMin:
shape = TechDraw.projectEx(shape, FreeCAD.Vector(0, 0, 1))[0]
face = Part.Face(Part.Wire(shape.Edges))
return splitter.slice(face, [tool, ], "Split")
class splitAreaTaskPanel:
def __init__(self):
self.area = None
self.tool = None
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "Project", "Area", "PVPlantSplitArea.ui"))
self.form.buttonAreaSel.clicked.connect(self.addArea)
self.form.buttonToolSel.clicked.connect(self.addTool)
#self.view = FreeCADGui.ActiveDocument.ActiveView
#self.call = self.view.addEventCallback("SoEvent", self.action)
def addArea(self):
self.area = FreeCADGui.Selection.getSelection()[0]
self.form.lineArea.setText(self.area.Name)
def addTool(self):
self.tool = FreeCADGui.Selection.getSelection()[0]
self.form.lineTool.setText(self.tool.Name)
def accept(self):
import Part
self.closeForm()
results = splitArea(self.area, self.tool)
if isinstance(results, Part.Compound):
for face in results.Faces:
Part.show(face.Wire, self.area.Label + "-split")
elif isinstance(results, list):
for face in results:
Part.show(face.Wire, self.area.Label + "-split")
else:
Part.show(results)
if self.form.checkBoxDeleteTool.isChecked():
FreeCAD.ActiveDocument.removeObject(self.tool.Name)
if self.form.checkBoxDeleteArea.isChecked():
FreeCAD.ActiveDocument.removeObject(self.area.Name)
return True
def reject(self):
print(" .. Rejecting .. ")
if self.new:
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
self.closeForm()
return True
def closeForm(self):
print(" .. Closing .. ")
#self.view.removeEventCallback("SoEvent", self.call)
FreeCADGui.Control.closeDialog()
def joinAreas(areas):
if len(areas) == 0:
return None
import TechDraw
shapes = []
for area in areas:
shape = area.Shape
if shape.BoundBox.ZMax != shape.BoundBox.ZMin:
shape = TechDraw.projectEx(shape, FreeCAD.Vector(0, 0, 1))[0]
shapes.append(shape)
shape = shapes.pop()
shape.fuse(shapes)
return shape
class CommandSplitArea:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "split_area.svg")),
'Accel': "A, S",
'MenuText': "Split Area",
'ToolTip': "Split Area"}
def IsActive(self):
return (not FreeCAD.ActiveDocument is None and
not FreeCAD.ActiveDocument.findObjects(Name="ProhibitedArea") is None and
not FreeCAD.ActiveDocument.findObjects(Name="OffsetArea") is None)
def Activated(self):
self.TaskPanel = splitAreaTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return
class CommandJoinAreas:
def GetResources(self):
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "split_area.svg")),
'Accel': "A, J",
'MenuText': "Join Areas",
'ToolTip': "Join Areas"}
def IsActive(self):
return (not FreeCAD.ActiveDocument is None and
not FreeCAD.ActiveDocument.findObjects(Name="ProhibitedArea") is None and
not FreeCAD.ActiveDocument.findObjects(Name="OffsetArea") is None)
def Activated(self):
self.TaskPanel = splitAreaTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return
if FreeCAD.GuiUp:
FreeCADGui.addCommand('SplitArea', CommandSplitArea())
FreeCADGui.addCommand('JoinAreas', CommandJoinAreas())

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formSplitArea</class>
<widget class="QWidget" name="formSplitArea">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>313</width>
<height>301</height>
</rect>
</property>
<property name="windowTitle">
<string>Split Area</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Herramienta de corte</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineTool">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonToolSel">
<property name="text">
<string>Seleccionar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Area</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLineEdit" name="lineArea">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="buttonAreaSel">
<property name="text">
<string>Seleccionar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Resultado</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkBoxDeleteTool">
<property name="text">
<string>Borrar herramienta de corte</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxDeleteArea">
<property name="text">
<string>Borrar Area inicial</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

163
Project/ProjectSetup.py Normal file
View File

@@ -0,0 +1,163 @@
import FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui, QtCore
import os
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Project Setup"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
from PVPlantResources import DirIcons as DirIcons
class ProjectSetupDialog(QtGui.QWidget):
'''The editmode TaskPanel to select what you want to export'''
def __init__(self):
QtGui.QWidget.__init__(self)
import os
# self.form:
self.form = FreeCADGui.PySideUic.loadUi(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "ProjectSetup.ui"))
#self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))
self.bcnf = FreeCADGui.UiLoader().createWidget("Gui::ColorButton")
self.bcnf.setProperty('color', QtGui.QColor(255, 126, 126))
self.bcsf = FreeCADGui.UiLoader().createWidget("Gui::ColorButton")
self.bcsf.setProperty('color', QtGui.QColor(255, 126, 0))
gridLayout = self.form.tab1.layout()
gridLayout.addWidget(self.bcnf, 2, 1)
gridLayout.addWidget(self.bcsf, 2, 2)
self.setdefaultvalues()
self.layout = QtGui.QHBoxLayout(self)
self.layout.setContentsMargins(4, 4, 4, 4)
self.layout.addWidget(self.form)
self.form.buttonAccept.clicked.connect(self.onAcceptClick)
self.form.buttonCancel.clicked.connect(self.onCancelClick)
def setdefaultvalues(self):
doc = FreeCAD.ActiveDocument
pl = doc.PropertiesList
if "MaximumTiltPositive" in pl:
self.form.editNFTL.setValue(doc.MaximumTiltPositive)
if "MaximumTiltPositiveColor" in pl:
col = QtGui.QColor()
col.setRgbF(doc.MaximumTiltPositiveColor[0], doc.MaximumTiltPositiveColor[1],
doc.MaximumTiltPositiveColor[2])
self.bcsf.setProperty('color', col)
if "MaximumTiltNegative" in pl:
self.form.editSFTL.setValue(-doc.MaximumTiltNegative)
if "MaximumTiltNegativeColor" in pl:
col = QtGui.QColor()
col.setRgbF(doc.MaximumTiltNegativeColor[0], doc.MaximumTiltNegativeColor[1],
doc.MaximumTiltNegativeColor[2])
self.bcnf.setProperty('color', col)
if "MaximumWestEastSlope" in pl:
self.form.editWETL.setValue(doc.MaximumWestEastSlope)
def onAcceptClick(self):
doc = FreeCAD.ActiveDocument
pl = doc.PropertiesList
if not ("FramesChecking" in pl):
doc.addProperty("App::PropertyBool",
"FramesChecking",
"PVPlantProject",
"All the frames inside this area."
).FramesChecking = False
doc.setEditorMode("FramesChecking", 1) # no editable
doc.setEditorMode("FramesChecking", 2) # no visible
if not ("MaximumTiltPositive" in pl):
doc.addProperty("App::PropertyAngle",
"MaximumTiltPositive",
"PVPlantProject",
"All the frames inside this area."
)
if not ("MaximumTiltPositiveColor" in pl):
doc.addProperty("App::PropertyColor",
"MaximumTiltPositiveColor",
"PVPlantProject",
"All the frames inside this area."
)
if not ("MaximumTiltNegative" in pl):
doc.addProperty("App::PropertyAngle",
"MaximumTiltNegative",
"PVPlantProject",
"All the frames inside this area."
)
if not ("MaximumTiltNegativeColor" in pl):
doc.addProperty("App::PropertyColor",
"MaximumTiltNegativeColor",
"PVPlantProject",
"All the frames inside this area."
)
if not ("MaximumWestEastSlope" in pl):
doc.addProperty("App::PropertyAngle",
"MaximumWestEastSlope",
"PVPlantProject",
"All the frames inside this area."
)
doc.MaximumTiltPositive = self.form.editNFTL.value()
col = self.bcsf.property('color')
doc.MaximumTiltPositiveColor = (col.redF(), col.greenF(), col.blueF())
doc.MaximumTiltNegative = -self.form.editSFTL.value()
col = self.bcnf.property('color')
doc.MaximumTiltNegativeColor = (col.redF(), col.greenF(), col.blueF())
doc.MaximumWestEastSlope = self.form.editWETL.value()
self.closeForm()
def onCancelClick(self):
self.closeForm()
def closeForm(self):
self.close()
class CommandProjectSetup:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "flash.svg")),
'Accel': "P, S",
'MenuText': "Project Setup",
'ToolTip': "Setup all the variable for this project"}
def Activated(self):
taskd = ProjectSetupDialog()
taskd.setParent(FreeCADGui.getMainWindow())
taskd.setWindowFlags(QtCore.Qt.Window)
taskd.show()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('ProjectSetup', CommandProjectSetup())

201
Project/ProjectSetup.ui Normal file
View File

@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formProjectSetup</class>
<widget class="QDialog" name="formProjectSetup">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>485</width>
<height>384</height>
</rect>
</property>
<property name="windowTitle">
<string>Export to PVSyst</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab1">
<attribute name="title">
<string>Frame Tolerances</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>9</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Maximum north-south slope:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Frame coloring:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Maximum west-east slope:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="editWETL">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> º</string>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="value">
<double>8.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="editSFTL">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> º</string>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="value">
<double>2.800000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QWidget" name="widget_2" native="true"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>South facing</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editNFTL">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string> º</string>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>North Facing</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab2">
<attribute name="title">
<string>tab2</string>
</attribute>
</widget>
<widget class="QWidget" name="tab3">
<attribute name="title">
<string>Page</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="buttonAccept">
<property name="text">
<string>Aceptar</string>
</property>
<property name="icon">
<iconset theme="Accept">
<normaloff>../../../../../.designer/backup</normaloff>../../../../../.designer/backup</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCancel">
<property name="text">
<string>Cancelar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

108
Project/utils/renamer.py Normal file
View File

@@ -0,0 +1,108 @@
# /**********************************************************************
# * *
# * 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 FreeCAD
if FreeCAD.GuiUp:
import FreeCADGui, os
from PySide import QtCore
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
def rename(objects, mask, mode=0):
'''
mode = 0/1/2/3
0: izquierda a derecha - arriba a abajo
1: arriba a abajo - izquierda a derecha
'''
# sort:
tmp = sorted(objects, key=lambda x: (x.Placement.Base.x,
x.Placement.Base.y))
for idx, obj in tmp:
obj.Name = name
class renamerTaskPanel:
def __init__(self, obj=None):
self.obj = obj
# -------------------------------------------------------------------------------------------------------------
# Module widget form
# -------------------------------------------------------------------------------------------------------------
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui")
self.formRack.widgetTracker.setVisible(False)
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
self.form = [self.formRack, self.formPiling]
def accept(self):
self.closeForm()
return True
def reject(self):
self.closeForm()
return False
def closeForm(self):
FreeCADGui.Control.closeDialog()
class _CommandRenamer:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "trackersetup.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTracker", "TrackerSetup"),
'Accel': "R, F",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTracker",
"Creates a TrackerSetup object from setup dialog.")}
def IsActive(self):
return (not FreeCAD.ActiveDocument is None and
not FreeCAD.ActiveDocument.getObject("Site") is None)
def Activated(self):
self.TaskPanel = renamerTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return

90
Project/utils/renamer.ui Normal file
View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>255</width>
<height>337</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Renombrar por sub-planta</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>GroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboBox">
<item>
<property name="text">
<string>New Item</string>
</property>
</item>
<item>
<property name="text">
<string>New Item</string>
</property>
</item>
<item>
<property name="text">
<string>New Item</string>
</property>
</item>
<item>
<property name="text">
<string>New Item</string>
</property>
</item>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Leyenda</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,820 @@
<?xml version='1.0' standalone='yes' ?>
<LVData xmlns="http://www.ni.com/LVData">
<Version>12.0f3</Version>
<Cluster>
<Name>Cable Data</Name>
<NumElts>11</NumElts>
<Cluster>
<Name>General</Name>
<NumElts>9</NumElts>
<String>
<Name>Title</Name>
<Val>0.5mm2 Cu_Single core, 1kV V-90 insulated (unsheathed)</Val>
</String>
<String>
<Name>Description</Name>
<Val>To AS/NZS 5000.1</Val>
</String>
<Path>
<Name>Path</Name>
<Val>C:\Users\User\Desktop\Cables\Olex LV Handbook\PVC Insulated\0.5mm2 Cu_Single core, 1kV V-90 insulated (unsheathed).xml</Val>
</Path>
<DBL>
<Name>Frequency (Hz)</Name>
<Val>50.00000000000000</Val>
</DBL>
<EL>
<Name>Phases</Name>
<Choice>DC</Choice>
<Choice>Single phase</Choice>
<Choice>Three phase</Choice>
<Val>2</Val>
</EL>
<EL>
<Name>Cores</Name>
<Choice>Single core</Choice>
<Choice>Multicore</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Voltage, U (phase to phase)</Name>
<Val>1000.00000000000000</Val>
</DBL>
<DBL>
<Name>Daily load factor, L</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Conductor temperature, theta (deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Conductor</Name>
<NumElts>10</NumElts>
<EW>
<Name>Conductor size (mm2)</Name>
<Choice>0.5 mm2</Choice>
<Choice>0.75 mm2</Choice>
<Choice>1 mm2</Choice>
<Choice>1.5 mm2</Choice>
<Choice>2.5 mm2</Choice>
<Choice>4 mm2</Choice>
<Choice>6 mm2</Choice>
<Choice>10 mm2</Choice>
<Choice>16 mm2</Choice>
<Choice>25 mm2</Choice>
<Choice>35 mm2</Choice>
<Choice>50 mm2</Choice>
<Choice>70 mm2</Choice>
<Choice>95 mm2</Choice>
<Choice>120 mm2</Choice>
<Choice>150 mm2</Choice>
<Choice>185 mm2</Choice>
<Choice>240 mm2</Choice>
<Choice>300 mm2</Choice>
<Choice>400 mm2</Choice>
<Choice>500 mm2</Choice>
<Choice>630 mm2</Choice>
<Choice>800 mm2</Choice>
<Choice>1000 mm2</Choice>
<Choice>1200 mm2</Choice>
<Choice>1600 mm2</Choice>
<Choice>1800 mm2</Choice>
<Choice>2000 mm2</Choice>
<Choice>2500 mm2</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Conductor class</Name>
<Choice>Class 1 solid conductors for single or multicore cables</Choice>
<Choice>Class 2 stranded conductors for single or multicore cables</Choice>
<Choice>Class 5 flexible copper conductors for single or multicore cables</Choice>
<Choice>Class 6 flexible copper conductors for single or multicore cables</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Conductor material</Name>
<Choice>Copper, plain wires</Choice>
<Choice>Copper, metal-coated wires</Choice>
<Choice>Aluminium wires</Choice>
<Choice>Aluminium alloy wires</Choice>
<Val>0</Val>
</EW>
<Boolean>
<Name>Hard-drawn Cu? (aerial cable)</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Conductor Type</Name>
<Choice>Copper_Round, stranded_Dried &amp; impregnated</Choice>
<Choice>Copper_Round, stranded_Not dried &amp; impregnated</Choice>
<Choice>Copper_Round, 4 segment</Choice>
<Choice>Copper_Hollow, helical stranded_Dried and impregnated</Choice>
<Choice>Copper_Sector-shaped_Dried and impregnated</Choice>
<Choice>Copper_Sector-shaped_Not dried and impregnated</Choice>
<Choice>Aluminium_Round, stranded</Choice>
<Choice>Aluminium_Round, 4 segment</Choice>
<Choice>Aluminium_Round, 5 segment</Choice>
<Choice>Aluminium_Round, 6 segment</Choice>
<Choice>Aluminium, Segmental with peripheral strands</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Conductor shape (self inductance)</Name>
<Choice>Circular</Choice>
<Choice>Circular compacted</Choice>
<Choice>Shaped</Choice>
<Val>0</Val>
</EW>
<DBL>
<Name>External diameter of cable, De (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>DC resistance</Name>
<NumElts>3</NumElts>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Standards</Name>
<NumElts>3</NumElts>
<EW>
<Name>DC resistance calculation based on:</Name>
<Choice>Insulation max. operating temperature, Theta (deg.C)</Choice>
<Choice>Conductor operating temperature (deg.C)</Choice>
<Val>0</Val>
</EW>
<DBL>
<Name>Conductor operating temperature (deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
<EW>
<Name>Standards</Name>
<Choice>IEC 60228:2004</Choice>
<Choice>AS/NZS 1125:2001</Choice>
<Val>1</Val>
</EW>
</Cluster>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>3.60000000000000E-2</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>3.93000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Conductor No. of Wires</Name>
<NumElts>2</NumElts>
<EL>
<Name>Mode</Name>
<Choice>Based on conductor size (mm2) and IEC 60228</Choice>
<Choice>Specify no. of conductors</Choice>
<Val>0</Val>
</EL>
<I32>
<Name>No. of conductor wires</Name>
<Val>0</Val>
</I32>
</Cluster>
<Cluster>
<Name>Conductor diameter</Name>
<NumElts>2</NumElts>
<EL>
<Name>Mode</Name>
<Choice>Based on conductor size (mm2)</Choice>
<Choice>Specify conductor diameter</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Nominal conductor diameter, dc (m)</Name>
<Val>8.00000000000000E-4</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Conductor shield</Name>
<NumElts>2</NumElts>
<Boolean>
<Name>Conductor shield?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>3.60000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Insulation</Name>
<NumElts>5</NumElts>
<EW>
<Name>Type of cable insulation</Name>
<Choice>Impregnated paper_Solid type, fully- pre- or mass-impregnated non-draining</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 36kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 87kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 160kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 220kV</Choice>
<Choice>Impregnated paper_Oil-pressure pipe-type</Choice>
<Choice>Impregnated paper_External gas pressure</Choice>
<Choice>Impregnated paper_Internal gas pressure</Choice>
<Choice>Butyl rubber</Choice>
<Choice>EPR_up to and including 18/30 (36) kV</Choice>
<Choice>EPR_greater than 18/30 (36) kV</Choice>
<Choice>PVC</Choice>
<Choice>PE (HD and LD)</Choice>
<Choice>XLPE_Unfilled_up to and including 18/30 (36) kV</Choice>
<Choice>XLPE_Unfilled_greater than 18/30 (36) kV</Choice>
<Choice>XLPE_Filled_greater than 18/30 (36) kV</Choice>
<Choice>PPL_equal to or greater than 63/110 kV</Choice>
<Val>11</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Epsilon</Name>
<Val>8.00000000000000</Val>
</DBL>
<DBL>
<Name>Tandelta</Name>
<Val>0.10000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>8.00000000000000E-4</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>2.40000000000000E-3</Val>
</DBL>
</Cluster>
<I32>
<Name>Insulation. Max. operating temp., Theta (deg.C)</Name>
<Val>90</Val>
</I32>
</Cluster>
<Cluster>
<Name>Insulation screen</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Insulation screen?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Single core</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Semi-conductor screen</Choice>
<Choice>Copper screen tape</Choice>
<Choice>Aluminium screen tape</Choice>
<Val>0</Val>
</EW>
</Cluster>
<Cluster>
<Name>Multi-core</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Belted (unscreened)-Multicore</Choice>
<Choice>Semi-conductor screen</Choice>
<Choice>Copper screen tape</Choice>
<Choice>Aluminium screen tape</Choice>
<Val>1</Val>
</EW>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Sheath</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Cable sheath?</Name>
<Val>0</Val>
</Boolean>
<EL>
<Name>Multicore</Name>
<Choice>Common</Choice>
<Choice>Sheath per phase</Choice>
<Val>0</Val>
</EL>
<Cluster>
<Name>Type of sheath</Name>
<NumElts>3</NumElts>
<EW>
<Name>Type of sheath</Name>
<Choice>Aluminium</Choice>
<Choice>Lead</Choice>
<Choice>Lead with reinforcing tape</Choice>
<Choice>Copper</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Construction and dimensions</Name>
<NumElts>2</NumElts>
<EW>
<Name>Construction</Name>
<Choice>Non-corrugated</Choice>
<Choice>Corrugated</Choice>
<Choice>Longitudinally corrugated</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Dimensions</Name>
<NumElts>3</NumElts>
<Cluster>
<Name>Non-corrugated</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Corrugated</Name>
<NumElts>3</NumElts>
<DBL>
<Name>Thickness of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Inner diameter of corrugated sheath (m)</Name>
<Val>2.40000000000000E-3</Val>
</DBL>
<DBL>
<Name>Outer diameter of corrugated sheath (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Longitudinally corrugated</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Corrugation height</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Inner radius of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Outer radius of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>Tape</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Overlap (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Width (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Accumulation factor</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>Calculated properties</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Diameter over sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>DC resistance at 25 deg.C. (Ohm/m)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Sheath reinforcing tape</Name>
<NumElts>3</NumElts>
<Boolean>
<Name>Sheath reinforcing tape?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Material</Name>
<NumElts>3</NumElts>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<EW>
<Name>Material</Name>
<Choice>Copper</Choice>
<Choice>Brass/bronze</Choice>
<Choice>Zinc</Choice>
<Choice>Stainless steel</Choice>
<Choice>Steel</Choice>
<Val>0</Val>
</EW>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Width (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of tapes</Name>
<Val>1</Val>
</I32>
</Cluster>
</Cluster>
<Cluster>
<Name>Concentric neutral</Name>
<NumElts>6</NumElts>
<Boolean>
<Name>Concentric neutral?</Name>
<Val>0</Val>
</Boolean>
<EL>
<Name>Multicore</Name>
<Choice>Common</Choice>
<Choice>Around each core</Choice>
<Val>0</Val>
</EL>
<EW>
<Name>Material</Name>
<Choice>Custom</Choice>
<Choice>Copper</Choice>
<Choice>Brass/bronze</Choice>
<Choice>Zinc</Choice>
<Choice>Stainless steel</Choice>
<Val>1</Val>
</EW>
<EW>
<Name>Construction</Name>
<Choice>Round wires</Choice>
<Choice>Flat straps</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Electrical and magnetic material properties</Name>
<NumElts>1</NumElts>
<Cluster>
<Name>Electrical resistivity and temp. coefficient</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>1.72410000000000E-8</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>3.93000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Dimensions_Round Wire</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of wires</Name>
<Val>1</Val>
</I32>
</Cluster>
<Cluster>
<Name>Dimensions_Flat straps</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Strap length (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of straps</Name>
<Val>1</Val>
</I32>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Bedding</Name>
<NumElts>5</NumElts>
<Boolean>
<Name>Armour bedding?</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Material</Name>
<Choice>Compounded jute and fibrous materials</Choice>
<Choice>Rubber sandwich protection</Choice>
<Choice>Polychloroprene</Choice>
<Choice>PVC up to and including 35 kV</Choice>
<Choice>PVC greater than 35 kV</Choice>
<Choice>PVC/bitumen on corrugated aluminium sheaths</Choice>
<Choice>PE</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>1</NumElts>
<DBL>
<Name>Thermal resistivity, rhoT (Cm/W)</Name>
<Val>6.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.99880000000000</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>2.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Armour/reinforcing tape</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Armour/reinforcing tape?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Material</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Custom_non-magnetic tape</Choice>
<Choice>Steel tape reinforcement</Choice>
<Choice>Custom_non-magnetic wires</Choice>
<Choice>Custom_magnetic wires</Choice>
<Choice>Steel wires_touching</Choice>
<Choice>Steel wires_not touching</Choice>
<Choice>Copper armour wires</Choice>
<Choice>Stainless steel wires</Choice>
<Choice>TECK armour</Choice>
<Val>4</Val>
</EW>
</Cluster>
<Cluster>
<Name>Electrical and magnetic material properties</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Electrical resistivity and temp. coefficient</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>1.38000000000000E-7</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>4.50000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Magnetic properties (if applicable)</Name>
<NumElts>4</NumElts>
<EL>
<Name>Magnetic properties</Name>
<Choice>Standard</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Longitudinal relative permeability, Mue-e</Name>
<Val>10.00000000000000</Val>
</DBL>
<DBL>
<Name>Traverse relative permeability, Mue-t</Name>
<Val>45.00000000000000</Val>
</DBL>
<DBL>
<Name>Gamma angle (degrees)</Name>
<Val>45.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Dimensions_Wire</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of wires</Name>
<Val>1</Val>
</I32>
</Cluster>
<Cluster>
<Name>Dimensions_Tape</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Cross-sectional area of tape armour (m2)</Name>
<Val>1.00000000000000</Val>
</DBL>
<EW>
<Name>Lay of tapes (reinforcement resistance)</Name>
<Choice>Very long lay (longitudinal tapes)</Choice>
<Choice>Wound at approximately 54 degrees</Choice>
<Choice>Wound in very short lay (circumferential tapes)</Choice>
<Choice>Layers of tapes in contact with each other having a very short lay</Choice>
<Val>0</Val>
</EW>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Jacket/Serving</Name>
<NumElts>5</NumElts>
<Boolean>
<Name>Jacket/Serving?</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Material</Name>
<Choice>Compounded jute and fibrous materials</Choice>
<Choice>Rubber sandwich</Choice>
<Choice>Polychropropene</Choice>
<Choice>PVC up to and including 35 kV</Choice>
<Choice>PVC above 35 kV</Choice>
<Choice>Butyl rubber</Choice>
<Choice>Coal tar wrapping</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>1</NumElts>
<DBL>
<Name>Thermal resistivity, rhoT (Cm/W)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.80000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
</Cluster>
</LVData>

View File

@@ -0,0 +1,820 @@
<?xml version='1.0' standalone='yes' ?>
<LVData xmlns="http://www.ni.com/LVData">
<Version>12.0f3</Version>
<Cluster>
<Name>Cable Data</Name>
<NumElts>11</NumElts>
<Cluster>
<Name>General</Name>
<NumElts>9</NumElts>
<String>
<Name>Title</Name>
<Val>1.5mm2 Cu_Single core, 1kV V-90 insulated (unsheathed)</Val>
</String>
<String>
<Name>Description</Name>
<Val>To AS/NZS 5000.1</Val>
</String>
<Path>
<Name>Path</Name>
<Val>C:\Users\User\Desktop\Cables\Olex LV Handbook\PVC Insulated\1.5mm2 Cu_Single core, 1kV V-90 insulated (unsheathed).xml</Val>
</Path>
<DBL>
<Name>Frequency (Hz)</Name>
<Val>50.00000000000000</Val>
</DBL>
<EL>
<Name>Phases</Name>
<Choice>DC</Choice>
<Choice>Single phase</Choice>
<Choice>Three phase</Choice>
<Val>2</Val>
</EL>
<EL>
<Name>Cores</Name>
<Choice>Single core</Choice>
<Choice>Multicore</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Voltage, U (phase to phase)</Name>
<Val>1000.00000000000000</Val>
</DBL>
<DBL>
<Name>Daily load factor, L</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Conductor temperature, theta (deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Conductor</Name>
<NumElts>10</NumElts>
<EW>
<Name>Conductor size (mm2)</Name>
<Choice>0.5 mm2</Choice>
<Choice>0.75 mm2</Choice>
<Choice>1 mm2</Choice>
<Choice>1.5 mm2</Choice>
<Choice>2.5 mm2</Choice>
<Choice>4 mm2</Choice>
<Choice>6 mm2</Choice>
<Choice>10 mm2</Choice>
<Choice>16 mm2</Choice>
<Choice>25 mm2</Choice>
<Choice>35 mm2</Choice>
<Choice>50 mm2</Choice>
<Choice>70 mm2</Choice>
<Choice>95 mm2</Choice>
<Choice>120 mm2</Choice>
<Choice>150 mm2</Choice>
<Choice>185 mm2</Choice>
<Choice>240 mm2</Choice>
<Choice>300 mm2</Choice>
<Choice>400 mm2</Choice>
<Choice>500 mm2</Choice>
<Choice>630 mm2</Choice>
<Choice>800 mm2</Choice>
<Choice>1000 mm2</Choice>
<Choice>1200 mm2</Choice>
<Choice>1600 mm2</Choice>
<Choice>1800 mm2</Choice>
<Choice>2000 mm2</Choice>
<Choice>2500 mm2</Choice>
<Val>3</Val>
</EW>
<EW>
<Name>Conductor class</Name>
<Choice>Class 1 solid conductors for single or multicore cables</Choice>
<Choice>Class 2 stranded conductors for single or multicore cables</Choice>
<Choice>Class 5 flexible copper conductors for single or multicore cables</Choice>
<Choice>Class 6 flexible copper conductors for single or multicore cables</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Conductor material</Name>
<Choice>Copper, plain wires</Choice>
<Choice>Copper, metal-coated wires</Choice>
<Choice>Aluminium wires</Choice>
<Choice>Aluminium alloy wires</Choice>
<Val>0</Val>
</EW>
<Boolean>
<Name>Hard-drawn Cu? (aerial cable)</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Conductor Type</Name>
<Choice>Copper_Round, stranded_Dried &amp; impregnated</Choice>
<Choice>Copper_Round, stranded_Not dried &amp; impregnated</Choice>
<Choice>Copper_Round, 4 segment</Choice>
<Choice>Copper_Hollow, helical stranded_Dried and impregnated</Choice>
<Choice>Copper_Sector-shaped_Dried and impregnated</Choice>
<Choice>Copper_Sector-shaped_Not dried and impregnated</Choice>
<Choice>Aluminium_Round, stranded</Choice>
<Choice>Aluminium_Round, 4 segment</Choice>
<Choice>Aluminium_Round, 5 segment</Choice>
<Choice>Aluminium_Round, 6 segment</Choice>
<Choice>Aluminium, Segmental with peripheral strands</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Conductor shape (self inductance)</Name>
<Choice>Circular</Choice>
<Choice>Circular compacted</Choice>
<Choice>Shaped</Choice>
<Val>0</Val>
</EW>
<DBL>
<Name>External diameter of cable, De (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>DC resistance</Name>
<NumElts>3</NumElts>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Standards</Name>
<NumElts>3</NumElts>
<EW>
<Name>DC resistance calculation based on:</Name>
<Choice>Insulation max. operating temperature, Theta (deg.C)</Choice>
<Choice>Conductor operating temperature (deg.C)</Choice>
<Val>0</Val>
</EW>
<DBL>
<Name>Conductor operating temperature (deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
<EW>
<Name>Standards</Name>
<Choice>IEC 60228:2004</Choice>
<Choice>AS/NZS 1125:2001</Choice>
<Val>1</Val>
</EW>
</Cluster>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>1.21000000000000E-2</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>3.93000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Conductor No. of Wires</Name>
<NumElts>2</NumElts>
<EL>
<Name>Mode</Name>
<Choice>Based on conductor size (mm2) and IEC 60228</Choice>
<Choice>Specify no. of conductors</Choice>
<Val>0</Val>
</EL>
<I32>
<Name>No. of conductor wires</Name>
<Val>0</Val>
</I32>
</Cluster>
<Cluster>
<Name>Conductor diameter</Name>
<NumElts>2</NumElts>
<EL>
<Name>Mode</Name>
<Choice>Based on conductor size (mm2)</Choice>
<Choice>Specify conductor diameter</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Nominal conductor diameter, dc (m)</Name>
<Val>1.50000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Conductor shield</Name>
<NumElts>2</NumElts>
<Boolean>
<Name>Conductor shield?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>3.25000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Insulation</Name>
<NumElts>5</NumElts>
<EW>
<Name>Type of cable insulation</Name>
<Choice>Impregnated paper_Solid type, fully- pre- or mass-impregnated non-draining</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 36kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 87kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 160kV</Choice>
<Choice>Impregnated paper_Oil-filled, self-contained_up to Uo = 220kV</Choice>
<Choice>Impregnated paper_Oil-pressure pipe-type</Choice>
<Choice>Impregnated paper_External gas pressure</Choice>
<Choice>Impregnated paper_Internal gas pressure</Choice>
<Choice>Butyl rubber</Choice>
<Choice>EPR_up to and including 18/30 (36) kV</Choice>
<Choice>EPR_greater than 18/30 (36) kV</Choice>
<Choice>PVC</Choice>
<Choice>PE (HD and LD)</Choice>
<Choice>XLPE_Unfilled_up to and including 18/30 (36) kV</Choice>
<Choice>XLPE_Unfilled_greater than 18/30 (36) kV</Choice>
<Choice>XLPE_Filled_greater than 18/30 (36) kV</Choice>
<Choice>PPL_equal to or greater than 63/110 kV</Choice>
<Val>11</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Epsilon</Name>
<Val>8.00000000000000</Val>
</DBL>
<DBL>
<Name>Tandelta</Name>
<Val>0.10000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>8.00000000000000E-4</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>3.10000000000000E-3</Val>
</DBL>
</Cluster>
<I32>
<Name>Insulation. Max. operating temp., Theta (deg.C)</Name>
<Val>90</Val>
</I32>
</Cluster>
<Cluster>
<Name>Insulation screen</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Insulation screen?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Single core</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Semi-conductor screen</Choice>
<Choice>Copper screen tape</Choice>
<Choice>Aluminium screen tape</Choice>
<Val>0</Val>
</EW>
</Cluster>
<Cluster>
<Name>Multi-core</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Belted (unscreened)-Multicore</Choice>
<Choice>Semi-conductor screen</Choice>
<Choice>Copper screen tape</Choice>
<Choice>Aluminium screen tape</Choice>
<Val>1</Val>
</EW>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Sheath</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Cable sheath?</Name>
<Val>0</Val>
</Boolean>
<EL>
<Name>Multicore</Name>
<Choice>Common</Choice>
<Choice>Sheath per phase</Choice>
<Val>0</Val>
</EL>
<Cluster>
<Name>Type of sheath</Name>
<NumElts>3</NumElts>
<EW>
<Name>Type of sheath</Name>
<Choice>Aluminium</Choice>
<Choice>Lead</Choice>
<Choice>Lead with reinforcing tape</Choice>
<Choice>Copper</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Construction and dimensions</Name>
<NumElts>2</NumElts>
<EW>
<Name>Construction</Name>
<Choice>Non-corrugated</Choice>
<Choice>Corrugated</Choice>
<Choice>Longitudinally corrugated</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Dimensions</Name>
<NumElts>3</NumElts>
<Cluster>
<Name>Non-corrugated</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Corrugated</Name>
<NumElts>3</NumElts>
<DBL>
<Name>Thickness of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Inner diameter of corrugated sheath (m)</Name>
<Val>3.10000000000000E-3</Val>
</DBL>
<DBL>
<Name>Outer diameter of corrugated sheath (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Longitudinally corrugated</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Corrugation height</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Inner radius of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Outer radius of corrugated sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>Tape</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Overlap (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Width (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Accumulation factor</Name>
<Val>0.00000000000000</Val>
</DBL>
<Cluster>
<Name>Calculated properties</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Diameter over sheath (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>DC resistance at 25 deg.C. (Ohm/m)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Sheath reinforcing tape</Name>
<NumElts>3</NumElts>
<Boolean>
<Name>Sheath reinforcing tape?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Material</Name>
<NumElts>3</NumElts>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<EW>
<Name>Material</Name>
<Choice>Copper</Choice>
<Choice>Brass/bronze</Choice>
<Choice>Zinc</Choice>
<Choice>Stainless steel</Choice>
<Choice>Steel</Choice>
<Val>0</Val>
</EW>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.00000000000000</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Width (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of tapes</Name>
<Val>1</Val>
</I32>
</Cluster>
</Cluster>
<Cluster>
<Name>Concentric neutral</Name>
<NumElts>6</NumElts>
<Boolean>
<Name>Concentric neutral?</Name>
<Val>0</Val>
</Boolean>
<EL>
<Name>Multicore</Name>
<Choice>Common</Choice>
<Choice>Around each core</Choice>
<Val>0</Val>
</EL>
<EW>
<Name>Material</Name>
<Choice>Custom</Choice>
<Choice>Copper</Choice>
<Choice>Brass/bronze</Choice>
<Choice>Zinc</Choice>
<Choice>Stainless steel</Choice>
<Val>1</Val>
</EW>
<EW>
<Name>Construction</Name>
<Choice>Round wires</Choice>
<Choice>Flat straps</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Electrical and magnetic material properties</Name>
<NumElts>1</NumElts>
<Cluster>
<Name>Electrical resistivity and temp. coefficient</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>1.72410000000000E-8</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>3.93000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Dimensions_Round Wire</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of wires</Name>
<Val>1</Val>
</I32>
</Cluster>
<Cluster>
<Name>Dimensions_Flat straps</Name>
<NumElts>5</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Strap length (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of straps</Name>
<Val>1</Val>
</I32>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Bedding</Name>
<NumElts>5</NumElts>
<Boolean>
<Name>Armour bedding?</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Material</Name>
<Choice>Compounded jute and fibrous materials</Choice>
<Choice>Rubber sandwich protection</Choice>
<Choice>Polychloroprene</Choice>
<Choice>PVC up to and including 35 kV</Choice>
<Choice>PVC greater than 35 kV</Choice>
<Choice>PVC/bitumen on corrugated aluminium sheaths</Choice>
<Choice>PE</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>1</NumElts>
<DBL>
<Name>Thermal resistivity, rhoT (Cm/W)</Name>
<Val>6.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>0.99845000000000</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>2.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Armour/reinforcing tape</Name>
<NumElts>4</NumElts>
<Boolean>
<Name>Armour/reinforcing tape?</Name>
<Val>0</Val>
</Boolean>
<Cluster>
<Name>Material</Name>
<NumElts>1</NumElts>
<EW>
<Name>Material</Name>
<Choice>Custom_non-magnetic tape</Choice>
<Choice>Steel tape reinforcement</Choice>
<Choice>Custom_non-magnetic wires</Choice>
<Choice>Custom_magnetic wires</Choice>
<Choice>Steel wires_touching</Choice>
<Choice>Steel wires_not touching</Choice>
<Choice>Copper armour wires</Choice>
<Choice>Stainless steel wires</Choice>
<Choice>TECK armour</Choice>
<Val>4</Val>
</EW>
</Cluster>
<Cluster>
<Name>Electrical and magnetic material properties</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Electrical resistivity and temp. coefficient</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Resistivity (Rho) ohm.m at 20 deg.C.</Name>
<Val>1.38000000000000E-7</Val>
</DBL>
<DBL>
<Name>Electrical temp. coeff. of metal, a20 (per K at 20 deg.C)</Name>
<Val>4.50000000000000E-3</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Magnetic properties (if applicable)</Name>
<NumElts>4</NumElts>
<EL>
<Name>Magnetic properties</Name>
<Choice>Standard</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EL>
<DBL>
<Name>Longitudinal relative permeability, Mue-e</Name>
<Val>10.00000000000000</Val>
</DBL>
<DBL>
<Name>Traverse relative permeability, Mue-t</Name>
<Val>45.00000000000000</Val>
</DBL>
<DBL>
<Name>Gamma angle (degrees)</Name>
<Val>45.00000000000000</Val>
</DBL>
</Cluster>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<Cluster>
<Name>Dimensions_Wire</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Length of lay (m)</Name>
<Val>1.00000000000000</Val>
</DBL>
<I32>
<Name>No. of wires</Name>
<Val>1</Val>
</I32>
</Cluster>
<Cluster>
<Name>Dimensions_Tape</Name>
<NumElts>4</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
<DBL>
<Name>Cross-sectional area of tape armour (m2)</Name>
<Val>1.00000000000000</Val>
</DBL>
<EW>
<Name>Lay of tapes (reinforcement resistance)</Name>
<Choice>Very long lay (longitudinal tapes)</Choice>
<Choice>Wound at approximately 54 degrees</Choice>
<Choice>Wound in very short lay (circumferential tapes)</Choice>
<Choice>Layers of tapes in contact with each other having a very short lay</Choice>
<Val>0</Val>
</EW>
</Cluster>
</Cluster>
</Cluster>
<Cluster>
<Name>Jacket/Serving</Name>
<NumElts>5</NumElts>
<Boolean>
<Name>Jacket/Serving?</Name>
<Val>0</Val>
</Boolean>
<EW>
<Name>Material</Name>
<Choice>Compounded jute and fibrous materials</Choice>
<Choice>Rubber sandwich</Choice>
<Choice>Polychropropene</Choice>
<Choice>PVC up to and including 35 kV</Choice>
<Choice>PVC above 35 kV</Choice>
<Choice>Butyl rubber</Choice>
<Choice>Coal tar wrapping</Choice>
<Val>0</Val>
</EW>
<EW>
<Name>Calculation mode:</Name>
<Choice>Standards</Choice>
<Choice>Custom</Choice>
<Val>0</Val>
</EW>
<Cluster>
<Name>Custom</Name>
<NumElts>1</NumElts>
<DBL>
<Name>Thermal resistivity, rhoT (Cm/W)</Name>
<Val>0.00000000000000</Val>
</DBL>
</Cluster>
<Cluster>
<Name>Dimensions</Name>
<NumElts>2</NumElts>
<DBL>
<Name>Thickness (m)</Name>
<Val>2.45000000000000E-3</Val>
</DBL>
<DBL>
<Name>Diameter (m)</Name>
<Val>8.00000000000000E-3</Val>
</DBL>
</Cluster>
</Cluster>
</Cluster>
</LVData>

Some files were not shown because too many files have changed in this diff Show More