142 lines
4.5 KiB
Python
142 lines
4.5 KiB
Python
from abc import ABC, abstractmethod
|
|
import numpy as np
|
|
|
|
from pygarment.garmentcode.connector import Stitches
|
|
|
|
|
|
class BaseComponent(ABC):
|
|
"""Basic interface for garment-related components
|
|
|
|
NOTE: modifier methods return self object to allow chaining of the
|
|
operations
|
|
"""
|
|
|
|
def __init__(self, name, verbose=False) -> None:
|
|
self.name = name
|
|
self.verbose = verbose
|
|
|
|
# List or dictionary of the interfaces of this components
|
|
# available for connectivity with other components
|
|
self.interfaces = {}
|
|
|
|
# Rules for connecting subcomponents
|
|
self.stitching_rules = Stitches()
|
|
|
|
# Info
|
|
def pivot_3D(self):
|
|
"""Pivot location of a component in 3D"""
|
|
return [0, 0, 0]
|
|
|
|
def bbox(self):
|
|
"""Bounding box -- in 2D"""
|
|
return np.array([0, 0]), np.array([0, 0])
|
|
|
|
def bbox3D(self):
|
|
"""Bounding box in 3D space"""
|
|
return np.array([0, 0, 0]), np.array([0, 0, 0])
|
|
|
|
def is_self_intersecting(self):
|
|
"""Check whether the component have self-intersections"""
|
|
return False
|
|
|
|
# Operations
|
|
@abstractmethod
|
|
def translate_by(self, delta_translation):
|
|
return self
|
|
|
|
@abstractmethod
|
|
def translate_to(self, new_translation):
|
|
"""Set panel translation to be exactly that vector"""
|
|
return self
|
|
|
|
@abstractmethod
|
|
def rotate_by(self, delta_rotation):
|
|
return self
|
|
|
|
@abstractmethod
|
|
def rotate_to(self, new_rot):
|
|
return self
|
|
|
|
@abstractmethod
|
|
def assembly(self, *args, **kwargs):
|
|
pass
|
|
|
|
# ----- Placement routines: these are the same for panels and components
|
|
def place_below(self, comp, gap=2):
|
|
"""Place below the provided component"""
|
|
other_bbox = comp.bbox3D()
|
|
curr_bbox = self.bbox3D()
|
|
|
|
self.translate_by([0, other_bbox[0][1] - curr_bbox[1][1] - gap, 0])
|
|
return self
|
|
|
|
def place_by_interface(
|
|
self,
|
|
self_interface,
|
|
out_interface,
|
|
gap=2,
|
|
alignment='center',
|
|
gap_dir=None
|
|
):
|
|
"""Adjust the placement of component according to the connectivity
|
|
instruction
|
|
|
|
Alignment options:
|
|
'center' center of the interface to center of the interface
|
|
'top' - top on Y axis
|
|
'bottom' - bottom on Y axis
|
|
'left' - left on X axis
|
|
'right' - right on X axis
|
|
"""
|
|
|
|
# Align translation
|
|
self_bbox = self_interface.bbox_3d()
|
|
out_bbox = out_interface.bbox_3d()
|
|
|
|
# Determine alignment point depending on requested alignment type
|
|
point_out = (out_bbox[1] + out_bbox[0]) / 2
|
|
point_self = (self_bbox[1] + self_bbox[0]) / 2
|
|
if alignment == 'center':
|
|
pass # No modification needed
|
|
elif alignment == 'top':
|
|
point_out[1] = out_bbox[1][1] # Use max in Y
|
|
point_self[1] = self_bbox[1][1]
|
|
elif alignment == 'bottom':
|
|
point_out[1] = out_bbox[0][1] # Use min in Y
|
|
point_self[1] = self_bbox[0][1]
|
|
elif alignment == 'right':
|
|
point_out[0] = out_bbox[0][0] # Use min in X
|
|
point_self[0] = self_bbox[0][0]
|
|
elif alignment == 'left':
|
|
point_out[0] = out_bbox[1][0] # Use max in X
|
|
point_self[0] = self_bbox[1][0]
|
|
else:
|
|
raise ValueError(
|
|
f'{self.__class__.__name__}::{self.name}::ERROR::'
|
|
f'Uknown alignment type ({alignment}) requested in place_by_interface().'
|
|
f' Available types: center, top, bottom, left, right')
|
|
|
|
# Add a gap outside the current
|
|
if gap_dir is None:
|
|
full_bbox = self.bbox3D()
|
|
center = (full_bbox[0] + full_bbox[1]) / 2
|
|
mid_self = (self_bbox[1] + self_bbox[0]) / 2
|
|
gap_dir = mid_self - center
|
|
|
|
gap_dir = gap * gap_dir / np.linalg.norm(gap_dir)
|
|
diff = point_out - (point_self + gap_dir)
|
|
|
|
self.translate_by(diff)
|
|
|
|
# NOTE: Norm evaluation of vertex set will fail
|
|
# for the alignment of 2D panels, where they are likely
|
|
# to be in one line or in a panel plane instead of
|
|
# the interface place -- so I'm not using norms for gap estimation
|
|
|
|
# TODO Estimate rotation
|
|
# TODO not just placement by the midpoint of the interfaces?
|
|
# It created a little overlap when both interfaces are angled a little differently
|
|
return self
|
|
|
|
|