2025-01-28 00:04:13 +01:00
|
|
|
# /**********************************************************************
|
|
|
|
|
# * *
|
|
|
|
|
# * 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")
|
|
|
|
|
|
2025-04-14 10:05:32 +06:00
|
|
|
if not ("mesh" in pl):
|
2025-01-28 00:04:13 +01:00
|
|
|
obj.addProperty("Mesh::PropertyMeshKernel",
|
2025-04-14 10:05:32 +06:00
|
|
|
"mesh",
|
2025-01-28 00:04:13 +01:00
|
|
|
"Surface",
|
|
|
|
|
"Mesh")
|
2025-04-14 10:05:32 +06:00
|
|
|
obj.setEditorMode("mesh", 1)
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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":
|
2025-04-14 10:05:32 +06:00
|
|
|
obj.mesh = obj.InitialMesh.copy()
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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
|
2025-04-14 10:05:32 +06:00
|
|
|
offset = self.site.Origin
|
|
|
|
|
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
|
|
|
|
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
|
|
|
|
datavals = datavals * 1000 # Ajuste de altura
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
# 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]
|
|
|
|
|
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:
|
2025-04-14 10:05:32 +06:00
|
|
|
triangulated = Triangulation.Triangulate(pts)
|
|
|
|
|
mesh.addMesh(triangulated)
|
2025-01-28 00:04:13 +01:00
|
|
|
except TypeError:
|
2025-04-14 10:05:32 +06:00
|
|
|
print(f"Error al procesar {len(pts)} puntos: {str(e)}")
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
if obj.DEM:
|
|
|
|
|
obj.DEM = None
|
2025-04-14 10:05:32 +06:00
|
|
|
obj.mesh = mesh
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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.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):
|
2025-04-14 10:05:32 +06:00
|
|
|
'''vobj.addProperty("App::PropertyIntegerConstraint",
|
2025-01-28 00:04:13 +01:00
|
|
|
"Transparency",
|
|
|
|
|
"Surface Style",
|
2025-04-14 10:05:32 +06:00
|
|
|
"Set triangle face transparency").Transparency = (50, 0, 100, 1)'''
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
if not ("ShapeColor" in pl):
|
|
|
|
|
vobj.addProperty("App::PropertyColor",
|
|
|
|
|
"ShapeColor",
|
|
|
|
|
"Surface Style",
|
2025-04-14 10:05:32 +06:00
|
|
|
"Set triangle face color").ShapeColor = (0.0, 0.667, 0.49, vobj.Transparency / 100)
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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
|
2025-04-14 10:05:32 +06:00
|
|
|
self.Object = vobj.Object
|
|
|
|
|
# Inicializar colores correctamente
|
2025-01-28 00:04:13 +01:00
|
|
|
vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor
|
|
|
|
|
|
|
|
|
|
def onDocumentRestored(self, vobj):
|
|
|
|
|
self.setProperties(vobj)
|
|
|
|
|
|
|
|
|
|
def onChanged(self, vobj, prop):
|
2025-04-14 10:05:32 +06:00
|
|
|
""" Update Object visuals when a view property changed. """
|
|
|
|
|
|
2025-01-28 00:04:13 +01:00
|
|
|
if prop == "ShapeColor" or prop == "Transparency":
|
|
|
|
|
if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"):
|
|
|
|
|
color = vobj.getPropertyByName("ShapeColor")
|
|
|
|
|
transparency = vobj.getPropertyByName("Transparency")
|
2025-04-14 10:05:32 +06:00
|
|
|
color = (color[0], color[1], color[2], 50 / 100)
|
2025-01-28 00:04:13 +01:00
|
|
|
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)
|
2025-04-14 10:05:32 +06:00
|
|
|
#highlight.addChild(boundaries)
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
# 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)
|
2025-04-14 10:05:32 +06:00
|
|
|
#surface_root.addChild(major_contours)
|
|
|
|
|
#surface_root.addChild(minor_contours)
|
2025-01-28 00:04:13 +01:00
|
|
|
vobj.addDisplayMode(surface_root, "Surface")
|
|
|
|
|
|
|
|
|
|
# Boundary root.
|
2025-04-14 10:05:32 +06:00
|
|
|
#boundary_root = coin.SoSeparator()
|
|
|
|
|
#boundary_root.addChild(boundaries)
|
|
|
|
|
#vobj.addDisplayMode(boundary_root, "Boundary")
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
# 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)
|
2025-04-14 10:05:32 +06:00
|
|
|
#wireframe_root.addChild(major_contours)
|
|
|
|
|
#wireframe_root.addChild(minor_contours)
|
2025-01-28 00:04:13 +01:00
|
|
|
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":
|
2025-04-14 10:05:32 +06:00
|
|
|
if obj.mesh:
|
|
|
|
|
print("Mostrar mesh")
|
2025-01-28 00:04:13 +01:00
|
|
|
|
2025-04-14 10:05:32 +06:00
|
|
|
mesh = obj.mesh
|
|
|
|
|
vertices = [tuple(v) for v in mesh.Topology[0]]
|
|
|
|
|
faces = []
|
|
|
|
|
for face in mesh.Topology[1]:
|
|
|
|
|
faces.extend(face)
|
|
|
|
|
faces.append(-1)
|
2025-01-28 00:04:13 +01:00
|
|
|
|
2025-04-14 10:05:32 +06:00
|
|
|
# Asignar a los nodos de visualización
|
|
|
|
|
self.geo_coords.point.values = vertices # <-- ¡Clave!
|
|
|
|
|
self.triangles.coordIndex.values = faces # <-- ¡Clave!
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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):
|
2025-04-14 10:05:32 +06:00
|
|
|
if hasattr(self, "Object") and self.Object:
|
|
|
|
|
return [self.Object.CuttingBoundary, ]
|
|
|
|
|
return []
|
2025-01-28 00:04:13 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2025-03-28 19:40:11 +06:00
|
|
|
'''class _CommandTerrain:
|
2025-01-28 00:04:13 +01:00
|
|
|
"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:
|
2025-03-28 19:40:11 +06:00
|
|
|
FreeCADGui.addCommand('Terrain', _CommandTerrain())'''
|