Placement: isInside optimizado con shapely + caché LRU + prefiltro BoundBox. Caché se limpia al cambiar área
This commit is contained in:
+46
-7
@@ -1186,6 +1186,8 @@ class _PVPlantPlacementTaskPanel:
|
||||
self.Dir = FreeCAD.Vector(0, -1, 0)
|
||||
self._terrain_interpolator = None
|
||||
self._frame_footprints_cache = {}
|
||||
self._isinside_cache = {} # LRU: (frame_name, x, y) -> bool
|
||||
self._area_polygon = None # Caché shapely del área
|
||||
|
||||
# UI setup
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantPlacement.ui"))
|
||||
@@ -1285,8 +1287,10 @@ class _PVPlantPlacementTaskPanel:
|
||||
if prohibited_faces:
|
||||
self.Area = self.Area.cut(prohibited_faces)
|
||||
|
||||
# Clear terrain interpolator cache when area changes
|
||||
# Clear caches when area changes
|
||||
self._terrain_interpolator = None
|
||||
self._area_polygon = None
|
||||
self._isinside_cache.clear()
|
||||
|
||||
def _setup_terrain_interpolator(self):
|
||||
"""Cached terrain interpolator"""
|
||||
@@ -1425,18 +1429,53 @@ class _PVPlantPlacementTaskPanel:
|
||||
|
||||
return df
|
||||
|
||||
def _get_area_polygon(self):
|
||||
"""Convierte self.Area a shapely Polygon para comprobaciones rápidas"""
|
||||
if self._area_polygon is None and self.Area:
|
||||
from shapely.geometry import Polygon
|
||||
verts = self.Area.Vertexes
|
||||
if len(verts) >= 3:
|
||||
self._area_polygon = Polygon([(v.x, v.y) for v in verts])
|
||||
return self._area_polygon
|
||||
|
||||
def isInside(self, frame, point):
|
||||
"""Optimized inside check with early termination"""
|
||||
if not self.Area.isInside(point, 1e-6, True): # Reduced tolerance for speed
|
||||
"""
|
||||
Comprueba si un frame cabe en el área en un punto dado.
|
||||
Usa shapely para la comprobación 2D (mucho más rápido que Part.cut).
|
||||
"""
|
||||
# Caché LRU: mismo frame + misma posición
|
||||
key = (frame.Name, round(point.x, 0), round(point.y, 0))
|
||||
if key in self._isinside_cache:
|
||||
return self._isinside_cache[key]
|
||||
|
||||
# Prefiltro rápido por BoundBox
|
||||
fw, fl = frame.Width.Value / 2, frame.Length.Value / 2
|
||||
if (point.x - fw < self.Area.BoundBox.XMin or
|
||||
point.x + fw > self.Area.BoundBox.XMax or
|
||||
point.y - fl < self.Area.BoundBox.YMin or
|
||||
point.y + fl > self.Area.BoundBox.YMax):
|
||||
self._isinside_cache[key] = False
|
||||
return False
|
||||
|
||||
frame_footprint = self._get_frame_footprint(frame)
|
||||
frame_footprint.Placement.Base = point
|
||||
# Comprobación precisa con shapely
|
||||
ap = self._get_area_polygon()
|
||||
if ap is not None:
|
||||
from shapely.geometry import box
|
||||
fp = box(point.x - fw, point.y - fl, point.x + fw, point.y + fl)
|
||||
result = ap.contains(fp)
|
||||
self._isinside_cache[key] = result
|
||||
return result
|
||||
|
||||
# Fallback OCC (si shapely falla)
|
||||
try:
|
||||
frame_footprint = self._get_frame_footprint(frame)
|
||||
frame_footprint.Placement.Base = point
|
||||
cut = frame_footprint.cut([self.Area])
|
||||
return len(cut.Vertexes) == 0
|
||||
except:
|
||||
result = len(cut.Vertexes) == 0
|
||||
self._isinside_cache[key] = result
|
||||
return result
|
||||
except Part.OCCError:
|
||||
self._isinside_cache[key] = False
|
||||
return False
|
||||
|
||||
def getAligments(self):
|
||||
|
||||
Reference in New Issue
Block a user