Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
6145a6bc6c
|
@ -187,6 +187,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
${X11_X11_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${X11_X11_LIB}
|
||||
${X11_Xrender_LIB}
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_DisplayManagerX11.cpp
|
||||
intern/GHOST_SystemX11.cpp
|
||||
|
@ -238,6 +243,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
list(APPEND INC_SYS
|
||||
${X11_xf86vmode_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xf86vmode_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_XFIXES)
|
||||
|
@ -245,6 +253,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
list(APPEND INC_SYS
|
||||
${X11_Xfixes_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xfixes_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_ALPHA)
|
||||
|
@ -256,6 +267,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
list(APPEND INC_SYS
|
||||
${X11_Xinput_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xinput_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_GHOST_X11)
|
||||
|
|
|
@ -1614,7 +1614,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|||
break;
|
||||
}
|
||||
case WT_PACKET:
|
||||
window->updatePendingWintabEvents();
|
||||
window->updateWintabEventsSyncTime();
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Pointer events, processed
|
||||
|
|
|
@ -1292,7 +1292,7 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
|
|||
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
|
||||
|
||||
updatePendingWintabEvents();
|
||||
updateWintabEvents();
|
||||
|
||||
auto &pendingEvents = m_wintab.pendingEvents;
|
||||
size_t pendingEventSize = pendingEvents.size();
|
||||
|
@ -1388,6 +1388,44 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
|
|||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::updateWintabEvents()
|
||||
{
|
||||
readWintabEvents();
|
||||
// When a Wintab device is used to leave window focus, some of it's packets are periodically not
|
||||
// queued in time to be flushed. Reading packets needs to occur before expiring packets to clear
|
||||
// these from the queue.
|
||||
expireWintabEvents();
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::updateWintabEventsSyncTime()
|
||||
{
|
||||
readWintabEvents();
|
||||
|
||||
if (!m_wintab.pendingEvents.empty()) {
|
||||
auto lastEvent = m_wintab.pendingEvents.back();
|
||||
m_wintab.sysTimeOffset = ::GetTickCount() - lastEvent.pkTime;
|
||||
}
|
||||
|
||||
expireWintabEvents();
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::readWintabEvents()
|
||||
{
|
||||
if (!(m_wintab.packetsGet && m_wintab.context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &pendingEvents = m_wintab.pendingEvents;
|
||||
|
||||
/* Get new packets. */
|
||||
const int numPackets = m_wintab.packetsGet(
|
||||
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
|
||||
for (int i = 0; i < numPackets; i++) {
|
||||
pendingEvents.push(m_wintab.pkts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wintab (per documentation but may vary with implementation) does not update when its event
|
||||
* buffer is full. This is an issue because we need some synchronization point between Wintab
|
||||
* events and Win32 events, so we can't drain and process the queue immediately. We need to
|
||||
|
@ -1396,17 +1434,12 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
|
|||
* mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse
|
||||
* events and no documented time stamp shared between the two, so we synchronize on mouse button
|
||||
* events. */
|
||||
void GHOST_WindowWin32::updatePendingWintabEvents()
|
||||
void GHOST_WindowWin32::expireWintabEvents()
|
||||
{
|
||||
if (!(m_wintab.packetsGet && m_wintab.context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &pendingEvents = m_wintab.pendingEvents;
|
||||
|
||||
/* Clear outdated events from queue. */
|
||||
DWORD currTime = ::GetTickCount();
|
||||
DWORD millisTimeout = 500;
|
||||
DWORD currTime = ::GetTickCount() - m_wintab.sysTimeOffset;
|
||||
DWORD millisTimeout = 300;
|
||||
while (!pendingEvents.empty()) {
|
||||
DWORD pkTime = pendingEvents.front().pkTime;
|
||||
|
||||
|
@ -1417,24 +1450,6 @@ void GHOST_WindowWin32::updatePendingWintabEvents()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get new packets. */
|
||||
const int numPackets = m_wintab.packetsGet(
|
||||
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
|
||||
int i = 0;
|
||||
/* Don't queue outdated packets, such events can include packets that occurred before the current
|
||||
* window lost and regained focus. */
|
||||
for (; i < numPackets; i++) {
|
||||
DWORD pkTime = m_wintab.pkts[i].pkTime;
|
||||
|
||||
if (currTime < pkTime + millisTimeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; i < numPackets; i++) {
|
||||
pendingEvents.push(m_wintab.pkts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
|
||||
|
|
|
@ -486,9 +486,14 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
|||
GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
|
||||
|
||||
/**
|
||||
* Updates stored pending Wintab events.
|
||||
* Updates pending Wintab events and syncs Wintab time with OS time.
|
||||
*/
|
||||
void updatePendingWintabEvents();
|
||||
void updateWintabEventsSyncTime();
|
||||
|
||||
/**
|
||||
* Updates pending Wintab events.
|
||||
*/
|
||||
void updateWintabEvents();
|
||||
|
||||
GHOST_TSuccess beginFullScreen() const
|
||||
{
|
||||
|
@ -629,6 +634,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
|||
GHOST_TUns8 numSysButtons = 0;
|
||||
/** Cursors currently in contact mapped to system buttons */
|
||||
DWORD sysButtonsPressed = 0;
|
||||
DWORD sysTimeOffset = 0;
|
||||
LONG maxPressure = 0;
|
||||
LONG maxAzimuth = 0, maxAltitude = 0;
|
||||
/** Reusable buffer to read in Wintab Packets. */
|
||||
|
@ -642,6 +648,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
|||
*/
|
||||
void initializeWintab();
|
||||
|
||||
void readWintabEvents();
|
||||
|
||||
void expireWintabEvents();
|
||||
|
||||
/**
|
||||
* Convert Wintab system mapped (mouse) buttons into Ghost button mask.
|
||||
* \param cursor: The Wintab cursor associated to the button.
|
||||
|
|
|
@ -1217,7 +1217,7 @@ def km_view3d(params):
|
|||
("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("data_path", 'tool_settings.use_snap')]}),
|
||||
op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", False)]),
|
||||
op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", True)]),
|
||||
("object.transform_axis_target", {"type": 'T', "value": 'PRESS', "shift": True}, None),
|
||||
("transform.skin_resize", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
|
||||
])
|
||||
|
|
|
@ -593,7 +593,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
|
|||
return
|
||||
|
||||
# If the active file is an ID, use its name directly so renaming is possible from right here.
|
||||
layout.prop(context.id if not None else active_file, "name", text="")
|
||||
layout.prop(context.id if context.id is not None else active_file, "name", text="")
|
||||
|
||||
|
||||
class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
|
||||
|
|
|
@ -485,6 +485,7 @@ geometry_node_categories = [
|
|||
NodeItem("GeometryNodeAttributeMath"),
|
||||
NodeItem("GeometryNodeAttributeFill"),
|
||||
NodeItem("GeometryNodeAttributeMix"),
|
||||
NodeItem("GeometryNodeAttributeColorRamp"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
|
|
|
@ -267,10 +267,12 @@ template<typename T> class TypedWriteAttribute {
|
|||
using BooleanReadAttribute = TypedReadAttribute<bool>;
|
||||
using FloatReadAttribute = TypedReadAttribute<float>;
|
||||
using Float3ReadAttribute = TypedReadAttribute<float3>;
|
||||
using Int32ReadAttribute = TypedReadAttribute<int>;
|
||||
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
|
||||
using BooleanWriteAttribute = TypedWriteAttribute<bool>;
|
||||
using FloatWriteAttribute = TypedWriteAttribute<float>;
|
||||
using Float3WriteAttribute = TypedWriteAttribute<float3>;
|
||||
using Int32WriteAttribute = TypedWriteAttribute<int>;
|
||||
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -136,6 +136,11 @@ class GeometryComponent {
|
|||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const;
|
||||
|
||||
/* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
|
||||
* Returns null when the attribute does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain) const;
|
||||
|
||||
/* Get a read-only attribute for the given domain and data type.
|
||||
* Returns a constant attribute based on the default value if the attribute does not exist.
|
||||
* Never returns null. */
|
||||
|
|
|
@ -1352,6 +1352,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_JOIN_GEOMETRY 1010
|
||||
#define GEO_NODE_ATTRIBUTE_FILL 1011
|
||||
#define GEO_NODE_ATTRIBUTE_MIX 1012
|
||||
#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -660,6 +660,28 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
|||
return attribute;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
|
||||
const AttributeDomain domain) const
|
||||
{
|
||||
if (!this->attribute_domain_supported(domain)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (attribute->domain() != domain) {
|
||||
attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
|
|
|
@ -2054,7 +2054,7 @@ static void bke_view_layer_verify_aov_cb(void *userdata,
|
|||
const char *name,
|
||||
int UNUSED(channels),
|
||||
const char *UNUSED(chanid),
|
||||
int UNUSED(type))
|
||||
eNodeSocketDatatype UNUSED(type))
|
||||
{
|
||||
GHash *name_count = userdata;
|
||||
void **value_p;
|
||||
|
|
|
@ -702,6 +702,12 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
|
|||
&ob_proxy->proxy->id;
|
||||
ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
|
||||
|
||||
/* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
|
||||
* sure this is a valid state, but for now just abort the overriding process. */
|
||||
if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We manually convert the proxy object into a library override, further override handling will
|
||||
* then be handled by `BKE_lib_override_library_create()` just as for a regular override
|
||||
* creation.
|
||||
|
|
|
@ -4741,6 +4741,7 @@ static void registerGeometryNodes(void)
|
|||
register_node_type_geo_attribute_math();
|
||||
register_node_type_geo_join_geometry();
|
||||
register_node_type_geo_attribute_mix();
|
||||
register_node_type_geo_attribute_color_ramp();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes(void)
|
||||
|
|
|
@ -26,36 +26,60 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Jenkins Lookup3 Hash Functions.
|
||||
* Source: http://burtleburtle.net/bob/c/lookup3.c
|
||||
*/
|
||||
|
||||
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
|
||||
#define final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; \
|
||||
c -= rot(b, 14); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 11); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 25); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 16); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 4); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 14); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 24); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_int_3d(unsigned int kx, unsigned int ky, unsigned int kz)
|
||||
{
|
||||
unsigned int a, b, c;
|
||||
a = b = c = 0xdeadbeef + (3 << 2) + 13;
|
||||
|
||||
c += kz;
|
||||
b += ky;
|
||||
a += kx;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
|
||||
{
|
||||
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
|
||||
|
||||
unsigned int a, b, c;
|
||||
|
||||
a = b = c = 0xdeadbeef + (2 << 2) + 13;
|
||||
a += kx;
|
||||
b += ky;
|
||||
|
||||
c ^= b;
|
||||
c -= rot(b, 14);
|
||||
a ^= c;
|
||||
a -= rot(c, 11);
|
||||
b ^= a;
|
||||
b -= rot(a, 25);
|
||||
c ^= b;
|
||||
c -= rot(b, 16);
|
||||
a ^= c;
|
||||
a -= rot(c, 4);
|
||||
b ^= a;
|
||||
b -= rot(a, 14);
|
||||
c ^= b;
|
||||
c -= rot(b, 24);
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
|
||||
#undef rot
|
||||
}
|
||||
|
||||
#undef final
|
||||
#undef rot
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_string(const char *str)
|
||||
{
|
||||
unsigned int i = 0, c;
|
||||
|
|
|
@ -284,12 +284,6 @@ static void seq_convert_transform_crop_2(const Scene *scene,
|
|||
|
||||
char name_esc[(sizeof(seq->name) - 2) * 2], *path;
|
||||
BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc));
|
||||
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_x", name_esc);
|
||||
seq_convert_transform_animation_2(scene, path, scale_to_fit_factor);
|
||||
MEM_freeN(path);
|
||||
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_y", name_esc);
|
||||
seq_convert_transform_animation_2(scene, path, scale_to_fit_factor);
|
||||
MEM_freeN(path);
|
||||
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_x", name_esc);
|
||||
seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
|
||||
MEM_freeN(path);
|
||||
|
|
|
@ -2527,7 +2527,7 @@ static bool convert_proxy_to_override_poll(bContext *C)
|
|||
return obact != NULL && obact->proxy != NULL;
|
||||
}
|
||||
|
||||
static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -2541,6 +2541,15 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
|
||||
const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy);
|
||||
|
||||
if (!success) {
|
||||
BKE_reportf(
|
||||
op->reports,
|
||||
RPT_ERROR_INVALID_INPUT,
|
||||
"Could not create a library override from proxy '%s' (might use already local data?)",
|
||||
ob_proxy->id.name + 2);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Remove the instance empty from this scene, the items now have an overridden collection
|
||||
* instead. */
|
||||
if (success && is_override_instancing_object) {
|
||||
|
|
|
@ -6086,6 +6086,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
|
|||
}
|
||||
}
|
||||
|
||||
/* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with
|
||||
* zero radius, thus we have no pbvh nodes on the first brush step. */
|
||||
if (totnode ||
|
||||
((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) {
|
||||
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
|
||||
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
|
||||
ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Only act if some verts are inside the brush area. */
|
||||
if (totnode) {
|
||||
float location[3];
|
||||
|
@ -6109,12 +6120,6 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
|
|||
update_brush_local_mat(sd, ob);
|
||||
}
|
||||
|
||||
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
|
||||
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
|
||||
ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) {
|
||||
SCULPT_pose_brush_init(sd, ob, ss, brush);
|
||||
}
|
||||
|
|
|
@ -2778,13 +2778,20 @@ void FILE_OT_delete(struct wmOperatorType *ot)
|
|||
static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_UI);
|
||||
SpaceFile *sfile = CTX_wm_space_file(C);
|
||||
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
|
||||
|
||||
ARegion *region_ctx = CTX_wm_region(C);
|
||||
CTX_wm_region_set(C, region);
|
||||
UI_textbutton_activate_rna(C, region, params, "filter_search");
|
||||
|
||||
if (area) {
|
||||
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
|
||||
CTX_wm_region_set(C, region);
|
||||
if (UI_textbutton_activate_rna(C, region, params, "filter_search")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CTX_wm_region_set(C, region_ctx);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
|
@ -803,16 +803,25 @@ static int /*eContextResult*/ file_context(const bContext *C,
|
|||
|
||||
if (CTX_data_equals(member, "active_file")) {
|
||||
FileDirEntry *file = filelist_file(sfile->files, params->active_file);
|
||||
if (file == NULL) {
|
||||
return CTX_RESULT_NO_DATA;
|
||||
}
|
||||
|
||||
CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
|
||||
return CTX_RESULT_OK;
|
||||
}
|
||||
if (CTX_data_equals(member, "id")) {
|
||||
const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
|
||||
if (file == NULL) {
|
||||
return CTX_RESULT_NO_DATA;
|
||||
}
|
||||
|
||||
ID *id = filelist_file_get_id(file);
|
||||
if (id) {
|
||||
CTX_data_id_pointer_set(result, id);
|
||||
if (id == NULL) {
|
||||
return CTX_RESULT_NO_DATA;
|
||||
}
|
||||
|
||||
CTX_data_id_pointer_set(result, id);
|
||||
return CTX_RESULT_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -3215,6 +3215,13 @@ static void node_geometry_buts_attribute_point_distribute(uiLayout *layout,
|
|||
uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_buts_attribute_color_ramp(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiTemplateColorRamp(layout, ptr, "color_ramp", 0);
|
||||
}
|
||||
|
||||
static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
{
|
||||
switch (ntype->type) {
|
||||
|
@ -3245,6 +3252,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
|||
case GEO_NODE_POINT_DISTRIBUTE:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
|
||||
break;
|
||||
case GEO_NODE_ATTRIBUTE_COLOR_RAMP:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_color_ramp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -737,7 +737,7 @@ static void id_local_fn(bContext *C,
|
|||
}
|
||||
|
||||
static void object_proxy_to_override_convert_fn(bContext *C,
|
||||
ReportList *UNUSED(reports),
|
||||
ReportList *reports,
|
||||
Scene *UNUSED(scene),
|
||||
TreeElement *UNUSED(te),
|
||||
TreeStoreElem *UNUSED(tsep),
|
||||
|
@ -754,8 +754,15 @@ static void object_proxy_to_override_convert_fn(bContext *C,
|
|||
return;
|
||||
}
|
||||
|
||||
BKE_lib_override_library_proxy_convert(
|
||||
CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy);
|
||||
if (!BKE_lib_override_library_proxy_convert(
|
||||
CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) {
|
||||
BKE_reportf(
|
||||
reports,
|
||||
RPT_ERROR_INVALID_INPUT,
|
||||
"Could not create a library override from proxy '%s' (might use already local data?)",
|
||||
ob_proxy->id.name + 2);
|
||||
return;
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_WINDOW, NULL);
|
||||
|
|
|
@ -2305,7 +2305,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
|
|||
|
||||
UI_view2d_view_ortho(v2d);
|
||||
/* Get timeline bound-box, needed for the scroll-bars. */
|
||||
SEQ_timeline_boundbox(scene, ed->seqbasep, &v2d->tot);
|
||||
SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot);
|
||||
draw_seq_backdrop(v2d);
|
||||
UI_view2d_constant_grid_draw(v2d, FPS);
|
||||
|
||||
|
|
|
@ -90,11 +90,7 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op)
|
|||
Scene *scene = CTX_data_scene(C);
|
||||
const Editing *ed = BKE_sequencer_editing_get(scene, false);
|
||||
|
||||
if (ed == NULL) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
SEQ_timeline_boundbox(scene, ed->seqbasep, &box);
|
||||
SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box);
|
||||
UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
|
|
@ -503,8 +503,8 @@
|
|||
.random_position = 0.0f, \
|
||||
.rotation = 0.0f, \
|
||||
.random_rotation = 0.0f, \
|
||||
.particle_offset = 1.0f, \
|
||||
.particle_amount = 0.0f, \
|
||||
.particle_offset = 0.0f, \
|
||||
.particle_amount = 1.0f, \
|
||||
.index_layer_name = "", \
|
||||
.value_layer_name = "", \
|
||||
}
|
||||
|
|
|
@ -1084,6 +1084,10 @@ typedef struct NodeAttributeMix {
|
|||
uint8_t input_type_b;
|
||||
} NodeAttributeMix;
|
||||
|
||||
typedef struct NodeAttributeColorRamp {
|
||||
ColorBand color_ramp;
|
||||
} NodeAttributeColorRamp;
|
||||
|
||||
/* script node mode */
|
||||
#define NODE_SCRIPT_INTERNAL 0
|
||||
#define NODE_SCRIPT_EXTERNAL 1
|
||||
|
|
|
@ -8507,6 +8507,18 @@ static void def_geo_point_distribute(StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_color_ramp(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeAttributeColorRamp", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "ColorRamp");
|
||||
RNA_def_property_ui_text(prop, "Color Ramp", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
|
|
|
@ -138,6 +138,7 @@ set(SRC
|
|||
function/nodes/node_fn_switch.cc
|
||||
function/node_function_util.cc
|
||||
|
||||
geometry/nodes/node_geo_attribute_color_ramp.cc
|
||||
geometry/nodes/node_geo_attribute_fill.cc
|
||||
geometry/nodes/node_geo_attribute_math.cc
|
||||
geometry/nodes/node_geo_attribute_randomize.cc
|
||||
|
|
|
@ -39,6 +39,7 @@ void register_node_type_geo_attribute_randomize(void);
|
|||
void register_node_type_geo_attribute_math(void);
|
||||
void register_node_type_geo_join_geometry(void);
|
||||
void register_node_type_geo_attribute_mix(void);
|
||||
void register_node_type_geo_attribute_color_ramp(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ using bke::Float3ReadAttribute;
|
|||
using bke::Float3WriteAttribute;
|
||||
using bke::FloatReadAttribute;
|
||||
using bke::FloatWriteAttribute;
|
||||
using bke::Int32ReadAttribute;
|
||||
using bke::Int32WriteAttribute;
|
||||
using bke::PersistentDataHandleMap;
|
||||
using bke::PersistentObjectHandle;
|
||||
using bke::ReadAttribute;
|
||||
|
|
|
@ -279,6 +279,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUT
|
|||
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
|
||||
|
||||
/* undefine macros */
|
||||
#undef DefNode
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BKE_colorband.h"
|
||||
|
||||
static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_STRING, N_("Attribute")},
|
||||
{SOCK_STRING, N_("Result")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_attribute_color_ramp_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component)
|
||||
{
|
||||
const bNode &bnode = params.node();
|
||||
NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage;
|
||||
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
/* Once we support more domains at the user level, we have to decide how the result domain is
|
||||
* choosen. */
|
||||
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
|
||||
const CustomDataType result_type = CD_PROP_COLOR;
|
||||
|
||||
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
|
||||
result_name, result_domain, result_type);
|
||||
if (!attribute_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color4fWriteAttribute attribute_out = std::move(attribute_result);
|
||||
|
||||
const std::string input_name = params.get_input<std::string>("Attribute");
|
||||
FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
|
||||
input_name, result_domain, 0.0f);
|
||||
|
||||
Span<float> data_in = attribute_in.get_span();
|
||||
MutableSpan<Color4f> data_out = attribute_out.get_span();
|
||||
|
||||
ColorBand *color_ramp = &node_storage->color_ramp;
|
||||
for (const int i : data_in.index_range()) {
|
||||
BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
|
||||
}
|
||||
|
||||
attribute_out.apply_span();
|
||||
}
|
||||
|
||||
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
|
||||
static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
|
||||
sizeof(NodeAttributeColorRamp), __func__);
|
||||
BKE_colorband_init(&node_storage->color_ramp, true);
|
||||
node->storage = node_storage;
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_attribute_color_ramp()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0);
|
||||
node_type_socket_templates(
|
||||
&ntype, geo_node_attribute_color_ramp_in, geo_node_attribute_color_ramp_out);
|
||||
node_type_storage(
|
||||
&ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init);
|
||||
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_rand.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
@ -59,48 +60,85 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode
|
|||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void randomize_attribute(BooleanWriteAttribute &attribute, RandomNumberGenerator &rng)
|
||||
/** Rehash to combine the seed with the "id" hash and a mutator for each dimension. */
|
||||
static float noise_from_index_and_mutator(const int seed, const int hash, const int mutator)
|
||||
{
|
||||
const int combined_hash = BLI_hash_int_3d(seed, hash, mutator);
|
||||
return BLI_hash_int_01(combined_hash);
|
||||
}
|
||||
|
||||
/** Rehash to combine the seed with the "id" hash. */
|
||||
static float noise_from_index(const int seed, const int hash)
|
||||
{
|
||||
const int combined_hash = BLI_hash_int_2d(seed, hash);
|
||||
return BLI_hash_int_01(combined_hash);
|
||||
}
|
||||
|
||||
static void randomize_attribute(BooleanWriteAttribute &attribute, Span<int> hashes, const int seed)
|
||||
{
|
||||
MutableSpan<bool> attribute_span = attribute.get_span();
|
||||
for (const int i : IndexRange(attribute.size())) {
|
||||
const bool value = rng.get_float() > 0.5f;
|
||||
const bool value = noise_from_index(seed, hashes[i]) > 0.5f;
|
||||
attribute_span[i] = value;
|
||||
}
|
||||
attribute.apply_span();
|
||||
}
|
||||
|
||||
static void randomize_attribute(FloatWriteAttribute &attribute,
|
||||
float min,
|
||||
float max,
|
||||
RandomNumberGenerator &rng)
|
||||
static void randomize_attribute(
|
||||
FloatWriteAttribute &attribute, float min, float max, Span<int> hashes, const int seed)
|
||||
{
|
||||
MutableSpan<float> attribute_span = attribute.get_span();
|
||||
for (const int i : IndexRange(attribute.size())) {
|
||||
const float value = rng.get_float() * (max - min) + min;
|
||||
const float value = noise_from_index(seed, hashes[i]) * (max - min) + min;
|
||||
attribute_span[i] = value;
|
||||
}
|
||||
attribute.apply_span();
|
||||
}
|
||||
|
||||
static void randomize_attribute(Float3WriteAttribute &attribute,
|
||||
float3 min,
|
||||
float3 max,
|
||||
RandomNumberGenerator &rng)
|
||||
static void randomize_attribute(
|
||||
Float3WriteAttribute &attribute, float3 min, float3 max, Span<int> hashes, const int seed)
|
||||
{
|
||||
MutableSpan<float3> attribute_span = attribute.get_span();
|
||||
for (const int i : IndexRange(attribute.size())) {
|
||||
const float x = rng.get_float();
|
||||
const float y = rng.get_float();
|
||||
const float z = rng.get_float();
|
||||
const float x = noise_from_index_and_mutator(seed, hashes[i], 47);
|
||||
const float y = noise_from_index_and_mutator(seed, hashes[i], 8);
|
||||
const float z = noise_from_index_and_mutator(seed, hashes[i], 64);
|
||||
const float3 value = float3(x, y, z) * (max - min) + min;
|
||||
attribute_span[i] = value;
|
||||
}
|
||||
attribute.apply_span();
|
||||
}
|
||||
|
||||
static Array<int> get_element_hashes(GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const int attribute_size)
|
||||
{
|
||||
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
|
||||
ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
|
||||
Array<int> hashes(attribute_size);
|
||||
if (hash_attribute) {
|
||||
BLI_assert(hashes.size() == hash_attribute->size());
|
||||
const CPPType &cpp_type = hash_attribute->cpp_type();
|
||||
fn::GSpan items = hash_attribute->get_span();
|
||||
for (const int i : hashes.index_range()) {
|
||||
hashes[i] = (int)cpp_type.hash(items[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If there is no "id" attribute for per-point variation, just create it here. */
|
||||
RandomNumberGenerator rng;
|
||||
rng.seed(0);
|
||||
for (const int i : hashes.index_range()) {
|
||||
hashes[i] = rng.get_int32();
|
||||
}
|
||||
}
|
||||
|
||||
return hashes;
|
||||
}
|
||||
|
||||
static void randomize_attribute(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
RandomNumberGenerator &rng)
|
||||
const int seed)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
|
||||
|
@ -116,24 +154,26 @@ static void randomize_attribute(GeometryComponent &component,
|
|||
return;
|
||||
}
|
||||
|
||||
Array<int> hashes = get_element_hashes(component, domain, attribute->size());
|
||||
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT: {
|
||||
FloatWriteAttribute float_attribute = std::move(attribute);
|
||||
const float min_value = params.get_input<float>("Min_001");
|
||||
const float max_value = params.get_input<float>("Max_001");
|
||||
randomize_attribute(float_attribute, min_value, max_value, rng);
|
||||
randomize_attribute(float_attribute, min_value, max_value, hashes, seed);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
Float3WriteAttribute float3_attribute = std::move(attribute);
|
||||
const float3 min_value = params.get_input<float3>("Min");
|
||||
const float3 max_value = params.get_input<float3>("Max");
|
||||
randomize_attribute(float3_attribute, min_value, max_value, rng);
|
||||
randomize_attribute(float3_attribute, min_value, max_value, hashes, seed);
|
||||
break;
|
||||
}
|
||||
case CD_PROP_BOOL: {
|
||||
BooleanWriteAttribute boolean_attribute = std::move(attribute);
|
||||
randomize_attribute(boolean_attribute, rng);
|
||||
randomize_attribute(boolean_attribute, hashes, seed);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -147,14 +187,10 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
|
|||
const int seed = params.get_input<int>("Seed");
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
RandomNumberGenerator rng;
|
||||
rng.seed_random(seed);
|
||||
randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng);
|
||||
randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, seed);
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
RandomNumberGenerator rng;
|
||||
rng.seed_random(seed + 3245231);
|
||||
randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng);
|
||||
randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, seed);
|
||||
}
|
||||
|
||||
params.set_output("Geometry", geometry_set);
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
|
||||
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_FLOAT, N_("Minimum Distance"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Maximum Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Distance Min"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
|
||||
{SOCK_STRING, N_("Density Attribute")},
|
||||
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
|
||||
{-1, ""},
|
||||
|
@ -58,6 +58,7 @@ namespace blender::nodes {
|
|||
static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh,
|
||||
const float density,
|
||||
const FloatReadAttribute &density_factors,
|
||||
Vector<int> &r_ids,
|
||||
const int seed)
|
||||
{
|
||||
/* This only updates a cache and can be considered to be logically const. */
|
||||
|
@ -95,6 +96,9 @@ static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh,
|
|||
float3 point_pos;
|
||||
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
|
||||
points.append(point_pos);
|
||||
|
||||
/* Build a hash stable even when the mesh is deformed. */
|
||||
r_ids.append(((int)(bary_coords.hash()) + looptri_index));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,10 +110,14 @@ struct RayCastAll_Data {
|
|||
|
||||
BVHTree_RayCastCallback raycast_callback;
|
||||
|
||||
/** The original coordinate the result point was projected from. */
|
||||
float2 raystart;
|
||||
|
||||
const Mesh *mesh;
|
||||
float base_weight;
|
||||
FloatReadAttribute *density_factors;
|
||||
Vector<float3> *projected_points;
|
||||
Vector<int> *stable_ids;
|
||||
float cur_point_weight;
|
||||
};
|
||||
|
||||
|
@ -148,6 +156,9 @@ static void project_2d_bvh_callback(void *userdata,
|
|||
|
||||
if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) {
|
||||
data->projected_points->append(hit->co);
|
||||
|
||||
/* Build a hash stable even when the mesh is deformed. */
|
||||
data->stable_ids->append((int)data->raystart.hash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +167,7 @@ static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
|
|||
const float density,
|
||||
const float minimum_distance,
|
||||
const FloatReadAttribute &density_factors,
|
||||
Vector<int> &r_ids,
|
||||
const int seed)
|
||||
{
|
||||
Vector<float3> points;
|
||||
|
@ -191,6 +203,7 @@ static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
|
|||
const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0);
|
||||
poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max);
|
||||
Vector<float3> final_points;
|
||||
r_ids.reserve(output_points_target);
|
||||
final_points.reserve(output_points_target);
|
||||
|
||||
/* Check if we have any points we should remove from the final possion distribition. */
|
||||
|
@ -205,6 +218,7 @@ static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
|
|||
data.raycast_callback = treedata.raycast_callback;
|
||||
data.mesh = mesh;
|
||||
data.projected_points = &final_points;
|
||||
data.stable_ids = &r_ids;
|
||||
data.density_factors = const_cast<FloatReadAttribute *>(&density_factors);
|
||||
data.base_weight = std::min(
|
||||
1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier)));
|
||||
|
@ -229,6 +243,7 @@ static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
|
|||
raystart.y = output_points[idx].y + tile_curr_y_coord;
|
||||
|
||||
data.cur_point_weight = (float)idx / (float)output_points.size();
|
||||
data.raystart = raystart;
|
||||
|
||||
BLI_bvhtree_ray_cast_all(
|
||||
treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data);
|
||||
|
@ -252,7 +267,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
|||
return;
|
||||
}
|
||||
|
||||
const float density = params.extract_input<float>("Maximum Density");
|
||||
const float density = params.extract_input<float>("Density Max");
|
||||
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
|
||||
|
||||
if (density <= 0.0f) {
|
||||
|
@ -267,15 +282,17 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
|||
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
|
||||
const int seed = params.get_input<int>("Seed");
|
||||
|
||||
Vector<int> stable_ids;
|
||||
Vector<float3> points;
|
||||
|
||||
switch (distribute_method) {
|
||||
case GEO_NODE_POINT_DISTRIBUTE_RANDOM:
|
||||
points = random_scatter_points_from_mesh(mesh_in, density, density_factors, seed);
|
||||
points = random_scatter_points_from_mesh(
|
||||
mesh_in, density, density_factors, stable_ids, seed);
|
||||
break;
|
||||
case GEO_NODE_POINT_DISTRIBUTE_POISSON:
|
||||
const float min_dist = params.extract_input<float>("Minimum Distance");
|
||||
points = poisson_scatter_points_from_mesh(mesh_in, density, min_dist, density_factors, seed);
|
||||
const float min_dist = params.extract_input<float>("Distance Min");
|
||||
points = poisson_scatter_points_from_mesh(
|
||||
mesh_in, density, min_dist, density_factors, stable_ids, seed);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -286,7 +303,16 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
|
|||
pointcloud->radius[i] = 0.05f;
|
||||
}
|
||||
|
||||
geometry_set_out.replace_pointcloud(pointcloud);
|
||||
PointCloudComponent &point_component =
|
||||
geometry_set_out.get_component_for_write<PointCloudComponent>();
|
||||
point_component.replace(pointcloud);
|
||||
|
||||
Int32WriteAttribute stable_id_attribute = point_component.attribute_try_ensure_for_write(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
MutableSpan<int> stable_ids_span = stable_id_attribute.get_span();
|
||||
stable_ids_span.copy_from(stable_ids);
|
||||
stable_id_attribute.apply_span();
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set_out));
|
||||
}
|
||||
} // namespace blender::nodes
|
||||
|
|
|
@ -194,6 +194,7 @@ struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSetting
|
|||
struct Editing *BKE_sequencer_editing_get(struct Scene *scene, bool alloc);
|
||||
struct Editing *BKE_sequencer_editing_ensure(struct Scene *scene);
|
||||
void BKE_sequencer_editing_free(struct Scene *scene, const bool do_id_user);
|
||||
struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed);
|
||||
void BKE_sequencer_sort(struct Scene *scene);
|
||||
struct Sequence *BKE_sequencer_from_elem(ListBase *seqbase, struct StripElem *se);
|
||||
struct Sequence *BKE_sequencer_active_get(struct Scene *scene);
|
||||
|
|
|
@ -327,6 +327,20 @@ void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_metho
|
|||
tool_settings->fit_method = fit_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase
|
||||
*
|
||||
* \param ed: sequence editor data
|
||||
* \return pointer to active seqbase. returns NULL if ed is NULL
|
||||
*/
|
||||
ListBase *SEQ_active_seqbase_get(const Editing *ed)
|
||||
{
|
||||
if (ed == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ed->seqbasep;
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -121,6 +121,7 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad
|
|||
|
||||
char file_path[FILE_MAX];
|
||||
BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name);
|
||||
BLI_path_abs(file_path, BKE_main_blendfile_path(CTX_data_main(C)));
|
||||
ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
|
||||
if (ibuf != NULL) {
|
||||
SEQ_set_scale_to_fit(seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
|
||||
|
|
|
@ -361,28 +361,26 @@ float BKE_sequence_get_fps(Scene *scene, Sequence *seq)
|
|||
*/
|
||||
void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect)
|
||||
{
|
||||
float min[2], max[2];
|
||||
min[0] = scene->r.sfra;
|
||||
max[0] = scene->r.efra + 1;
|
||||
min[1] = 0.0;
|
||||
max[1] = 8.0;
|
||||
rect->xmin = scene->r.sfra;
|
||||
rect->xmax = scene->r.efra + 1;
|
||||
rect->ymin = 0.0f;
|
||||
rect->ymax = 8.0f;
|
||||
|
||||
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
|
||||
if (min[0] > seq->startdisp - 1) {
|
||||
min[0] = seq->startdisp - 1;
|
||||
}
|
||||
if (max[0] < seq->enddisp + 1) {
|
||||
max[0] = seq->enddisp + 1;
|
||||
}
|
||||
if (max[1] < seq->machine + 2) {
|
||||
max[1] = seq->machine + 2;
|
||||
}
|
||||
if (seqbase == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
rect->xmin = min[0];
|
||||
rect->xmax = max[0];
|
||||
rect->ymin = min[1];
|
||||
rect->ymax = max[1];
|
||||
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
|
||||
if (rect->xmin > seq->startdisp - 1) {
|
||||
rect->xmin = seq->startdisp - 1;
|
||||
}
|
||||
if (rect->xmax < seq->enddisp + 1) {
|
||||
rect->xmax = seq->enddisp + 1;
|
||||
}
|
||||
if (rect->ymax < seq->machine + 2) {
|
||||
rect->ymax = seq->machine + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -204,6 +204,48 @@ add_blender_test(
|
|||
--run-all-tests
|
||||
)
|
||||
|
||||
add_blender_test(
|
||||
physics_dynamic_paint
|
||||
${TEST_SRC_DIR}/physics/dynamic_paint_test.blend
|
||||
--python ${TEST_PYTHON_DIR}/physics_dynamic_paint.py
|
||||
--
|
||||
--run-all-tests
|
||||
)
|
||||
|
||||
add_blender_test(
|
||||
deform_modifiers
|
||||
${TEST_SRC_DIR}/modeling/deform_modifiers.blend
|
||||
--python ${TEST_PYTHON_DIR}/deform_modifiers.py
|
||||
--
|
||||
--run-all-tests
|
||||
)
|
||||
|
||||
add_blender_test(
|
||||
physics_ocean
|
||||
${TEST_SRC_DIR}/physics/ocean_test.blend
|
||||
--python ${TEST_PYTHON_DIR}/physics_ocean.py
|
||||
--
|
||||
--run-all-tests
|
||||
)
|
||||
|
||||
|
||||
add_blender_test(
|
||||
physics_particle_system
|
||||
${TEST_SRC_DIR}/physics/physics_particle_test.blend
|
||||
--python ${TEST_PYTHON_DIR}/physics_particle_system.py
|
||||
--
|
||||
--run-all-tests
|
||||
)
|
||||
|
||||
# Particle Instance disabling currently broken in master
|
||||
# add_blender_test(
|
||||
# physics_particle_instance
|
||||
# ${TEST_SRC_DIR}/physics/physics_particle_instance.blend
|
||||
# --python ${TEST_PYTHON_DIR}/physics_particle_instance.py
|
||||
# --
|
||||
# --run-all-tests
|
||||
# )
|
||||
|
||||
add_blender_test(
|
||||
constraints
|
||||
--python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py
|
||||
|
|
|
@ -27,151 +27,287 @@ import os
|
|||
import sys
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import OperatorTest
|
||||
from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
|
||||
|
||||
|
||||
def main():
|
||||
tests = [
|
||||
# 0
|
||||
['EDGE', {10}, 'Cube_test', 'Cube_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {10, 7}, 'Cube_test', 'Cube_result_2', 'bevel', {'offset': 0.2, 'offset_type': 'WIDTH'}],
|
||||
['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_3', 'bevel', {'offset': 0.2, 'offset_type': 'DEPTH'}],
|
||||
['EDGE', {10}, 'Cube_test', 'Cube_result_4', 'bevel', {'offset': 0.4, 'segments': 2}],
|
||||
['EDGE', {10, 7}, 'Cube_test', 'Cube_result_5', 'bevel', {'offset': 0.4, 'segments': 3}],
|
||||
# 5
|
||||
['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_6', 'bevel', {'offset': 0.4, 'segments': 4}],
|
||||
['EDGE', {0, 10, 4, 7}, 'Cube_test', 'Cube_result_7', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.2}],
|
||||
['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_8', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.25}],
|
||||
['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_9', 'bevel', {'offset': 0.4, 'segments': 6, 'profile': 0.9}],
|
||||
['EDGE', {10, 7}, 'Cube_test', 'Cube_result_10', 'bevel', {'offset': 0.4, 'segments': 4, 'profile': 1.0}],
|
||||
# 10
|
||||
['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_11', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 1.0}],
|
||||
['EDGE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cube_test', 'Cube_result_12', 'bevel',
|
||||
{'offset': 0.4, 'segments': 8}],
|
||||
['EDGE', {5}, 'Pyr4_test', 'Pyr4_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {2, 5}, 'Pyr4_test', 'Pyr4_result_2', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {2, 3, 5}, 'Pyr4_test', 'Pyr4_result_3', 'bevel', {'offset': 0.2}],
|
||||
# 15
|
||||
['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_4', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_5', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {2, 3}, 'Pyr4_test', 'Pyr4_result_6', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_7', 'bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}],
|
||||
['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}],
|
||||
# 20
|
||||
['VERT', {1}, 'Pyr4_test', 'Pyr4_result_9', 'bevel',
|
||||
{'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}],
|
||||
['EDGE', {2, 3}, 'Pyr6_test', 'Pyr6_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {8, 2, 3}, 'Pyr6_test', 'Pyr6_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, 'Pyr6_test', 'Pyr6_result_3', 'bevel',
|
||||
{'offset': 0.2, 'segments': 4, 'profile': 0.8}],
|
||||
['EDGE', {8, 9, 3, 11}, 'Sept_test', 'Sept_result_1', 'bevel', {'offset': 0.1}],
|
||||
# 25
|
||||
['EDGE', {8, 9, 11}, 'Sept_test', 'Sept_result_2', 'bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}],
|
||||
['EDGE', {2, 8, 9, 12, 13, 14}, 'Saddle_test', 'Saddle_result_1', 'bevel', {'offset': 0.3, 'segments': 5}],
|
||||
['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}],
|
||||
['EDGE', {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, 112, 113, 114, 115},
|
||||
'Bent_test', 'Bent_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {1, 8, 9, 10, 11}, 'Bentlines_test', 'Bentlines_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
# 30
|
||||
['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_1', 'bevel', {'offset': 0.4, 'segments': 2}],
|
||||
['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_2', 'bevel',
|
||||
{'offset': 0.4, 'segments': 2, 'profile': 1.0}],
|
||||
['FACE', {1, 6, 7, 8, 9, 10, 11, 12}, 'Flaretop_test', 'Flaretop_result_3', 'bevel',
|
||||
{'offset': 0.4, 'segments': 4}],
|
||||
['EDGE', {4, 8, 10, 18, 24}, 'BentL_test', 'BentL_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {0, 1, 2, 10}, 'Wires_test', 'Wires_test_result_1', 'bevel', {'offset': 0.3}],
|
||||
# 35
|
||||
['VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 'Wires_test', 'Wires_test_result_2', 'bevel',
|
||||
{'offset': 0.3, 'affect': 'VERTICES'}],
|
||||
['EDGE', {3, 4, 5}, 'tri', 'tri_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4, 5}, 'tri', 'tri_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {3, 4, 5}, 'tri', 'tri_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {3, 4}, 'tri', 'tri_result_4', 'bevel', {'offset': 0.2}],
|
||||
# 40
|
||||
['EDGE', {3, 4}, 'tri', 'tri_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
|
||||
['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}],
|
||||
['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}],
|
||||
['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
|
||||
# 45
|
||||
['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_4', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
# 50
|
||||
['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_7', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_8', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_9', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
|
||||
# 55
|
||||
['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_4', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
# 60
|
||||
['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_1', 'bevel', {'offset': 0.2}],
|
||||
# 65
|
||||
['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_2', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {32, 35}, 'cube3', 'cube3_result_3', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {24, 35}, 'cube3', 'cube3_result_4', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
|
||||
['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
|
||||
# 70
|
||||
['EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, 'Tray', 'Tray_result_1', 'bevel', {'offset': 0.01, 'segments': 2}],
|
||||
['EDGE', {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, 'Bumptop', 'Bumptop_result_1', 'bevel',
|
||||
{'offset': 0.1, 'segments': 4}],
|
||||
['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}],
|
||||
['EDGE', {19, 20, 23, 15}, 'Window_test', 'Window_result_1', 'bevel', {'offset': 0.05, 'segments': 2}],
|
||||
# 75
|
||||
['EDGE', {8}, 'Cube_hn_test', 'Cube_hn_result_1', 'bevel', {'offset': 0.2, 'harden_normals': True}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_1', 'bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'PATCH'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_2', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_3', 'bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_4', 'bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'ARC'}],
|
||||
# 80
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_5', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_6', 'bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_7', 'bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_8', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps2_test', 'Blocksteps2_result_9', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
|
||||
# 85
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps3_test', 'Blocksteps3_result_10', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_11', 'bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
|
||||
['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_12', 'bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}],
|
||||
['EDGE', {1, 7}, 'Spike_test', 'Spike_result_1', 'bevel', {'offset': 0.2, 'segments': 3}]
|
||||
]
|
||||
MeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1',
|
||||
|
||||
operator_test = OperatorTest(tests)
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2}, 'EDGE', {10})]),
|
||||
MeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'offset_type': 'WIDTH'}, 'EDGE', {10, 7}, )]),
|
||||
MeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'offset_type': 'DEPTH'}, 'EDGE', {8, 10, 7}, )]),
|
||||
MeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2}, 'EDGE', {10}, )]),
|
||||
MeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 3}, 'EDGE', {10, 7}, )]),
|
||||
# 5
|
||||
MeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4}, 'EDGE', {8, 10, 7}, )]),
|
||||
MeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 5, 'profile': 0.2}, 'EDGE', {0, 10, 4, 7}, )]),
|
||||
MeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 5, 'profile': 0.25}, 'EDGE', {8, 10, 7}, )]),
|
||||
MeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 6, 'profile': 0.9}, 'EDGE', {8, 10, 7}, )]),
|
||||
MeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 4, 'profile': 1.0}, 'EDGE', {10, 7}, )]),
|
||||
# 10
|
||||
MeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 5, 'profile': 1.0}, 'EDGE', {8, 10, 7}, )]),
|
||||
MeshTest("test 12", 'Cube_test', 'Cube_result_12',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 8}, 'EDGE',
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]),
|
||||
MeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {5}, )]),
|
||||
MeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 5}, )]),
|
||||
MeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3, 5}, )]),
|
||||
# 15
|
||||
MeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {1, 2, 3, 5}, )]),
|
||||
MeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3}, 'EDGE', {1, 2, 3, 5}, )]),
|
||||
MeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {2, 3}, )]),
|
||||
MeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 4, 'profile': 0.15}, 'EDGE', {1, 2, 3, 5}, )]),
|
||||
MeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
|
||||
# 20
|
||||
MeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}, 'VERT',
|
||||
{1}, )]),
|
||||
MeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3}, )]),
|
||||
MeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {8, 2, 3}, )]),
|
||||
MeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 4, 'profile': 0.8}, 'EDGE',
|
||||
{0, 2, 3, 4, 6, 7, 9, 10, 11}, )]),
|
||||
MeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.1}, 'EDGE', {8, 9, 3, 11}, )]),
|
||||
# 25
|
||||
MeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.1, 'offset_type': 'WIDTH'}, 'EDGE', {8, 9, 11}, )]),
|
||||
MeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.3, 'segments': 5}, 'EDGE', {2, 8, 9, 12, 13, 14}, )]),
|
||||
MeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}, 'VERT', {4}, )]),
|
||||
|
||||
MeshTest('Bent_test', 'Bent_test', 'Bent_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3},
|
||||
'EDGE',
|
||||
{2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62,
|
||||
112, 113, 114, 115}, )]),
|
||||
MeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3}, 'EDGE', {1, 8, 9, 10, 11}, )]),
|
||||
# 30
|
||||
MeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 2}, 'EDGE', {26, 12, 20}, )]),
|
||||
MeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 2, 'profile': 1.0}, 'EDGE', {26, 12, 20}, )]),
|
||||
MeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.4, 'segments': 4}, 'FACE', {1, 6, 7, 8, 9, 10, 11, 12}, )]),
|
||||
MeshTest('BentL_test', 'BentL_test', 'BentL_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {4, 8, 10, 18, 24}, )]),
|
||||
MeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.3}, 'EDGE', {0, 1, 2, 10}, )]),
|
||||
# 35
|
||||
MeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.3, 'affect': 'VERTICES'}, 'VERT',
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, )]),
|
||||
MeshTest('tri_test_1', 'tri', 'tri_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri_test_2', 'tri', 'tri_result_2',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri_test_3', 'tri', 'tri_result_3',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri_test_4', 'tri', 'tri_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
|
||||
# 40
|
||||
MeshTest('tri_test_5', 'tri', 'tri_result_5',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
|
||||
MeshTest('tri_test_6', 'tri', 'tri_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
|
||||
MeshTest('tri_test_7', 'tri', 'tri_result_7',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
|
||||
MeshTest('tri_test_8', 'tri', 'tri_result_8',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
|
||||
MeshTest('tri_test_9', 'tri', 'tri_result_9',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
|
||||
# 45
|
||||
MeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
|
||||
MeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
|
||||
# 50
|
||||
MeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]),
|
||||
MeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 5}, )]),
|
||||
MeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 5}, )]),
|
||||
MeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 5}, )]),
|
||||
MeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
|
||||
# 55
|
||||
MeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
|
||||
MeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
|
||||
# 60
|
||||
MeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]),
|
||||
MeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
|
||||
MeshTest('cube3_test_1', 'cube3', 'cube3_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2}, 'EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]),
|
||||
# 65
|
||||
MeshTest('cube3_test_2', 'cube3', 'cube3_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2}, 'EDGE',
|
||||
{32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]),
|
||||
MeshTest('cube3_test_3', 'cube3', 'cube3_result_3',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 35}, )]),
|
||||
MeshTest('cube3_test_4', 'cube3', 'cube3_result_4',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {24, 35}, )]),
|
||||
MeshTest('cube3_test_5', 'cube3', 'cube3_result_5',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {24, 32, 35}, )]),
|
||||
MeshTest('cube3_test_6', 'cube3', 'cube3_result_6',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {24, 32, 35}, )]),
|
||||
# 70
|
||||
MeshTest('Tray', 'Tray', 'Tray_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.01, 'segments': 2}, 'EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, )]),
|
||||
MeshTest("test 73", 'Bumptop', 'Bumptop_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.1, 'segments': 4}, 'EDGE',
|
||||
{33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, )]),
|
||||
MeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2}, 'EDGE', {16, 14, 15}, )]),
|
||||
MeshTest('Window_test', 'Window_test', 'Window_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.05, 'segments': 2}, 'EDGE', {19, 20, 23, 15}, )]),
|
||||
# 75
|
||||
MeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'harden_normals': True}, 'EDGE', {8}, )]),
|
||||
MeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
|
||||
# 80
|
||||
MeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'},
|
||||
'EDGE', {4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
# 85
|
||||
MeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12',
|
||||
[OperatorSpecEditMode('bevel',
|
||||
{'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE',
|
||||
{4, 7, 39, 27, 30, 31}, )]),
|
||||
MeshTest('Spike_test', 'Spike_test', 'Spike_result_1',
|
||||
[OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 7})])
|
||||
|
||||
]
|
||||
operator_test = RunTest(tests)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
operator_test.do_compare = True
|
||||
operator_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
index = int(command[i + 1])
|
||||
operator_test.run_test(index)
|
||||
name = command[i + 1]
|
||||
operator_test.do_compare = False
|
||||
operator_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -29,33 +29,53 @@ import os
|
|||
import sys
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import OperatorTest
|
||||
from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
|
||||
|
||||
|
||||
def main():
|
||||
tests = [
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cubecube', 'Cubecube_result_7', 'intersect',
|
||||
{'mode': 'SELECT', 'separate_mode': 'NONE', 'solver' : 'FAST'}],
|
||||
['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
|
||||
|
||||
MeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1',
|
||||
[OperatorSpecEditMode('intersect_boolean',
|
||||
{'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2',
|
||||
[OperatorSpecEditMode('intersect_boolean', {'operation': 'INTERSECT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3',
|
||||
[OperatorSpecEditMode('intersect_boolean', {'operation': 'DIFFERENCE', 'solver': 'FAST'}, 'FACE',
|
||||
{0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect',
|
||||
{'separate_mode': 'CUT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5',
|
||||
[OperatorSpecEditMode('intersect',
|
||||
{'separate_mode': 'ALL', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6',
|
||||
[OperatorSpecEditMode('intersect',
|
||||
{'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
MeshTest('Cubecube_intersect_select_none', 'Cubecube',
|
||||
'Cubecube_result_7',
|
||||
[OperatorSpecEditMode('intersect',
|
||||
{'mode': 'SELECT', 'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE',
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]),
|
||||
MeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1',
|
||||
[OperatorSpecEditMode('intersect_boolean',
|
||||
{'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {6, 7, 8, 9, 10}, )]),
|
||||
MeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1',
|
||||
[OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
|
||||
]
|
||||
|
||||
operator_test = OperatorTest(tests)
|
||||
operator_test = RunTest(tests)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
operator_test.do_compare = True
|
||||
operator_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
index = int(command[i + 1])
|
||||
operator_test.run_test(index)
|
||||
name = command[i + 1]
|
||||
operator_test.do_compare = False
|
||||
operator_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# To run the test type: blender -b /path/to/the/blend/file --python path/to/this/py/file -- --run-all-tests -- --verbose
|
||||
# Type the above line in cmd/terminal, for example, look below
|
||||
# blender -b c:\blender-lib\deform_modifiers.blend --python c:\deform_modifiers.py -- --run-all-tests -- --verbose
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import MeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest
|
||||
|
||||
tests = [
|
||||
|
||||
# Surface Deform Test, finally can bind to the Target object.
|
||||
# Actual deformation occurs by animating imitating user input.
|
||||
|
||||
MeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform",
|
||||
[DeformModifierSpec(10, [
|
||||
ModifierSpec('surface_deform', 'SURFACE_DEFORM', {'target': bpy.data.objects["Cube"]})],
|
||||
OperatorSpecObjectMode('surfacedeform_bind', {'modifier': 'surface_deform'}))]),
|
||||
|
||||
# Mesh Deform Test, finally can bind to the Target object.
|
||||
# Actual deformation occurs by animating imitating user input.
|
||||
|
||||
MeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform",
|
||||
[DeformModifierSpec(10, [ModifierSpec('mesh_deform', 'MESH_DEFORM',
|
||||
{'object': bpy.data.objects["MeshCube"], 'precision': 2})],
|
||||
OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform'}))]),
|
||||
|
||||
# Surface Deform Test, finally can bind to the Target object.
|
||||
# Actual deformation occurs by animating imitating user input.
|
||||
|
||||
MeshTest("Hook", "testObjHookPlane", "expObjHookPlane",
|
||||
[DeformModifierSpec(10, [ModifierSpec('hook', 'HOOK',
|
||||
{'object': bpy.data.objects["Empty"], 'falloff_radius': 1,
|
||||
'vertex_group': 'Group'})])]),
|
||||
|
||||
# Laplacian Deform Test, first a hook is attached.
|
||||
|
||||
MeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian",
|
||||
[DeformModifierSpec(10,
|
||||
[ModifierSpec('hook2', 'HOOK', {'object': bpy.data.objects["Empty.001"],
|
||||
'vertex_group': 'hook_vg'}),
|
||||
ModifierSpec('laplace', 'LAPLACIANDEFORM', {'vertex_group': 'laplace_vg'})],
|
||||
OperatorSpecObjectMode('laplaciandeform_bind', {'modifier': 'laplace'}))]),
|
||||
|
||||
MeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp",
|
||||
[DeformModifierSpec(10, [ModifierSpec('warp', 'WARP',
|
||||
{'object_from': bpy.data.objects["From"],
|
||||
'object_to': bpy.data.objects["To"],
|
||||
})])]),
|
||||
|
||||
#############################################
|
||||
# Curves Deform Modifiers
|
||||
#############################################
|
||||
MeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature",
|
||||
[DeformModifierSpec(10, [ModifierSpec('curve_armature', 'ARMATURE',
|
||||
{'object': bpy.data.objects['testArmatureHelper'],
|
||||
'use_vertex_groups': False, 'use_bone_envelopes': True})])]),
|
||||
|
||||
MeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice",
|
||||
[DeformModifierSpec(10, [ModifierSpec('curve_lattice', 'LATTICE',
|
||||
{'object': bpy.data.objects['testLatticeCurve']})])]),
|
||||
|
||||
# HOOK for Curves can't be tested with current framework, as it requires going to Edit Mode to select vertices,
|
||||
# here is no equivalent of a vertex group in Curves.
|
||||
# Dummy test for Hook, can also be called corner case
|
||||
MeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook",
|
||||
[DeformModifierSpec(10,
|
||||
[ModifierSpec('curve_Hook', 'HOOK', {'object': bpy.data.objects['EmptyCurve']})])]),
|
||||
|
||||
MeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform",
|
||||
[DeformModifierSpec(10, [
|
||||
ModifierSpec('mesh_deform_curve', 'MESH_DEFORM', {'object': bpy.data.objects["Cylinder"],
|
||||
'precision': 2})],
|
||||
OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform_curve'}))]),
|
||||
|
||||
MeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp",
|
||||
[DeformModifierSpec(10, [ModifierSpec('warp_curve', 'WARP',
|
||||
{'object_from': bpy.data.objects["From_curve"],
|
||||
'object_to': bpy.data.objects["To_curve"]})])]),
|
||||
|
||||
]
|
||||
|
||||
deform_tests = RunTest(tests)
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
deform_tests.apply_modifiers = True
|
||||
deform_tests.do_compare = True
|
||||
deform_tests.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
deform_tests.apply_modifiers = False
|
||||
deform_tests.do_compare = False
|
||||
name = command[i + 1]
|
||||
deform_tests.run_test(name)
|
||||
break
|
|
@ -26,7 +26,7 @@ from random import shuffle, seed
|
|||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import ModifierTest, ModifierSpec
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
seed(0)
|
||||
|
||||
|
@ -47,7 +47,7 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
|
|||
ModifierSpec('array', 'ARRAY', {}),
|
||||
ModifierSpec('bevel', 'BEVEL', {'width': 0.1}),
|
||||
ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object, 'solver': 'FAST'}),
|
||||
ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1}),
|
||||
ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2),
|
||||
ModifierSpec('decimate', 'DECIMATE', {}),
|
||||
ModifierSpec('edge split', 'EDGE_SPLIT', {}),
|
||||
|
||||
|
@ -62,7 +62,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
|
|||
# ModifierSpec('remesh', 'REMESH', {}),
|
||||
|
||||
# ModifierSpec('screw', 'SCREW', {}), # screw can make the test very slow. Skipping for now.
|
||||
# ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible .
|
||||
|
||||
ModifierSpec('solidify', 'SOLIDIFY', {}),
|
||||
ModifierSpec('subsurf', 'SUBSURF', {}),
|
||||
|
@ -78,7 +77,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
|
|||
|
||||
|
||||
def main():
|
||||
|
||||
mask_first_list = get_generate_modifiers_list("testCubeMaskFirst", randomize=True)
|
||||
mask_vertex_group = "testCubeMaskFirst" + "_mask"
|
||||
mask_first_list.insert(0, ModifierSpec('mask', 'MASK', {'vertex_group': mask_vertex_group}))
|
||||
|
@ -88,169 +86,279 @@ def main():
|
|||
# List of 'Generate' modifiers on a cube
|
||||
###############################
|
||||
# 0
|
||||
# ["testCube", "expectedCube", get_generate_modifiers_list("testCube")],
|
||||
["testCubeRandom", "expectedCubeRandom", get_generate_modifiers_list("testCubeRandom", randomize=True)],
|
||||
["testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list],
|
||||
# MeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")),
|
||||
MeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom",
|
||||
get_generate_modifiers_list("testCubeRandom", randomize=True)),
|
||||
MeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list),
|
||||
|
||||
["testCollapseDecimate", "expectedCollapseDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]],
|
||||
["testPlanarDecimate", "expectedPlanarDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]],
|
||||
["testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]],
|
||||
MeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE',
|
||||
{'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]),
|
||||
MeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE',
|
||||
{'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]),
|
||||
MeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
|
||||
ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]),
|
||||
|
||||
# 5
|
||||
["testRadialBisectMirror", "expectedRadialBisectMirror",
|
||||
[ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}),
|
||||
ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]}),
|
||||
ModifierSpec('mirror3', 'MIRROR', {'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False), 'use_bisect_flip_axis': (False, True, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]],
|
||||
["regressT58411Mirror", "expectedT58411Mirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {}),
|
||||
ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}),
|
||||
ModifierSpec('subd', 'SUBSURF', {'levels': 1})]],
|
||||
MeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror",
|
||||
[ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}),
|
||||
ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False),
|
||||
'mirror_object': bpy.data.objects[
|
||||
"testRadialBisectMirrorHelper"]}),
|
||||
ModifierSpec('mirror3', 'MIRROR',
|
||||
{'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False),
|
||||
'use_bisect_flip_axis': (False, True, False),
|
||||
'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]),
|
||||
MeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {}),
|
||||
ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}),
|
||||
ModifierSpec('subd', 'SUBSURF', {'levels': 1})]),
|
||||
|
||||
["testBasicScrew", "expectedBasicScrew",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}),
|
||||
ModifierSpec("screw", 'SCREW', {'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2, 'use_normal_calculate': True})]],
|
||||
["testObjectScrew", "expectedObjectScrew",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}),
|
||||
ModifierSpec("screw", 'SCREW', {"angle": math.radians(600), 'steps': 32, 'iterations': 1, 'use_object_screw_offset': True, 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]],
|
||||
MeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}),
|
||||
ModifierSpec("screw", 'SCREW',
|
||||
{'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2,
|
||||
'use_normal_calculate': True})]),
|
||||
MeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}),
|
||||
ModifierSpec("screw", 'SCREW',
|
||||
{"angle": math.radians(600), 'steps': 32, 'iterations': 1,
|
||||
'use_object_screw_offset': True,
|
||||
'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]),
|
||||
|
||||
# 9
|
||||
["testMergedScrewWeld", "expectedMergedScrewWeld",
|
||||
[ModifierSpec("screw", 'SCREW', {'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1, 'use_normal_calculate': True, 'use_merge_vertices': True}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]],
|
||||
["regressT72380Weld", "expectedT72380Weld",
|
||||
[ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT', {'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]],
|
||||
["regressT72792Weld", "expectedT72792Weld",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]],
|
||||
MeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld",
|
||||
[ModifierSpec("screw", 'SCREW',
|
||||
{'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1,
|
||||
'use_normal_calculate': True, 'use_merge_vertices': True}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]),
|
||||
MeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld",
|
||||
[ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT',
|
||||
{'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]),
|
||||
MeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}),
|
||||
ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]),
|
||||
|
||||
############################################
|
||||
# One 'Generate' modifier on primitive meshes
|
||||
#############################################
|
||||
# 12
|
||||
["testCubeArray", "expectedCubeArray", [ModifierSpec('array', 'ARRAY', {})]],
|
||||
["testCapArray", "expectedCapArray",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_LENGTH', 'fit_length': 2.0, 'start_cap': bpy.data.objects["testCapStart"], 'end_cap': bpy.data.objects["testCapEnd"]})]],
|
||||
["testCurveArray", "expectedCurveArray",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"], 'use_relative_offset': False, 'use_constant_offset': True, 'constant_offset_displace': (0.5, 0, 0)})]],
|
||||
["testRadialArray", "expectedRadialArray",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True, 'use_merge_vertices_cap': True, 'use_relative_offset': False, 'use_object_offset': True, 'offset_object': bpy.data.objects["testRadialArrayHelper"]})]],
|
||||
MeshTest("CubeArray", "testCubeArray", "expectedCubeArray",
|
||||
[ModifierSpec('array', 'ARRAY', {})]),
|
||||
MeshTest("CapArray", "testCapArray", "expectedCapArray",
|
||||
[ModifierSpec('array', 'ARRAY',
|
||||
{'fit_type': 'FIT_LENGTH', 'fit_length': 2.0,
|
||||
'start_cap': bpy.data.objects["testCapStart"],
|
||||
'end_cap': bpy.data.objects["testCapEnd"]})]),
|
||||
MeshTest("CurveArray", "testCurveArray", "expectedCurveArray",
|
||||
[ModifierSpec('array', 'ARRAY',
|
||||
{'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"],
|
||||
'use_relative_offset': False, 'use_constant_offset': True,
|
||||
'constant_offset_displace': (0.5, 0, 0)})]),
|
||||
MeshTest("RadialArray", "testRadialArray", "expectedRadialArray",
|
||||
[ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True,
|
||||
'use_merge_vertices_cap': True, 'use_relative_offset': False,
|
||||
'use_object_offset': True,
|
||||
'offset_object': bpy.data.objects["testRadialArrayHelper"]})]),
|
||||
|
||||
["testCylinderBuild", "expectedCylinderBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1})]],
|
||||
MeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild",
|
||||
[ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
|
||||
|
||||
# 17
|
||||
["testConeDecimate", "expectedConeDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]],
|
||||
["testCubeEdgeSplit", "expectedCubeEdgeSplit", [ModifierSpec('edge split', 'EDGE_SPLIT', {})]],
|
||||
MeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate",
|
||||
[ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
|
||||
MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
|
||||
[ModifierSpec('edge split', 'EDGE_SPLIT', {})]),
|
||||
|
||||
["testSphereMirror", "expectedSphereMirror", [ModifierSpec('mirror', 'MIRROR', {})]],
|
||||
["testLocalMirror", "expectedLocalMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]],
|
||||
["testObjectOffsetMirror", "expectedObjectOffsetMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]],
|
||||
MeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {})]),
|
||||
MeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]),
|
||||
MeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR',
|
||||
{'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]),
|
||||
|
||||
["testCylinderMask", "expectedCylinderMask", [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]],
|
||||
["testConeMultiRes", "expectedConeMultiRes", [ModifierSpec('multires', 'MULTIRES', {})]],
|
||||
MeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask",
|
||||
[ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]),
|
||||
MeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes",
|
||||
[ModifierSpec('multires', 'MULTIRES', {})]),
|
||||
|
||||
# 24
|
||||
["testCubeScrew", "expectedCubeScrew", [ModifierSpec('screw', 'SCREW', {})]],
|
||||
MeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew",
|
||||
[ModifierSpec('screw', 'SCREW', {})]),
|
||||
|
||||
["testCubeSolidify", "expectedCubeSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {})]],
|
||||
["testComplexSolidify", "expectedComplexSolidify",
|
||||
[ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0, 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]],
|
||||
["regressT63063Solidify", "expectedT63063Solidify",
|
||||
[ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]],
|
||||
["regressT61979Solidify", "expectedT61979Solidify",
|
||||
[ModifierSpec('solid', 'SOLIDIFY', {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]],
|
||||
MeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify",
|
||||
[ModifierSpec('solidify', 'SOLIDIFY', {})]),
|
||||
MeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify",
|
||||
[ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0,
|
||||
'nonmanifold_thickness_mode': 'CONSTRAINTS'})]),
|
||||
MeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify",
|
||||
[ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]),
|
||||
MeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify",
|
||||
[ModifierSpec('solid', 'SOLIDIFY',
|
||||
{'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]),
|
||||
|
||||
["testMonkeySubsurf", "expectedMonkeySubsurf", [ModifierSpec('subsurf', 'SUBSURF', {})]],
|
||||
["testCatmullClarkSubdivisionSurface", "expectedCatmullClarkSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
|
||||
["testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]],
|
||||
["testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
|
||||
["testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
|
||||
MeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf",
|
||||
[ModifierSpec('subsurf', 'SUBSURF', {})]),
|
||||
MeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface",
|
||||
"expectedCatmullClarkSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
|
||||
MeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]),
|
||||
MeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
|
||||
MeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
|
||||
[ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
|
||||
|
||||
# 34
|
||||
["testSphereTriangulate", "expectedSphereTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]],
|
||||
["testMonkeyWireframe", "expectedMonkeyWireframe", [ModifierSpec('wireframe', 'WIREFRAME', {})]],
|
||||
#ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible .
|
||||
|
||||
["testMergedWeld", "expectedMergedWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]],
|
||||
["testMergedAllWeld", "expectedMergedAllWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]],
|
||||
["testMergedNoneWeld", "expectedMergedNoneWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]],
|
||||
MeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate",
|
||||
[ModifierSpec('triangulate', 'TRIANGULATE', {})]),
|
||||
MeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe",
|
||||
[ModifierSpec('wireframe', 'WIREFRAME', {})]),
|
||||
|
||||
# Duplicate the object, test object and expected object have same world coordinates.
|
||||
MeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin",
|
||||
[ModifierSpec('skin', 'SKIN', {})]),
|
||||
|
||||
MeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]),
|
||||
MeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]),
|
||||
MeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld",
|
||||
[ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]),
|
||||
|
||||
|
||||
#############################################
|
||||
# One 'Deform' modifier on primitive meshes
|
||||
#############################################
|
||||
# 39
|
||||
["testMonkeyArmature", "expectedMonkeyArmature",
|
||||
[ModifierSpec('armature', 'ARMATURE', {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]],
|
||||
["testTorusCast", "expectedTorusCast", [ModifierSpec('cast', 'CAST', {'factor': 2.64})]],
|
||||
["testCubeCurve", "expectedCubeCurve",
|
||||
[ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]],
|
||||
["testMonkeyDisplace", "expectedMonkeyDisplace", [ModifierSpec('displace', "DISPLACE", {})]],
|
||||
MeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature",
|
||||
[ModifierSpec('armature', 'ARMATURE',
|
||||
{'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]),
|
||||
MeshTest("TorusCast", "testTorusCast", "expectedTorusCast",
|
||||
[ModifierSpec('cast', 'CAST', {'factor': 2.64})]),
|
||||
MeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve",
|
||||
[ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]),
|
||||
MeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace",
|
||||
[ModifierSpec('displace', "DISPLACE", {})]),
|
||||
|
||||
# Hook modifier requires moving the hook object to get a mesh change, so can't test it with the current framework
|
||||
# ["testMonkeyHook", "expectedMonkeyHook",
|
||||
# [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': "HookVertexGroup"})]],
|
||||
# Hook modifier requires moving the hook object to get a mesh change
|
||||
# so can't test it with the current framework
|
||||
# MeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook",
|
||||
# [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group':
|
||||
# "HookVertexGroup"})]),
|
||||
|
||||
# 43
|
||||
#ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh
|
||||
["testCubeLattice", "expectedCubeLattice",
|
||||
[ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]],
|
||||
|
||||
# ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh
|
||||
MeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice",
|
||||
[ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]),
|
||||
|
||||
# Mesh Deform Modifier requires user input, so skip.
|
||||
MeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
|
||||
[ModifierSpec('shrinkwrap', 'SHRINKWRAP',
|
||||
{'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]),
|
||||
|
||||
# mesh_test = MeshTest("testMonkeyDeform", "expectedMonkeyDeform",[
|
||||
# ModifierSpec('mesh_deform', 'MESH_DEFORM', {'object': bpy.data.objects["testDeformStructure"]}),
|
||||
# OperatorSpec('meshdeform_bind',{'modifier':'MeshDeform'},'FACE',{i for in range(500)})
|
||||
# ] ,True)
|
||||
MeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
|
||||
[ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]),
|
||||
|
||||
["testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
|
||||
[ModifierSpec('shrinkwrap', 'SHRINKWRAP', {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]],
|
||||
|
||||
["testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
|
||||
[ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]],
|
||||
|
||||
["testPlaneSmooth", "expectedPlaneSmooth",
|
||||
[ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]],
|
||||
MeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth",
|
||||
[ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]),
|
||||
|
||||
# Smooth corrective requires a complex mesh.
|
||||
|
||||
["testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
|
||||
[ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]],
|
||||
MeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
|
||||
[ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]),
|
||||
|
||||
# Surface Deform and Warp requires user input, so skip.
|
||||
# Gets updated often
|
||||
MeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave",
|
||||
[ModifierSpec('wave', 'WAVE', {})]),
|
||||
|
||||
# Wave - requires complex mesh, so skip.
|
||||
#############################################
|
||||
# CURVES Generate Modifiers
|
||||
#############################################
|
||||
# Caution: Make sure test object has no modifier in "added" state, the test may fail.
|
||||
MeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray",
|
||||
[ModifierSpec('array', 'ARRAY', {})]),
|
||||
|
||||
MeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel",
|
||||
[ModifierSpec('bevel', 'BEVEL', {})]),
|
||||
|
||||
MeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild",
|
||||
[ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
|
||||
|
||||
MeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate",
|
||||
[ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
|
||||
|
||||
MeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit",
|
||||
[ModifierSpec('edgeSplit', 'EDGE_SPLIT', {})]),
|
||||
|
||||
MeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror",
|
||||
[ModifierSpec('mirror', 'MIRROR', {'use_axis': (True, True, False)})]),
|
||||
|
||||
MeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew",
|
||||
[ModifierSpec('screw', 'SCREW', {})]),
|
||||
|
||||
MeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify",
|
||||
[ModifierSpec('solidify', 'SOLIDIFY', {'thickness': 1})]),
|
||||
|
||||
MeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf",
|
||||
[ModifierSpec('subSurf', 'SUBSURF', {})]),
|
||||
|
||||
MeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate",
|
||||
[ModifierSpec('triangulate', 'TRIANGULATE', {})]),
|
||||
|
||||
# Test 60
|
||||
# Caution Weld: if the distance is increased beyond a limit, the object disappears
|
||||
MeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld",
|
||||
[ModifierSpec('weld', 'WELD', {})]),
|
||||
|
||||
MeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2",
|
||||
[ModifierSpec('weld', 'WELD', {})]),
|
||||
|
||||
#############################################
|
||||
# Curves Deform Modifiers
|
||||
#############################################
|
||||
# Test 62
|
||||
MeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast",
|
||||
[ModifierSpec('Cast', 'CAST', {'cast_type': 'CYLINDER', 'factor': 10})]),
|
||||
|
||||
MeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap",
|
||||
[ModifierSpec('ShrinkWrap', 'SHRINKWRAP',
|
||||
{'target': bpy.data.objects['testShrinkWrapHelperSuzanne']})]),
|
||||
|
||||
MeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform",
|
||||
[ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(90)})]),
|
||||
|
||||
MeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth",
|
||||
[ModifierSpec('smooth', 'SMOOTH', {'factor': 10})]),
|
||||
|
||||
MeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave",
|
||||
[ModifierSpec('curve_wave', 'WAVE', {'time_offset': -1.5})]),
|
||||
|
||||
MeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve",
|
||||
[ModifierSpec('curve_Curve', 'CURVE', {'object': bpy.data.objects['NurbsCurve']})]),
|
||||
|
||||
]
|
||||
|
||||
modifiers_test = ModifierTest(tests)
|
||||
modifiers_test = RunTest(tests)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
modifiers_test.apply_modifiers = True
|
||||
modifiers_test.do_compare = True
|
||||
modifiers_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
modifiers_test.apply_modifiers = False
|
||||
index = int(command[i + 1])
|
||||
modifiers_test.run_test(index)
|
||||
modifiers_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
modifiers_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ import functools
|
|||
import inspect
|
||||
import os
|
||||
|
||||
|
||||
# Output from this module and from blender itself will occur during tests.
|
||||
# We need to flush python so that the output is properly interleaved, otherwise
|
||||
# blender's output for one test will end up showing in the middle of another test...
|
||||
|
@ -54,37 +53,39 @@ print = functools.partial(print, flush=True)
|
|||
|
||||
class ModifierSpec:
|
||||
"""
|
||||
Holds one modifier and its parameters.
|
||||
Holds a Generate or Deform or Physics modifier type and its parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict):
|
||||
def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end=0):
|
||||
"""
|
||||
Constructs a modifier spec.
|
||||
:param modifier_name: str - name of object modifier, e.g. "myFirstSubsurfModif"
|
||||
:param modifier_type: str - type of object modifier, e.g. "SUBSURF"
|
||||
:param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
|
||||
:param frame_end: int - frame at which simulation needs to be baked or modifier needs to be applied.
|
||||
"""
|
||||
self.modifier_name = modifier_name
|
||||
self.modifier_type = modifier_type
|
||||
self.modifier_parameters = modifier_parameters
|
||||
self.frame_end = frame_end
|
||||
|
||||
def __str__(self):
|
||||
return "Modifier: " + self.modifier_name + " of type " + self.modifier_type + \
|
||||
" with parameters: " + str(self.modifier_parameters)
|
||||
|
||||
|
||||
class PhysicsSpec:
|
||||
class ParticleSystemSpec:
|
||||
"""
|
||||
Holds one Physics modifier and its parameters.
|
||||
Holds a Particle System modifier and its parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int):
|
||||
"""
|
||||
Constructs a physics spec.
|
||||
:param modifier_name: str - name of object modifier, e.g. "Cloth"
|
||||
:param modifier_type: str - type of object modifier, e.g. "CLOTH"
|
||||
:param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
|
||||
:param frame_end:int - the last frame of the simulation at which it is baked
|
||||
Constructs a particle system spec.
|
||||
:param modifier_name: str - name of object modifier, e.g. "Particles"
|
||||
:param modifier_type: str - type of object modifier, e.g. "PARTICLE_SYSTEM"
|
||||
:param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"seed" : 1}
|
||||
:param frame_end: int - the last frame of the simulation at which the modifier is applied
|
||||
"""
|
||||
self.modifier_name = modifier_name
|
||||
self.modifier_type = modifier_type
|
||||
|
@ -96,14 +97,14 @@ class PhysicsSpec:
|
|||
" with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end)
|
||||
|
||||
|
||||
class OperatorSpec:
|
||||
class OperatorSpecEditMode:
|
||||
"""
|
||||
Holds one operator and its parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, operator_name: str, operator_parameters: dict, select_mode: str, selection: set):
|
||||
"""
|
||||
Constructs an operatorSpec. Raises ValueError if selec_mode is invalid.
|
||||
Constructs an OperatorSpecEditMode. Raises ValueError if selec_mode is invalid.
|
||||
:param operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill"
|
||||
:param operator_parameters: dict - {name : val} dictionary containing operator parameters.
|
||||
:param select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE'
|
||||
|
@ -121,6 +122,45 @@ class OperatorSpec:
|
|||
" in selection mode: " + self.select_mode + ", selecting " + str(self.selection)
|
||||
|
||||
|
||||
class OperatorSpecObjectMode:
|
||||
"""
|
||||
Holds an object operator and its parameters. Helper class for DeformModifierSpec.
|
||||
Needed to support operations in Object Mode and not Edit Mode which is supported by OperatorSpecEditMode.
|
||||
"""
|
||||
|
||||
def __init__(self, operator_name: str, operator_parameters: dict):
|
||||
"""
|
||||
:param operator_name: str - name of the object operator from bpy.ops.object, e.g. "shade_smooth" or "shape_keys"
|
||||
:param operator_parameters: dict - contains operator parameters.
|
||||
"""
|
||||
self.operator_name = operator_name
|
||||
self.operator_parameters = operator_parameters
|
||||
|
||||
def __str__(self):
|
||||
return "Operator: " + self.operator_name + " with parameters: " + str(self.operator_parameters)
|
||||
|
||||
|
||||
class DeformModifierSpec:
|
||||
"""
|
||||
Holds a list of deform modifier and OperatorSpecObjectMode.
|
||||
For deform modifiers which have an object operator
|
||||
"""
|
||||
|
||||
def __init__(self, frame_number: int, modifier_list: list, object_operator_spec: OperatorSpecObjectMode = None):
|
||||
"""
|
||||
Constructs a Deform Modifier spec (for user input)
|
||||
:param frame_number: int - the frame at which animated keyframe is inserted
|
||||
:param modifier_list: ModifierSpec - contains modifiers
|
||||
:param object_operator_spec: OperatorSpecObjectMode - contains object operators
|
||||
"""
|
||||
self.frame_number = frame_number
|
||||
self.modifier_list = modifier_list
|
||||
self.object_operator_spec = object_operator_spec
|
||||
|
||||
def __str__(self):
|
||||
return "Modifier: " + str(self.modifier_list) + " with object operator " + str(self.object_operator_spec)
|
||||
|
||||
|
||||
class MeshTest:
|
||||
"""
|
||||
A mesh testing class targeted at testing modifiers and operators on a single object.
|
||||
|
@ -129,33 +169,45 @@ class MeshTest:
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
test_object_name: str,
|
||||
expected_object_name: str,
|
||||
operations_stack=None,
|
||||
apply_modifiers=False,
|
||||
threshold=None,
|
||||
self,
|
||||
test_name: str,
|
||||
test_object_name: str,
|
||||
expected_object_name: str,
|
||||
operations_stack=None,
|
||||
apply_modifiers=False,
|
||||
do_compare=False,
|
||||
threshold=None
|
||||
):
|
||||
"""
|
||||
Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name
|
||||
or test_object_name don't exist.
|
||||
:param test_object: str - Name of object of mesh type to run the operations on.
|
||||
:param expected_object: str - Name of object of mesh type that has the expected
|
||||
:param test_name: str - unique test name identifier.
|
||||
:param test_object_name: str - Name of object of mesh type to run the operations on.
|
||||
:param expected_object_name: str - Name of object of mesh type that has the expected
|
||||
geometry after running the operations.
|
||||
:param operations_stack: list - stack holding operations to perform on the test_object.
|
||||
:param apply_modifier: bool - True if we want to apply the modifiers right after adding them to the object.
|
||||
This affects operations of type ModifierSpec only.
|
||||
:param apply_modifiers: bool - True if we want to apply the modifiers right after adding them to the object.
|
||||
- True if we want to apply the modifier to a list of modifiers, after some operation.
|
||||
This affects operations of type ModifierSpec and DeformModifierSpec.
|
||||
:param do_compare: bool - True if we want to compare the test and expected objects, False otherwise.
|
||||
:param threshold : exponent: To allow variations and accept difference to a certain degree.
|
||||
|
||||
"""
|
||||
if operations_stack is None:
|
||||
operations_stack = []
|
||||
for operation in operations_stack:
|
||||
if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpec)):
|
||||
raise ValueError("Expected operation of type {} or {}. Got {}".
|
||||
format(type(ModifierSpec), type(OperatorSpec),
|
||||
if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpecEditMode)
|
||||
or isinstance(operation, OperatorSpecObjectMode) or isinstance(operation, DeformModifierSpec)
|
||||
or isinstance(operation, ParticleSystemSpec)):
|
||||
raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}".
|
||||
format(type(ModifierSpec), type(OperatorSpecEditMode),
|
||||
type(DeformModifierSpec), type(ParticleSystemSpec),
|
||||
type(operation)))
|
||||
self.operations_stack = operations_stack
|
||||
self.apply_modifier = apply_modifiers
|
||||
self.do_compare = do_compare
|
||||
self.threshold = threshold
|
||||
self.test_name = test_name
|
||||
|
||||
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
|
||||
self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
|
||||
|
@ -187,22 +239,6 @@ class MeshTest:
|
|||
objects = bpy.data.objects
|
||||
self.expected_object = objects[expected_object_name]
|
||||
|
||||
def add_modifier(self, modifier_spec: ModifierSpec):
|
||||
"""
|
||||
Add a modifier to the operations stack.
|
||||
:param modifier_spec: modifier to add to the operations stack
|
||||
"""
|
||||
self.operations_stack.append(modifier_spec)
|
||||
if self.verbose:
|
||||
print("Added modififier {}".format(modifier_spec))
|
||||
|
||||
def add_operator(self, operator_spec: OperatorSpec):
|
||||
"""
|
||||
Adds an operator to the operations stack.
|
||||
:param operator_spec: OperatorSpec - operator to add to the operations stack.
|
||||
"""
|
||||
self.operations_stack.append(operator_spec)
|
||||
|
||||
def _on_failed_test(self, compare_result, validation_success, evaluated_test_object):
|
||||
if self.update and validation_success:
|
||||
if self.verbose:
|
||||
|
@ -239,83 +275,164 @@ class MeshTest:
|
|||
"""
|
||||
return self._test_updated
|
||||
|
||||
def _apply_modifier(self, test_object, modifier_spec: ModifierSpec):
|
||||
def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name):
|
||||
"""
|
||||
Add modifier to object and apply (if modifier_spec.apply_modifier is True)
|
||||
Doing a depth first traversal of the modifier parameters and setting their values.
|
||||
:param: modifier: Of type modifier, its altered to become a setting in recursion.
|
||||
:param: modifier_parameters : dict or sequence, a simple/nested dictionary of modifier parameters.
|
||||
:param: nested_settings_path : list(stack): helps in tracing path to each node.
|
||||
"""
|
||||
if not isinstance(modifier_parameters, dict):
|
||||
param_setting = None
|
||||
for i, setting in enumerate(nested_settings_path):
|
||||
|
||||
# We want to set the attribute only when we have reached the last setting.
|
||||
# Applying of intermediate settings is meaningless.
|
||||
if i == len(nested_settings_path) - 1:
|
||||
setattr(modifier, setting, modifier_parameters)
|
||||
|
||||
elif hasattr(modifier, setting):
|
||||
param_setting = getattr(modifier, setting)
|
||||
# getattr doesn't accept canvas_surfaces["Surface"], but we need to pass it to setattr.
|
||||
if setting == "canvas_surfaces":
|
||||
modifier = param_setting.active
|
||||
else:
|
||||
modifier = param_setting
|
||||
else:
|
||||
# Clean up first
|
||||
bpy.ops.object.delete()
|
||||
raise Exception("Modifier '{}' has no parameter named '{}'".
|
||||
format(modifier_name, setting))
|
||||
|
||||
# It pops the current node before moving on to its sibling.
|
||||
nested_settings_path.pop()
|
||||
return
|
||||
|
||||
for key in modifier_parameters:
|
||||
nested_settings_path.append(key)
|
||||
self._set_parameters_impl(modifier, modifier_parameters[key], nested_settings_path, modifier_name)
|
||||
|
||||
if nested_settings_path:
|
||||
nested_settings_path.pop()
|
||||
|
||||
def set_parameters(self, modifier, modifier_parameters):
|
||||
"""
|
||||
Wrapper for _set_parameters_util
|
||||
"""
|
||||
settings = []
|
||||
modifier_name = modifier.name
|
||||
self._set_parameters_impl(modifier, modifier_parameters, settings, modifier_name)
|
||||
|
||||
def _add_modifier(self, test_object, modifier_spec: ModifierSpec):
|
||||
"""
|
||||
Add modifier to object.
|
||||
:param test_object: bpy.types.Object - Blender object to apply modifier on.
|
||||
:param modifier_spec: ModifierSpec - ModifierSpec object with parameters
|
||||
"""
|
||||
bakers_list = ['CLOTH', 'SOFT_BODY', 'DYNAMIC_PAINT', 'FLUID']
|
||||
scene = bpy.context.scene
|
||||
scene.frame_set(1)
|
||||
modifier = test_object.modifiers.new(modifier_spec.modifier_name,
|
||||
modifier_spec.modifier_type)
|
||||
|
||||
if modifier is None:
|
||||
raise Exception("This modifier type is already added on the Test Object, please remove it and try again.")
|
||||
|
||||
if self.verbose:
|
||||
print("Created modifier '{}' of type '{}'.".
|
||||
format(modifier_spec.modifier_name, modifier_spec.modifier_type))
|
||||
|
||||
for param_name in modifier_spec.modifier_parameters:
|
||||
try:
|
||||
setattr(modifier, param_name, modifier_spec.modifier_parameters[param_name])
|
||||
if self.verbose:
|
||||
print("\t set parameter '{}' with value '{}'".
|
||||
format(param_name, modifier_spec.modifier_parameters[param_name]))
|
||||
except AttributeError:
|
||||
# Clean up first
|
||||
bpy.ops.object.delete()
|
||||
raise AttributeError("Modifier '{}' has no parameter named '{}'".
|
||||
format(modifier_spec.modifier_type, param_name))
|
||||
# Special case for Dynamic Paint, need to toggle Canvas on.
|
||||
if modifier.type == "DYNAMIC_PAINT":
|
||||
bpy.ops.dpaint.type_toggle(type='CANVAS')
|
||||
|
||||
if self.apply_modifier:
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name)
|
||||
self.set_parameters(modifier, modifier_spec.modifier_parameters)
|
||||
|
||||
if modifier.type in bakers_list:
|
||||
self._bake_current_simulation(test_object, modifier.name, modifier_spec.frame_end)
|
||||
|
||||
scene.frame_set(modifier_spec.frame_end)
|
||||
|
||||
def _apply_modifier(self, test_object, modifier_name):
|
||||
# Modifier automatically gets applied when converting from Curve to Mesh.
|
||||
if test_object.type == 'CURVE':
|
||||
bpy.ops.object.convert(target='MESH')
|
||||
elif test_object.type == 'MESH':
|
||||
bpy.ops.object.modifier_apply(modifier=modifier_name)
|
||||
else:
|
||||
raise Exception("This object type is not yet supported!")
|
||||
|
||||
def _bake_current_simulation(self, test_object, test_modifier_name, frame_end):
|
||||
"""
|
||||
FLUID: Bakes the simulation
|
||||
SOFT BODY, CLOTH, DYNAMIC PAINT: Overrides the point_cache context and then bakes.
|
||||
"""
|
||||
|
||||
def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end):
|
||||
for scene in bpy.data.scenes:
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == test_mod_type:
|
||||
obj.modifiers[test_mod_name].point_cache.frame_end = frame_end
|
||||
override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache}
|
||||
for modifier in test_object.modifiers:
|
||||
if modifier.type == 'FLUID':
|
||||
bpy.ops.fluid.bake_all()
|
||||
break
|
||||
|
||||
elif modifier.type == 'CLOTH' or modifier.type == 'SOFT_BODY':
|
||||
test_object.modifiers[test_modifier_name].point_cache.frame_end = frame_end
|
||||
override_setting = modifier.point_cache
|
||||
override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting}
|
||||
bpy.ops.ptcache.bake(override, bake=True)
|
||||
break
|
||||
|
||||
def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec):
|
||||
elif modifier.type == 'DYNAMIC_PAINT':
|
||||
dynamic_paint_setting = modifier.canvas_settings.canvas_surfaces.active
|
||||
override_setting = dynamic_paint_setting.point_cache
|
||||
override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting}
|
||||
bpy.ops.ptcache.bake(override, bake=True)
|
||||
break
|
||||
|
||||
def _apply_particle_system(self, test_object, particle_sys_spec: ParticleSystemSpec):
|
||||
"""
|
||||
Apply Physics settings to test objects.
|
||||
Applies Particle System settings to test objects
|
||||
"""
|
||||
scene = bpy.context.scene
|
||||
scene.frame_set(1)
|
||||
modifier = test_object.modifiers.new(physics_spec.modifier_name,
|
||||
physics_spec.modifier_type)
|
||||
physics_setting = modifier.settings
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
test_object.modifiers.new(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type)
|
||||
|
||||
settings_name = test_object.particle_systems.active.settings.name
|
||||
particle_setting = bpy.data.particles[settings_name]
|
||||
if self.verbose:
|
||||
print("Created modifier '{}' of type '{}'.".
|
||||
format(physics_spec.modifier_name, physics_spec.modifier_type))
|
||||
format(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type))
|
||||
|
||||
for param_name in physics_spec.modifier_parameters:
|
||||
for param_name in particle_sys_spec.modifier_parameters:
|
||||
try:
|
||||
setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name])
|
||||
if param_name == "seed":
|
||||
system_setting = test_object.particle_systems[particle_sys_spec.modifier_name]
|
||||
setattr(system_setting, param_name, particle_sys_spec.modifier_parameters[param_name])
|
||||
else:
|
||||
setattr(particle_setting, param_name, particle_sys_spec.modifier_parameters[param_name])
|
||||
|
||||
if self.verbose:
|
||||
print("\t set parameter '{}' with value '{}'".
|
||||
format(param_name, physics_spec.modifier_parameters[param_name]))
|
||||
format(param_name, particle_sys_spec.modifier_parameters[param_name]))
|
||||
except AttributeError:
|
||||
# Clean up first
|
||||
bpy.ops.object.delete()
|
||||
raise AttributeError("Modifier '{}' has no parameter named '{}'".
|
||||
format(physics_spec.modifier_type, param_name))
|
||||
format(particle_sys_spec.modifier_type, param_name))
|
||||
|
||||
scene.frame_set(physics_spec.frame_end + 1)
|
||||
|
||||
self._bake_current_simulation(
|
||||
test_object,
|
||||
physics_spec.modifier_type,
|
||||
physics_spec.modifier_name,
|
||||
physics_spec.frame_end,
|
||||
)
|
||||
bpy.context.scene.frame_set(particle_sys_spec.frame_end)
|
||||
test_object.select_set(True)
|
||||
bpy.ops.object.duplicates_make_real()
|
||||
test_object.select_set(True)
|
||||
bpy.ops.object.join()
|
||||
if self.apply_modifier:
|
||||
bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name)
|
||||
self._apply_modifier(test_object, particle_sys_spec.modifier_name)
|
||||
|
||||
def _apply_operator(self, test_object, operator: OperatorSpec):
|
||||
def _apply_operator_edit_mode(self, test_object, operator: OperatorSpecEditMode):
|
||||
"""
|
||||
Apply operator on test object.
|
||||
:param test_object: bpy.types.Object - Blender object to apply operator on.
|
||||
:param operator: OperatorSpec - OperatorSpec object with parameters.
|
||||
:param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters.
|
||||
"""
|
||||
mesh = test_object.data
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
@ -340,15 +457,64 @@ class MeshTest:
|
|||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_mode(type=operator.select_mode)
|
||||
mesh_operator = getattr(bpy.ops.mesh, operator.operator_name)
|
||||
if not mesh_operator:
|
||||
raise AttributeError("No mesh operator {}".format(operator.operator_name))
|
||||
retval = mesh_operator(**operator.operator_parameters)
|
||||
|
||||
try:
|
||||
retval = mesh_operator(**operator.operator_parameters)
|
||||
except AttributeError:
|
||||
raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name))
|
||||
except TypeError as ex:
|
||||
raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex))
|
||||
|
||||
if retval != {'FINISHED'}:
|
||||
raise RuntimeError("Unexpected operator return value: {}".format(retval))
|
||||
if self.verbose:
|
||||
print("Applied {}".format(operator))
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
def _apply_operator_object_mode(self, operator: OperatorSpecObjectMode):
|
||||
"""
|
||||
Applies the object operator.
|
||||
"""
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
object_operator = getattr(bpy.ops.object, operator.operator_name)
|
||||
|
||||
try:
|
||||
retval = object_operator(**operator.operator_parameters)
|
||||
except AttributeError:
|
||||
raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name))
|
||||
except TypeError as ex:
|
||||
raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex))
|
||||
|
||||
if retval != {'FINISHED'}:
|
||||
raise RuntimeError("Unexpected operator return value: {}".format(retval))
|
||||
if self.verbose:
|
||||
print("Applied operator {}".format(operator))
|
||||
|
||||
def _apply_deform_modifier(self, test_object, operation: list):
|
||||
"""
|
||||
param: operation: list: List of modifiers or combination of modifier and object operator.
|
||||
"""
|
||||
|
||||
scene = bpy.context.scene
|
||||
scene.frame_set(1)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
modifier_operations_list = operation.modifier_list
|
||||
modifier_names = []
|
||||
object_operations = operation.object_operator_spec
|
||||
for modifier_operations in modifier_operations_list:
|
||||
if isinstance(modifier_operations, ModifierSpec):
|
||||
self._add_modifier(test_object, modifier_operations)
|
||||
modifier_names.append(modifier_operations.modifier_name)
|
||||
|
||||
if isinstance(object_operations, OperatorSpecObjectMode):
|
||||
self._apply_operator_object_mode(object_operations)
|
||||
|
||||
scene.frame_set(operation.frame_number)
|
||||
|
||||
if self.apply_modifier:
|
||||
for mod_name in modifier_names:
|
||||
self._apply_modifier(test_object, mod_name)
|
||||
|
||||
def run_test(self):
|
||||
"""
|
||||
|
@ -369,24 +535,40 @@ class MeshTest:
|
|||
evaluated_test_object = bpy.context.active_object
|
||||
evaluated_test_object.name = "evaluated_object"
|
||||
if self.verbose:
|
||||
print()
|
||||
print(evaluated_test_object.name, "is set to active")
|
||||
|
||||
# Add modifiers and operators.
|
||||
for operation in self.operations_stack:
|
||||
if isinstance(operation, ModifierSpec):
|
||||
self._apply_modifier(evaluated_test_object, operation)
|
||||
self._add_modifier(evaluated_test_object, operation)
|
||||
if self.apply_modifier:
|
||||
self._apply_modifier(evaluated_test_object, operation.modifier_name)
|
||||
|
||||
elif isinstance(operation, OperatorSpec):
|
||||
self._apply_operator(evaluated_test_object, operation)
|
||||
elif isinstance(operation, OperatorSpecEditMode):
|
||||
self._apply_operator_edit_mode(evaluated_test_object, operation)
|
||||
|
||||
elif isinstance(operation, OperatorSpecObjectMode):
|
||||
self._apply_operator_object_mode(operation)
|
||||
|
||||
elif isinstance(operation, DeformModifierSpec):
|
||||
self._apply_deform_modifier(evaluated_test_object, operation)
|
||||
|
||||
elif isinstance(operation, ParticleSystemSpec):
|
||||
self._apply_particle_system(evaluated_test_object, operation)
|
||||
|
||||
elif isinstance(operation, PhysicsSpec):
|
||||
self._apply_physics_settings(evaluated_test_object, operation)
|
||||
else:
|
||||
raise ValueError("Expected operation of type {} or {} or {}. Got {}".
|
||||
format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec),
|
||||
type(operation)))
|
||||
raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}".
|
||||
format(type(ModifierSpec), type(OperatorSpecEditMode),
|
||||
type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation)))
|
||||
|
||||
# Compare resulting mesh with expected one.
|
||||
# Compare only when self.do_compare is set to True, it is set to False for run-test and returns.
|
||||
if not self.do_compare:
|
||||
print("Meshes/objects are not compared, compare evaluated and expected object in Blender for "
|
||||
"visualization only.")
|
||||
return False
|
||||
|
||||
if self.verbose:
|
||||
print("Comparing expected mesh with resulting mesh...")
|
||||
evaluated_test_mesh = evaluated_test_object.data
|
||||
|
@ -415,90 +597,7 @@ class MeshTest:
|
|||
return self._on_failed_test(compare_result, validation_success, evaluated_test_object)
|
||||
|
||||
|
||||
class OperatorTest:
|
||||
"""
|
||||
Helper class that stores and executes operator tests.
|
||||
|
||||
Example usage:
|
||||
|
||||
>>> tests = [
|
||||
>>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}],
|
||||
>>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}],
|
||||
>>> ]
|
||||
>>> operator_test = OperatorTest(tests)
|
||||
>>> operator_test.run_all_tests()
|
||||
"""
|
||||
|
||||
def __init__(self, operator_tests):
|
||||
"""
|
||||
Constructs an operator test.
|
||||
:param operator_tests: list - list of operator test cases. Each element in the list must contain the following
|
||||
in the correct order:
|
||||
1) select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE'
|
||||
2) selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10].
|
||||
3) test_object_name: bpy.Types.Object - test object
|
||||
4) expected_object_name: bpy.Types.Object - expected object
|
||||
5) operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill"
|
||||
6) operator_parameters: dict - {name : val} dictionary containing operator parameters.
|
||||
"""
|
||||
self.operator_tests = operator_tests
|
||||
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
|
||||
self._failed_tests_list = []
|
||||
|
||||
def run_test(self, index: int):
|
||||
"""
|
||||
Run a single test from operator_tests list
|
||||
:param index: int - index of test
|
||||
:return: bool - True if test is successful. False otherwise.
|
||||
"""
|
||||
case = self.operator_tests[index]
|
||||
if len(case) != 6:
|
||||
raise ValueError("Expected exactly 6 parameters for each test case, got {}".format(len(case)))
|
||||
select_mode = case[0]
|
||||
selection = case[1]
|
||||
test_object_name = case[2]
|
||||
expected_object_name = case[3]
|
||||
operator_name = case[4]
|
||||
operator_parameters = case[5]
|
||||
|
||||
operator_spec = OperatorSpec(operator_name, operator_parameters, select_mode, selection)
|
||||
|
||||
test = MeshTest(test_object_name, expected_object_name)
|
||||
test.add_operator(operator_spec)
|
||||
|
||||
success = test.run_test()
|
||||
if test.is_test_updated():
|
||||
# Run the test again if the blend file has been updated.
|
||||
success = test.run_test()
|
||||
return success
|
||||
|
||||
def run_all_tests(self):
|
||||
for index, _ in enumerate(self.operator_tests):
|
||||
if self.verbose:
|
||||
print()
|
||||
print("Running test {}...".format(index))
|
||||
success = self.run_test(index)
|
||||
|
||||
if not success:
|
||||
self._failed_tests_list.append(index)
|
||||
|
||||
if len(self._failed_tests_list) != 0:
|
||||
print("Following tests failed: {}".format(self._failed_tests_list))
|
||||
|
||||
blender_path = bpy.app.binary_path
|
||||
blend_path = bpy.data.filepath
|
||||
frame = inspect.stack()[1]
|
||||
module = inspect.getmodule(frame[0])
|
||||
python_path = module.__file__
|
||||
|
||||
print("Run following command to open Blender and run the failing test:")
|
||||
print("{} {} --python {} -- {} {}"
|
||||
.format(blender_path, blend_path, python_path, "--run-test", "<test_index>"))
|
||||
|
||||
raise Exception("Tests {} failed".format(self._failed_tests_list))
|
||||
|
||||
|
||||
class ModifierTest:
|
||||
class RunTest:
|
||||
"""
|
||||
Helper class that stores and executes modifier tests.
|
||||
|
||||
|
@ -508,71 +607,70 @@ class ModifierTest:
|
|||
>>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}),
|
||||
>>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1})
|
||||
>>> ]
|
||||
>>> tests = [
|
||||
>>> ["testCube", "expectedCube", modifier_list],
|
||||
>>> ["testCube_2", "expectedCube_2", modifier_list]
|
||||
>>> operator_list = [
|
||||
>>> OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE),
|
||||
>>> ]
|
||||
>>> modifiers_test = ModifierTest(tests)
|
||||
>>> tests = [
|
||||
>>> MeshTest("Test1", "testCube", "expectedCube", modifier_list),
|
||||
>>> MeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list),
|
||||
>>> MeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list)
|
||||
>>> ]
|
||||
>>> modifiers_test = RunTest(tests)
|
||||
>>> modifiers_test.run_all_tests()
|
||||
"""
|
||||
|
||||
def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None):
|
||||
def __init__(self, tests, apply_modifiers=False, do_compare=False):
|
||||
"""
|
||||
Construct a modifier test.
|
||||
:param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following
|
||||
:param tests: list - list of modifier or operator test cases. Each element in the list must contain the
|
||||
following
|
||||
in the correct order:
|
||||
0) test_name: str - unique test name
|
||||
1) test_object_name: bpy.Types.Object - test object
|
||||
2) expected_object_name: bpy.Types.Object - expected object
|
||||
3) modifiers: list - list of mesh_test.ModifierSpec objects.
|
||||
3) modifiers or operators: list - list of mesh_test.ModifierSpec objects or
|
||||
mesh_test.OperatorSpecEditMode objects
|
||||
"""
|
||||
self.modifier_tests = modifier_tests
|
||||
self.tests = tests
|
||||
self._ensure_unique_test_name_or_raise_error()
|
||||
self.apply_modifiers = apply_modifiers
|
||||
self.threshold = threshold
|
||||
self.do_compare = do_compare
|
||||
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
|
||||
self._failed_tests_list = []
|
||||
|
||||
def run_test(self, index: int):
|
||||
def _ensure_unique_test_name_or_raise_error(self):
|
||||
"""
|
||||
Run a single test from self.modifier_tests list
|
||||
:param index: int - index of test
|
||||
:return: bool - True if test passed, False otherwise.
|
||||
Check if the test name is unique else raise an error.
|
||||
"""
|
||||
case = self.modifier_tests[index]
|
||||
if len(case) != 3:
|
||||
raise ValueError("Expected exactly 3 parameters for each test case, got {}".format(len(case)))
|
||||
test_object_name = case[0]
|
||||
expected_object_name = case[1]
|
||||
spec_list = case[2]
|
||||
all_test_names = []
|
||||
for each_test in self.tests:
|
||||
test_name = each_test.test_name
|
||||
all_test_names.append(test_name)
|
||||
|
||||
test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold)
|
||||
if self.apply_modifiers:
|
||||
test.apply_modifier = True
|
||||
|
||||
for modifier_spec in spec_list:
|
||||
test.add_modifier(modifier_spec)
|
||||
|
||||
success = test.run_test()
|
||||
if test.is_test_updated():
|
||||
# Run the test again if the blend file has been updated.
|
||||
success = test.run_test()
|
||||
|
||||
return success
|
||||
seen_name = set()
|
||||
for ele in all_test_names:
|
||||
if ele in seen_name:
|
||||
raise ValueError("{} is a duplicate, write a new unique name.".format(ele))
|
||||
else:
|
||||
seen_name.add(ele)
|
||||
|
||||
def run_all_tests(self):
|
||||
"""
|
||||
Run all tests in self.modifiers_tests list. Raises an exception if one the tests fails.
|
||||
Run all tests in self.tests list. Raises an exception if one the tests fails.
|
||||
"""
|
||||
for index, _ in enumerate(self.modifier_tests):
|
||||
for test_number, each_test in enumerate(self.tests):
|
||||
test_name = each_test.test_name
|
||||
if self.verbose:
|
||||
print()
|
||||
print("Running test {}...\n".format(index))
|
||||
success = self.run_test(index)
|
||||
print("Running test {}...".format(test_number))
|
||||
print("Test name {}\n".format(test_name))
|
||||
success = self.run_test(test_name)
|
||||
|
||||
if not success:
|
||||
self._failed_tests_list.append(index)
|
||||
self._failed_tests_list.append(test_name)
|
||||
|
||||
if len(self._failed_tests_list) != 0:
|
||||
print("Following tests failed: {}".format(self._failed_tests_list))
|
||||
print("\nFollowing tests failed: {}".format(self._failed_tests_list))
|
||||
|
||||
blender_path = bpy.app.binary_path
|
||||
blend_path = bpy.data.filepath
|
||||
|
@ -582,6 +680,35 @@ class ModifierTest:
|
|||
|
||||
print("Run following command to open Blender and run the failing test:")
|
||||
print("{} {} --python {} -- {} {}"
|
||||
.format(blender_path, blend_path, python_path, "--run-test", "<test_index>"))
|
||||
.format(blender_path, blend_path, python_path, "--run-test", "<test_name>"))
|
||||
|
||||
raise Exception("Tests {} failed".format(self._failed_tests_list))
|
||||
|
||||
def run_test(self, test_name: str):
|
||||
"""
|
||||
Run a single test from self.tests list
|
||||
:param test_name: int - name of test
|
||||
:return: bool - True if test passed, False otherwise.
|
||||
"""
|
||||
case = None
|
||||
for index, each_test in enumerate(self.tests):
|
||||
if test_name == each_test.test_name:
|
||||
case = self.tests[index]
|
||||
break
|
||||
|
||||
if case is None:
|
||||
raise Exception('No test called {} found!'.format(test_name))
|
||||
|
||||
test = case
|
||||
if self.apply_modifiers:
|
||||
test.apply_modifier = True
|
||||
|
||||
if self.do_compare:
|
||||
test.do_compare = True
|
||||
|
||||
success = test.run_test()
|
||||
if test.is_test_updated():
|
||||
# Run the test again if the blend file has been updated.
|
||||
success = test.run_test()
|
||||
|
||||
return success
|
||||
|
|
|
@ -26,7 +26,7 @@ from random import shuffle, seed
|
|||
seed(0)
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import OperatorTest, OperatorSpec
|
||||
from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
|
||||
|
||||
# Central vertical loop of Suzanne
|
||||
MONKEY_LOOP_VERT = {68, 69, 71, 73, 74, 75, 76, 77, 90, 129, 136, 175, 188, 189, 198, 207,
|
||||
|
@ -39,126 +39,181 @@ def main():
|
|||
tests = [
|
||||
#### 0
|
||||
# bisect
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBisect", "expectedCubeBisect", "bisect",
|
||||
{"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, "use_fill": True}],
|
||||
MeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect",
|
||||
[OperatorSpecEditMode("bisect",
|
||||
{"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True,
|
||||
"use_fill": True}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
|
||||
|
||||
# blend from shape
|
||||
['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBlendFromShape", "expectedCubeBlendFromShape", "blend_from_shape",
|
||||
{"shape": "Key 1"}],
|
||||
MeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape",
|
||||
[OperatorSpecEditMode("blend_from_shape", {"shape": "Key 1"}, 'FACE', {0, 1, 2, 3, 4, 5})]),
|
||||
|
||||
# bridge edge loops
|
||||
["FACE", {0, 1}, "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", "bridge_edge_loops", {}],
|
||||
MeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop",
|
||||
[OperatorSpecEditMode("bridge_edge_loops", {}, "FACE", {0, 1})]),
|
||||
|
||||
# decimate
|
||||
["FACE", {i for i in range(500)}, "testMonkeyDecimate", "expectedMonkeyDecimate", "decimate", {"ratio": 0.1}],
|
||||
MeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate",
|
||||
[OperatorSpecEditMode("decimate",
|
||||
{"ratio": 0.1}, "FACE", {i for i in range(500)})]),
|
||||
|
||||
### 4
|
||||
# delete
|
||||
["VERT", {3}, "testCubeDeleteVertices", "expectedCubeDeleteVertices", "delete", {}],
|
||||
["FACE", {0}, "testCubeDeleteFaces", "expectedCubeDeleteFaces", "delete", {}],
|
||||
["EDGE", {0, 1, 2, 3}, "testCubeDeleteEdges", "expectedCubeDeleteEdges", "delete", {}],
|
||||
MeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices",
|
||||
[OperatorSpecEditMode("delete", {}, "VERT", {3})]),
|
||||
MeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces",
|
||||
[OperatorSpecEditMode("delete", {}, "FACE", {0})]),
|
||||
MeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges",
|
||||
[OperatorSpecEditMode("delete", {}, "EDGE", {0, 1, 2, 3})]),
|
||||
|
||||
# delete edge loop
|
||||
["VERT", MONKEY_LOOP_VERT, "testMokneyDeleteEdgeLoopVertices", "expectedMonkeyDeleteEdgeLoopVertices",
|
||||
"delete_edgeloop", {}],
|
||||
["EDGE", MONKEY_LOOP_EDGE, "testMokneyDeleteEdgeLoopEdges", "expectedMonkeyDeleteEdgeLoopEdges",
|
||||
"delete_edgeloop", {}],
|
||||
MeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices",
|
||||
"expectedMonkeyDeleteEdgeLoopVertices",
|
||||
[OperatorSpecEditMode("delete_edgeloop", {}, "VERT", MONKEY_LOOP_VERT)]),
|
||||
|
||||
MeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges",
|
||||
"expectedMonkeyDeleteEdgeLoopEdges",
|
||||
[OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE)]),
|
||||
|
||||
### 9
|
||||
# delete loose
|
||||
["VERT", {i for i in range(12)}, "testCubeDeleteLooseVertices", "expectedCubeDeleteLooseVertices",
|
||||
"delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}],
|
||||
["EDGE", {i for i in range(14)}, "testCubeDeleteLooseEdges", "expectedCubeDeleteLooseEdges",
|
||||
"delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}],
|
||||
["FACE", {i for i in range(7)}, "testCubeDeleteLooseFaces", "expectedCubeDeleteLooseFaces",
|
||||
"delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}],
|
||||
MeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices",
|
||||
"expectedCubeDeleteLooseVertices",
|
||||
[OperatorSpecEditMode("delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False},
|
||||
"VERT",
|
||||
{i for i in range(12)})]),
|
||||
MeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges",
|
||||
"expectedCubeDeleteLooseEdges",
|
||||
[OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False},
|
||||
"EDGE",
|
||||
{i for i in range(14)})]),
|
||||
MeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces",
|
||||
"expectedCubeDeleteLooseFaces",
|
||||
[OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True},
|
||||
"FACE",
|
||||
{i for i in range(7)})]),
|
||||
|
||||
# dissolve degenerate
|
||||
["VERT", {i for i in range(8)}, "testCubeDissolveDegenerate", "expectedCubeDissolveDegenerate",
|
||||
"dissolve_degenerate", {}],
|
||||
MeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate",
|
||||
"expectedCubeDissolveDegenerate",
|
||||
[OperatorSpecEditMode("dissolve_degenerate", {}, "VERT", {i for i in range(8)})]),
|
||||
|
||||
### 13
|
||||
# dissolve edges
|
||||
["EDGE", {0, 5, 6, 9}, "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
|
||||
"dissolve_edges", {}],
|
||||
MeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
|
||||
[OperatorSpecEditMode("dissolve_edges", {}, "EDGE", {0, 5, 6, 9})]),
|
||||
|
||||
# dissolve faces
|
||||
["VERT", {5, 34, 47, 49, 83, 91, 95}, "testCubeDissolveFaces", "expectedCubeDissolveFaces", "dissolve_faces",
|
||||
{}],
|
||||
MeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces",
|
||||
[OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})]),
|
||||
|
||||
### 15
|
||||
# dissolve verts
|
||||
["VERT", {16, 20, 22, 23, 25}, "testCubeDissolveVerts", "expectedCubeDissolveVerts", "dissolve_verts", {}],
|
||||
MeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts",
|
||||
[OperatorSpecEditMode("dissolve_verts", {}, "VERT", {16, 20, 22, 23, 25})]),
|
||||
|
||||
# duplicate
|
||||
["VERT", {i for i in range(33)} - {23}, "testConeDuplicateVertices", "expectedConeDuplicateVertices",
|
||||
"duplicate", {}],
|
||||
["VERT", {23}, "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", "duplicate", {}],
|
||||
["FACE", {6, 9}, "testConeDuplicateFaces", "expectedConeDuplicateFaces", "duplicate", {}],
|
||||
["EDGE", {i for i in range(64)}, "testConeDuplicateEdges", "expectedConeDuplicateEdges", "duplicate", {}],
|
||||
MeshTest("ConeDuplicateVertices", "testConeDuplicateVertices",
|
||||
"expectedConeDuplicateVertices",
|
||||
[OperatorSpecEditMode("duplicate", {}, "VERT", {i for i in range(33)} - {23})]),
|
||||
|
||||
MeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex",
|
||||
[OperatorSpecEditMode("duplicate", {}, "VERT", {23})]),
|
||||
MeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces",
|
||||
[OperatorSpecEditMode("duplicate", {}, "FACE", {6, 9})]),
|
||||
MeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges",
|
||||
[OperatorSpecEditMode("duplicate", {}, "EDGE", {i for i in range(64)})]),
|
||||
|
||||
### 20
|
||||
# edge collapse
|
||||
["EDGE", {1, 9, 4}, "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", "edge_collapse", {}],
|
||||
MeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse",
|
||||
[OperatorSpecEditMode("edge_collapse", {}, "EDGE", {1, 9, 4})]),
|
||||
|
||||
# edge face add
|
||||
["VERT", {1, 3, 4, 5, 7}, "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", "edge_face_add", {}],
|
||||
["VERT", {4, 5}, "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", "edge_face_add", {}],
|
||||
MeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace",
|
||||
[OperatorSpecEditMode("edge_face_add", {}, "VERT", {1, 3, 4, 5, 7})]),
|
||||
MeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge",
|
||||
[OperatorSpecEditMode("edge_face_add", {}, "VERT", {4, 5})]),
|
||||
|
||||
# edge rotate
|
||||
["EDGE", {1}, "testCubeEdgeRotate", "expectedCubeEdgeRotate", "edge_rotate", {}],
|
||||
MeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate",
|
||||
[OperatorSpecEditMode("edge_rotate", {}, "EDGE", {1})]),
|
||||
|
||||
# edge split
|
||||
["EDGE", {2, 5, 8, 11, 14, 17, 20, 23}, "testCubeEdgeSplit", "expectedCubeEdgeSplit", "edge_split", {}],
|
||||
MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
|
||||
[OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]),
|
||||
|
||||
### 25
|
||||
# face make planar
|
||||
["FACE", {i for i in range(500)}, "testMonkeyFaceMakePlanar", "expectedMonkeyFaceMakePlanar",
|
||||
"face_make_planar", {}],
|
||||
MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar",
|
||||
"expectedMonkeyFaceMakePlanar",
|
||||
[OperatorSpecEditMode("face_make_planar", {}, "FACE", {i for i in range(500)})]),
|
||||
|
||||
# face split by edges
|
||||
["VERT", {i for i in range(6)}, "testPlaneFaceSplitByEdges", "expectedPlaneFaceSplitByEdges",
|
||||
"face_split_by_edges", {}],
|
||||
MeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges",
|
||||
"expectedPlaneFaceSplitByEdges",
|
||||
[OperatorSpecEditMode("face_split_by_edges", {}, "VERT", {i for i in range(6)})]),
|
||||
|
||||
# fill
|
||||
["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFill", "expectedIcosphereFill",
|
||||
"fill", {}],
|
||||
["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFillUseBeautyFalse",
|
||||
"expectedIcosphereFillUseBeautyFalse", "fill", {"use_beauty": False}],
|
||||
MeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill",
|
||||
[OperatorSpecEditMode("fill", {}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
|
||||
MeshTest("IcosphereFillUseBeautyFalse",
|
||||
"testIcosphereFillUseBeautyFalse", "expectedIcosphereFillUseBeautyFalse",
|
||||
[OperatorSpecEditMode("fill", {"use_beauty": False}, "EDGE",
|
||||
{20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
|
||||
|
||||
# fill grid
|
||||
["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGrid", "expectedPlaneFillGrid",
|
||||
"fill_grid", {}],
|
||||
["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGridSimpleBlending",
|
||||
"expectedPlaneFillGridSimpleBlending", "fill_grid", {"use_interp_simple": True}],
|
||||
MeshTest("PlaneFillGrid", "testPlaneFillGrid",
|
||||
"expectedPlaneFillGrid",
|
||||
[OperatorSpecEditMode("fill_grid", {}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
|
||||
|
||||
MeshTest("PlaneFillGridSimpleBlending",
|
||||
"testPlaneFillGridSimpleBlending",
|
||||
"expectedPlaneFillGridSimpleBlending",
|
||||
[OperatorSpecEditMode("fill_grid", {"use_interp_simple": True}, "EDGE",
|
||||
{1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
|
||||
|
||||
### 31
|
||||
# fill holes
|
||||
["VERT", {i for i in range(481)}, "testSphereFillHoles", "expectedSphereFillHoles", "fill_holes", {"sides": 9}],
|
||||
MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
|
||||
[OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]),
|
||||
|
||||
# inset faces
|
||||
["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInset",
|
||||
"expectedCubeInset", "inset", {"thickness": 0.2}],
|
||||
["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95},
|
||||
"testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse",
|
||||
"inset", {"thickness": 0.2, "use_even_offset": False}],
|
||||
["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInsetDepth",
|
||||
"expectedCubeInsetDepth", "inset", {"thickness": 0.2, "depth": 0.2}],
|
||||
["FACE", {35, 36, 37, 45, 46, 47, 55, 56, 57}, "testGridInsetRelativeOffset", "expectedGridInsetRelativeOffset",
|
||||
"inset", {"thickness": 0.4, "use_relative_offset": True}],
|
||||
MeshTest("CubeInset",
|
||||
"testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT",
|
||||
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50,
|
||||
52,
|
||||
59, 61, 62, 65, 83, 91, 95})]),
|
||||
|
||||
MeshTest("CubeInsetEvenOffsetFalse",
|
||||
"testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse",
|
||||
[OperatorSpecEditMode("inset", {"thickness": 0.2, "use_even_offset": False}, "VERT",
|
||||
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]),
|
||||
MeshTest("CubeInsetDepth",
|
||||
"testCubeInsetDepth",
|
||||
"expectedCubeInsetDepth", [OperatorSpecEditMode("inset", {"thickness": 0.2, "depth": 0.2}, "VERT",
|
||||
{5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61,
|
||||
62,
|
||||
65, 83, 91, 95})]),
|
||||
MeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset",
|
||||
"expectedGridInsetRelativeOffset",
|
||||
[OperatorSpecEditMode("inset", {"thickness": 0.4,
|
||||
"use_relative_offset": True}, "FACE",
|
||||
{35, 36, 37, 45, 46, 47, 55, 56, 57})]),
|
||||
]
|
||||
|
||||
operators_test = OperatorTest(tests)
|
||||
operators_test = RunTest(tests)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
operators_test.do_compare = True
|
||||
operators_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
operators_test.apply_modifiers = False
|
||||
index = int(command[i + 1])
|
||||
operators_test.run_test(index)
|
||||
operators_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
operators_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -24,26 +24,42 @@ import sys
|
|||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import ModifierTest, PhysicsSpec
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
["testCloth", "expectedCloth",
|
||||
[PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]],
|
||||
|
||||
MeshTest("ClothSimple", "testClothPlane", "expectedClothPlane",
|
||||
[ModifierSpec('Cloth', 'CLOTH', {'settings': {'quality': 5}}, 15)], threshold=1e-3),
|
||||
|
||||
# Not reproducible
|
||||
# MeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure",
|
||||
# [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_pressure': True,
|
||||
# 'uniform_pressure_force': 1}}, 16)]),
|
||||
|
||||
# Not reproducible
|
||||
# MeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision",
|
||||
# [ModifierSpec('Cloth', 'CLOTH', {'collision_settings': {'use_self_collision': True}}, 67)]),
|
||||
|
||||
MeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring",
|
||||
[ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_internal_springs': True}}, 10)], threshold=1e-3),
|
||||
|
||||
]
|
||||
cloth_test = ModifierTest(test, threshold=1e-3)
|
||||
cloth_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
cloth_test.apply_modifiers = True
|
||||
cloth_test.do_compare = True
|
||||
cloth_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
cloth_test.apply_modifiers = False
|
||||
index = int(command[i + 1])
|
||||
cloth_test.run_test(index)
|
||||
cloth_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
cloth_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
|
||||
MeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane",
|
||||
[ModifierSpec('dynamic_paint', 'DYNAMIC_PAINT',
|
||||
{'ui_type': 'CANVAS',
|
||||
'canvas_settings': {'canvas_surfaces': {'surface_type': 'WAVE', 'frame_end': 15}}},
|
||||
15)]),
|
||||
|
||||
]
|
||||
dynamic_paint_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
dynamic_paint_test.apply_modifiers = True
|
||||
dynamic_paint_test.do_compare = True
|
||||
dynamic_paint_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
dynamic_paint_test.apply_modifiers = False
|
||||
dynamic_paint_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
dynamic_paint_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,54 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
# World coordinates of test and expected object should be same.
|
||||
MeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean",
|
||||
[ModifierSpec('Ocean', 'OCEAN', {})]),
|
||||
]
|
||||
ocean_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
ocean_test.apply_modifiers = True
|
||||
ocean_test.do_compare = True
|
||||
ocean_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
ocean_test.apply_modifiers = False
|
||||
ocean_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
ocean_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,56 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
|
||||
MeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance",
|
||||
[ModifierSpec('ParticleInstance', 'PARTICLE_INSTANCE', {'object': bpy.data.objects['Cube']})],
|
||||
threshold=1e-3),
|
||||
|
||||
]
|
||||
particle_instance_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
particle_instance_test.apply_modifiers = True
|
||||
particle_instance_test.do_compare = True
|
||||
particle_instance_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
particle_instance_test.apply_modifiers = False
|
||||
particle_instance_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
particle_instance_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,55 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import RunTest, ParticleSystemSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
MeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem",
|
||||
[ParticleSystemSpec('Particles', 'PARTICLE_SYSTEM', {'render_type': "OBJECT",
|
||||
'instance_object': bpy.data.objects['Cube']}, 20)], threshold=1e-3),
|
||||
|
||||
]
|
||||
particle_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
particle_test.apply_modifiers = True
|
||||
particle_test.do_compare = True
|
||||
particle_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
particle_test.apply_modifiers = False
|
||||
particle_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
particle_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -24,26 +24,31 @@ import sys
|
|||
import bpy
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from modules.mesh_test import ModifierTest, PhysicsSpec
|
||||
from modules.mesh_test import RunTest, ModifierSpec, MeshTest
|
||||
|
||||
|
||||
def main():
|
||||
test = [
|
||||
["testSoftBody", "expectedSoftBody",
|
||||
[PhysicsSpec('Softbody', 'SOFT_BODY', {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}, 45)]],
|
||||
|
||||
MeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody",
|
||||
[ModifierSpec('Softbody', 'SOFT_BODY',
|
||||
{'settings': {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}},
|
||||
45)]),
|
||||
]
|
||||
softBody_test = ModifierTest(test)
|
||||
soft_body_test = RunTest(test)
|
||||
|
||||
command = list(sys.argv)
|
||||
for i, cmd in enumerate(command):
|
||||
if cmd == "--run-all-tests":
|
||||
softBody_test.apply_modifiers = True
|
||||
softBody_test.run_all_tests()
|
||||
soft_body_test.apply_modifiers = True
|
||||
soft_body_test.do_compare = True
|
||||
soft_body_test.run_all_tests()
|
||||
break
|
||||
elif cmd == "--run-test":
|
||||
softBody_test.apply_modifiers = False
|
||||
index = int(command[i + 1])
|
||||
softBody_test.run_test(index)
|
||||
soft_body_test.apply_modifiers = False
|
||||
soft_body_test.do_compare = False
|
||||
name = command[i + 1]
|
||||
soft_body_test.run_test(name)
|
||||
break
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue