2025-12-01
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import platform
|
||||
from ctypes import *
|
||||
from os import path
|
||||
from .data import Parameters, QRParameters, create_string, create_default_QRParameters
|
||||
|
||||
ilp_methods = {
|
||||
"LEASTSQUARES": 1,
|
||||
"ABS": 2,
|
||||
}
|
||||
|
||||
flow_config_files = {
|
||||
"SIMPLE": "config/main_config/flow_virtual_simple.json",
|
||||
"HALF": "config/main_config/flow_virtual_half.json",
|
||||
}
|
||||
|
||||
satsuma_config_files = {
|
||||
"DEFAULT": "config/satsuma/default.json",
|
||||
"MST": "config/satsuma/approx-mst.json",
|
||||
"ROUND2EVEN": "config/satsuma/approx-round2even.json",
|
||||
"SYMMDC": "config/satsuma/approx-symmdc.json",
|
||||
"EDGETHRU": "config/satsuma/edgethru.json",
|
||||
"LEMON": "config/satsuma/lemon.json",
|
||||
"NODETHRU": "config/satsuma/nodethru.json",
|
||||
}
|
||||
|
||||
class QWException(Exception):
|
||||
pass
|
||||
|
||||
class Quadwild():
|
||||
def __init__(self, mesh_path: str) -> None:
|
||||
if mesh_path is None or len(mesh_path) == 0:
|
||||
raise QWException("mesh_path is empty")
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
quadwild_lib_filename = 'lib_quadwild.dll'
|
||||
quadpatches_lib_filename = 'lib_quadpatches.dll'
|
||||
elif system == "Darwin":
|
||||
quadwild_lib_filename = 'liblib_quadwild.dylib'
|
||||
quadpatches_lib_filename = 'liblib_quadpatches.dylib'
|
||||
else:
|
||||
quadwild_lib_filename = 'liblib_quadwild.so'
|
||||
quadpatches_lib_filename = 'liblib_quadpatches.so'
|
||||
|
||||
quadwild_lib_path = path.join(path.dirname(path.abspath(__file__)), quadwild_lib_filename)
|
||||
quadpatches_lib_path = path.join(path.dirname(path.abspath(__file__)), quadpatches_lib_filename)
|
||||
|
||||
self.quadwild = cdll.LoadLibrary(quadwild_lib_path)
|
||||
self.quadpatches = cdll.LoadLibrary(quadpatches_lib_path)
|
||||
|
||||
self.quadwild.remeshAndField2.argtypes = [POINTER(Parameters), c_char_p, c_char_p, c_char_p]
|
||||
self.quadwild.remeshAndField2.restype = None
|
||||
|
||||
self.quadwild.trace2.argtypes = [c_char_p]
|
||||
self.quadwild.trace2.restype = c_bool
|
||||
|
||||
self.quadpatches.quadPatches.argtypes = [c_char_p, POINTER(QRParameters), c_float, c_int, c_bool]
|
||||
self.quadpatches.quadPatches.restype = c_int
|
||||
|
||||
self.mesh_path = mesh_path
|
||||
self.mesh_path_without_ext, _ = path.splitext(mesh_path)
|
||||
self.sharp_path = f'{self.mesh_path_without_ext}_rem.sharp'
|
||||
self.field_path = f'{self.mesh_path_without_ext}_rem.rosy'
|
||||
self.remeshed_path = f'{self.mesh_path_without_ext}_rem.obj'
|
||||
self.traced_path = f'{self.mesh_path_without_ext}_rem_p0.obj'
|
||||
self.output_path = f'{self.mesh_path_without_ext}_rem_p0_0_quadrangulation.obj'
|
||||
self.output_smoothed_path = f'{self.mesh_path_without_ext}_rem_p0_0_quadrangulation_smooth.obj'
|
||||
|
||||
|
||||
def remeshAndField(self, remesh: bool, enableSharp: bool, sharpAngle: float) -> None:
|
||||
params = Parameters(
|
||||
remesh=remesh,
|
||||
sharpAngle=sharpAngle if enableSharp else -1,
|
||||
hasFeature=enableSharp,
|
||||
hasField=False,
|
||||
alpha=0.01, # Unused
|
||||
scaleFact=1, # Unused
|
||||
)
|
||||
mesh_filename_c = create_string(self.mesh_path)
|
||||
sharp_filename_c = create_string(self.sharp_path)
|
||||
field_filename_c = create_string(self.field_path)
|
||||
try:
|
||||
self.quadwild.remeshAndField2(byref(params), mesh_filename_c, sharp_filename_c, field_filename_c)
|
||||
except Exception as e:
|
||||
raise QWException("remeshAndField failed") from e
|
||||
|
||||
def trace(self) -> bool:
|
||||
remeshed_path_without_ext, _ = path.splitext(self.remeshed_path)
|
||||
filename_prefix_c = create_string(remeshed_path_without_ext)
|
||||
try:
|
||||
return self.quadwild.trace2(filename_prefix_c)
|
||||
except Exception as e:
|
||||
raise QWException("trace failed") from e
|
||||
|
||||
def quadrangulate(
|
||||
self,
|
||||
enableSmoothing: bool,
|
||||
scaleFact: float,
|
||||
fixedChartClusters: int,
|
||||
alpha: float,
|
||||
ilpMethod: str,
|
||||
timeLimit: int,
|
||||
gapLimit: float,
|
||||
minimumGap: float,
|
||||
isometry: bool,
|
||||
regularityQuadrilaterals: bool,
|
||||
regularityNonQuadrilaterals: bool,
|
||||
regularityNonQuadrilateralsWeight: float,
|
||||
alignSingularities: bool,
|
||||
alignSingularitiesWeight: float,
|
||||
repeatLosingConstraintsIterations: bool,
|
||||
repeatLosingConstraintsQuads: bool,
|
||||
repeatLosingConstraintsNonQuads: bool,
|
||||
repeatLosingConstraintsAlign: bool,
|
||||
hardParityConstraint: bool,
|
||||
flowConfig: str,
|
||||
satsumaConfig: str,
|
||||
callbackTimeLimit: list[float],
|
||||
callbackGapLimit: list[float],
|
||||
) -> int:
|
||||
params = create_default_QRParameters()
|
||||
|
||||
params.alpha = alpha
|
||||
params.ilpMethod = ilp_methods[ilpMethod]
|
||||
params.timeLimit = timeLimit
|
||||
params.gapLimit = gapLimit
|
||||
params.minimumGap = minimumGap
|
||||
params.isometry = isometry
|
||||
params.regularityQuadrilaterals = regularityQuadrilaterals
|
||||
params.regularityNonQuadrilaterals = regularityNonQuadrilaterals
|
||||
params.regularityNonQuadrilateralsWeight = regularityNonQuadrilateralsWeight
|
||||
params.alignSingularities = alignSingularities
|
||||
params.alignSingularitiesWeight = alignSingularitiesWeight
|
||||
params.repeatLosingConstraintsIterations = repeatLosingConstraintsIterations
|
||||
params.repeatLosingConstraintsQuads = repeatLosingConstraintsQuads
|
||||
params.repeatLosingConstraintsNonQuads = repeatLosingConstraintsNonQuads
|
||||
params.repeatLosingConstraintsAlign = repeatLosingConstraintsAlign
|
||||
params.hardParityConstraint = hardParityConstraint
|
||||
|
||||
params.flow_config_filename = path.join(path.dirname(path.abspath(__file__)), flow_config_files[flowConfig]).encode()
|
||||
params.satsuma_config_filename = path.join(path.dirname(path.abspath(__file__)), satsuma_config_files[satsumaConfig]).encode()
|
||||
|
||||
params.callbackTimeLimit = (c_float * len(callbackTimeLimit))(*callbackTimeLimit)
|
||||
params.callbackGapLimit = (c_float * len(callbackGapLimit))(*callbackGapLimit)
|
||||
|
||||
mesh_path_c = self.traced_path.encode()
|
||||
try:
|
||||
return self.quadpatches.quadPatches(mesh_path_c, byref(params), scaleFact, fixedChartClusters, enableSmoothing)
|
||||
except Exception as e:
|
||||
raise QWException("quadPatches failed") from e
|
||||
Reference in New Issue
Block a user