友图自动排料引擎 V1.0 开发指南

友图自动排料引擎是一款能比肩世界顶级排料引擎的国产软件,适用于CAD软件商二次开发。文章介绍了引擎的基本数据结构、调用流程、错误处理,以及提供的C++接口。引擎支持多种样片变换和材料排布,旨在提高材料利用率。

完整版本 : 

https://download.youkuaiyun.com/download/nestingChina/12156799

价格 

永久加密狗 15000元 两年内免费升级; 推广期八折; 可申请一个月试用RMB 200 + 加密狗100(试用期后不满意可以加密狗换回加密狗硬件费用) 

联系  18917360310 (官网建设中, 2020年4月后可访问 www.yotutech.com获取更多信息)

Benchmark

CPU: AMD 2700

OS : Windows 10 64bit

NestFab(Ultra) 八线程     YotuNester(极致版) 四线程

 

 

Pieces

NestFab

YotuNester

 

R0

R0/R180

R0

R0/R180

Shape0

43

67.63%

74.12%

68.79%

76.73%

swim

48

74.32%

78.18%

74.09%

78.19%

shirt

99

86.01%

88.63%

87.79%

89.62%

trouser

64

86.12%

91.51%

87.46%

92.28%

Average

 

78.52%

83.11%

79.53

84.205%

Improved

 

 

 

+1.01%

+1.095%

 

  附 Nestfab 参考价格

Nestfab是英国顶级排料软件,利用率与shapeshifter/autonester/Gemini在同一级别

对应人民币  永久 约 34879元;  年付费   约 8719元/年

 

 

 -----------------               正文        -----------------------

 

 

友图自动排料引擎 V1.0

YotuNester Engine V1.0

开发指南

 

 

 

 

上海友图科技有限公司2020.01发布

 


免责声明

我们力求使本文档准确、完善,但无法保证其绝对完美。如果我们发现了不清晰、疏漏甚至错误,我们将尽力在产品的后续版本中将其更正。对于因为本文档内容的不准确或疏漏造成的任何直接或间接损害或业务损失,上海友图科技有限公司不负责任。

 

 

 

 

2020年1月

 

版权所有 © 2020 上海友图科技有限公司 保留所有权利

开发套件详单

头文件  yotu_global.h yotu.h

链接库  yotu.dll  默认使用微软VS编译器编译(可选 Mingw/Gcc版本)

        使用VS编译器将采用win32线程,

使用Mingw/Gcc编译器将采用pthread线程

加密狗

默认工作平台 windows7/10  (Linux可选)

本文所用术语解释

样片  待排放的形状,衣片或零件统称为样片

材料  样片放入其中的容器,对应于服装行业的面料,钣金行业的板材,当前版本材料形状为矩形

材料高度 对应于服装行业的面料宽度或钣金行业的材料宽度

材料长度 可以指定材料长度为固定值,不指定时材料长度为无限长

样片间距  排放入材料后样片之间的最小间距

材料间距  排放入材料和样片与材料边缘的距离

 

自动排料引擎简介

排料是指以节省材料为主要目标,把若干样片不重叠地放置进材料的平面空间。评估自动排料引擎最直接的指标就是材料利用率(以下简称利用率)。

自动排料引擎通常是指自动排料的核心算法,封装成可调用库形式,供CAD软件商二次开发使用.

目前世界上最顶级的自动排料引擎分别有AutoNester,Gemini,ShapeShifter,NestFab. 中国本土的自动排料引擎在商业应用上尚未出现或距离以上几家指标相差甚远。上海友图科技有限公司开发的友图自动排料引擎(YotuNester Engine)是唯一能在利用率指标上比肩世界顶级自动排料引擎的厂商。并在小规模排料上对比目前顶级排料引擎有较明显优势。

友图自动排料引擎主创人员拥有十六年几何图形算法以及CAD/EDA行业开发经验,经过近十年的技术积累与探索,友图自动排料引擎于2020年1月正式发布。

YotuNester采用C++编程语言实现,为方便不同语言调用,接口采用C风格。

目录

第一部分 基本数据结构

第二部分 调用流程

  1. 数据准备
  2. 启动搜索
  3. 获取结果
  4. 终止运行

附录 Benchmark;接口头文件清单

第一部分  基本数据结构

Point2F  点结构,记录浮点数2D坐标

struct Point2F

{

  double X;

  double Y;

};

数据精度: 小数点后3位有效.

例如

Point2F (123.4567, 987.65432) 会被截断为  Point2F (123.456, 987.654)。

数据范围: -640000.000 ~ 640000.000

在数据范围允许的情况下,可以通过放大源数据以提高数据精度。

例如 上述点可以乘以 100 (需要对所有坐标做相同转换)成为:

Point2F(12345.67, 98765.432)

然后输入引擎计算,得到的结果除以100,将会是没有误差(重叠)的结果.

如果源数据小数点后没有超过3位,就不需要考虑精度问题.

 

Polygon2F 多边形结构,记录多边形的所有点,首尾点无需重合

struct Polygon2F

{

  Point2Fpts;

  unsigned size;

};

多边形必须为简单多边形,简单多边形指多边形的任意两条非相邻边不相交。

 

UserShape  样片数据及排料信息

struct UserShape

{

  unsigned     idType;

  unsigned       tran;

  unsigned      flipX;

  unsigned      flipY;

  unsigned   quantity;

  Polygon2F      ring;

};

 

每种样片都需要一个唯一标识的idType,建议从0开始递增。tran标识允许的变换角度。允许变换的角度有四类,分别是:

YT_TR_KIND_0

YT_TR_KIND_180

YT_TR_KIND_90

YT_TR_KIND_ANY

当前版本 YT_TR_KIND_ANY 不可用,如想获得接近任意角度的效果,请使用

YT_TR_KIND_90.

前三类变换角度下可以使用的旋转角度分别为

YT_TR_KIND_0   (YT_R0)

YT_TR_KIND_180  (YT_R0, YT_R180)

YT_TR_KIND_90   (YT_R0, YT_R90, YT_180, YT_270)

flipX标识是否可以沿X轴翻转,1为允许,0为不允许

flipY标识是否可以沿Y轴翻转,1为允许,0为不允许

 

以下表格为不同变换角度和翻转条件下的具体变换:

(请注意区分变换角度YT_TR_KIND_XXX与具体变换YT_Rxxx/YT_Fxxx)

 

不同变换角度和翻转条件下的具体变换

flipX == 0

flipY == 0

flipX == 1

flipY == 0

flipX == 0

flipY == 1

flipX == 1

flipY == 1

YT_TR_KIND_0

YT_R0

YT_R0

YT_FX

YT_R0

YT_FY

YT_R0

YT_FX

YT_FY

YT_180

YT_TR_KIND_180

YT_R0

YT_R180

YT_R0

YT_FX

YT_FY

YT_R180

YT_R0

YT_FX

YT_FY

YT_R180

YT_R0

YT_FX

YT_FY

YT_R180

YT_TR_KIND_90

YT_R0

YT_R90

YT_R180

YT_R270

YT_R0

YT_R90

YT_R180

YT_R270

YT_FX

YT_FY

YT_R90_FX

YT_R90_FY

YT_R0

YT_R90

YT_R180

YT_R270

YT_FX

YT_FY

YT_R90_FX

YT_R90_FY

YT_R0

YT_R90

YT_R180

YT_R270

YT_FX

YT_FY

YT_R90_FX

YT_R90_FY

 

quantity 标识该类样片的数量。

相同形状的样片请按idType归类。样片是否相同需要参考可变换角度。

例如形状A与B不相同,在A和B都使用YT_TR_KIND_180变换角度时,如果A旋转180度和B形状相同,那么A和B可视为同类样片。其余角度类推。

ring标识样片多边形数据。

 

UserSources 存放所有的源样片信息

struct UserSources

{

  UserShape* shapes;

  unsigned     size;

};

shapes所有的源样片种类集合,size为样片种类的个数。

*** 需要注意的是:源数据的内存分配 shapes以及Polygon2F中的pts都需要调用者自行管理,在调用前分配,运行结束后释放。

 

UserPlacement保存每个样片的排样结果

struct UserPlacement

{

  unsigned  idType;

  unsigned   idGrp;

  unsigned  idTran;

  unsigned idSheet;

  Point2F      pos;

};

排样结果不会包含任何源样片形状数据,只包含该样片放置的位置和对应的具体变换。

idType为源样片类型的标识,idGrp为源样片的序号即源样片的第几个实例。例如源样片3:idType的quantity为5,那么idGrp为0~4.  如下图所示

idType 为2的形状共8个,这八个分别对应下图标号 2-0, 2-1, 2-2, … , 2-7

 

idTran 样片的具体变换, 取YT_R0, … YT_R90_FX,…

idSheet标识所在的材料序号中,即在第几块材料中

pos 标识样片(外包矩形)的中心所在的位置

 

UserSolution保存完整排料结果

struct UserSolution

{

  unsigned        shapeSize;

  unsigned         sheetSize;

  UserPlacement*  placements;

  Point2F            lbOfMat;

  double         widthOfMat;

  double        lengthOfMat;

  double     lastLengthOfMat;

  double        utilization;

};

shapeSize        所有的样片个数

sheetSize        存放所需材料的个数,在不固定材料长度的情况始终为1

placements      存放所有的样片排料结果

lbOfMat         材料的左下角坐标

widthOfMat      材料的宽度(高度)

lengthOfMat     材料的长度,如果最终只需要一块材料(即sheetSize为1),请使用lastLengthOfMat

lastLengthOfMat  对于固定材料长度的情况,有可能最终排料结果使用多块材料,它保存最后一块材料的长度

utilization        材料(总)利用率.对于多块材料的情况下单块材料的利用率,请使用材料尺寸自行计算

由于每个样片的排样结果(UserPlacement)中包含idSheet信息,所以可以每

个样片按照idSheet分类,以便计算和显示每个材料的具体排料结果信息。

 

 

 

 

 

 

 

 

 

 

 

第二部分  调用流程

  1. 数据准备

以shape0为例

UserSources usrc;

unsigned id_counts = 0;

usrc.size = 4;

usrc.shapes = new UserShape[usrc.size];

usrc.shapes[0].idType = id_counts++;

usrc.shapes[0].tran = YT_TR_KIND_180;

usrc.shapes[0].flipX = 0;

usrc.shapes[0].flipY = 0;

usrc.shapes[0].quantity = 7;

usrc.shapes[0].ring.size = 4;

usrc.shapes[0].ring.pts = new Point2F[4];

usrc.shapes[0].ring.pts[0].X = …

usrc.shapes[0].ring.pts[0].Y = …

usrc.shapes[1].idType = id_counts++;

usrc.shapes[1].ring.pts = new Point2F[12];

调用yt_init初始化

yt_init声明解释如下

extern "C" LIBYOTU_EXPORT unsigned

yt_init(const UserSources* source,

double height,   //材料宽度(高度)

double length,   //材料长度(不限长则传入-1)

double space_piece, //样片间距

double space_mat);  //材料间距

调用代码:

unsigned rt = yt_init(&usrc, 40, -1, 0, 0);

返回值错误判断与处理

if (YT_NONE_ERROR != rt)

       {

              if(YT_IN_PROCESS_ERROR == rt)

                     cout << "In Process!" << endl; //已经在进程中,只允许一个进程

              else if(YT_WIDTH_ERROR == rt)

                     cout << "Width Error!" << endl;//指定的材料宽度小于样片的宽度(高度)

              else if(YT_DOG_ERROR == rt)

                     cout << "Dog Error!" << endl; //没有检测到加密狗或已过有效期

              else if(YT_IN_PROGRESS_ERROR == rt)

                     cout << "In Progress!" << endl;//当前正在运行

              else

                     cout << "Unknown Error!" << endl;//未知错误

              return false;

}

如果调用yt_init正常返回之后未调用yt_close关闭引擎(无论是否运行yt_run),再次调用yt_init将会返回YT_IN_PROGRESS_ERROR. 需调用yt_close关闭引擎(提前终止运行则需要先调用yt_stop,再使用yt_close关闭引擎),才能再次调用yt_init初始化.

当前版本只允许一个进程运行,同时运行多个进程将返回YT_IN_PROCESS_ERROR.

yt_init是阻塞调用,必须等到yt_init返回才能调用其它接口函数.

yt_init耗费时间与样片种类和样片形状的复杂度直接相关,通常情况下,1百(不是100个)样片,支持180度旋转,样片平均顶点数约100时,函数耗时在1~2秒(4核).

这种情况下如果支持90度旋转,yt_init耗时将会呈指数增长,所以不建议同时使用数百种样片、使用90度变换角度,并且顶点数最好控制在100以内。但是引擎并不会限制样片顶点数和样片种类个数

大量实验表明(不仅限于YotuNester,对于其它的排料引擎也是如此),在有限时间(几个小时)内,把几百个样片按100-150个分组分别运行,得到的结果比几百个样片同时计算的结果更好。 理论上几百个样片同时排放会比分组排放利用率更好,但是代价是需要耗费不成比例的时间。

  1. 启动搜索

yt_run声明与解释

extern "C" LIBYOTU_EXPORT void

yt_run(unsigned threads_number,  //启动线程数量

unsigned run_seconds,   //运行时间

unsigned random_base = 0);

由于绝大多数优化算法都会使用随机数,本引擎也不例外. 每个线程都会使用不同的随机数进行计算,正常情况下每次计算的结果都是可以复现的. 大部分情况下并不需要参数random_base,

调用示例:

    yt_run(4, 600); //启动4线程,运行时间为600秒

如果对结果不满意,可以尝试使用random_base更换随机数种子.

random_base 为0的情况下每个线程的随机数种子分别是 0, 1, … threads_number -1.

设定了random_base后每个线程的随机数种子为:

random_base, random_base+1, … random_base + threads_number – 1

更换随机数种子并不能保证一定带来利用率提升,只是提供提升利用率的可能.

正常情况下,传入相同的random_base,运行的结果也是可以复现的,所以如果使用该参数,请在排料结果中记录该值,以便可以复现。

当前版本下,各线程的计算是相互独立的,也就是说多线程并不会让程序运行更快,只是因为不同线程的利用率不同,获得更高利用率的可能性更高。

该函数是非阻塞式,调用之后立即返回,耗时可以忽略.

为提高性能,后续版本中多线程独立运算的方式可能会进行改进.

 

  1. 获取结果

extern "C" LIBYOTU_EXPORT bool

yt_query_solution(UserSolution* sln);

该函数需要传入UserSolution的实例指针,在调用该函数前需要按样片个数预先分配好空间,并设置UserSolution.utilization为0

以shape0为例

Shape0有四种样片

第一种  7个; 第二种  12个; 第三种  9 个; 第四种  15个

总计    43个

因此需要为UserSolution.placements指针分配43个UserPlacement结构的大小空间

调用示例:

UserSolution usln;

usln.utilization = 0;

usln.placements = new UserPlacement[43];

bool result = yt_query_solution(&usln);

yt_query_solution函数会通过UserSolution.utilization和引擎计算的利用率对比,如果引擎计算的利用率更高,则会把最新的结果写入usln,否则不会修改usln.  返回值为true时说明结果更新了。保持usln在其它的情况下只读是最好的选择。

usln中保存了每个样片的类型信息和排放位置以及具体变换,调用方可以根据这些信息生成用户可理解的文件或者图形界面信息。

建议定时调用yt_query_solution以查看是否结果更新,更新频率不宜大于1次/秒,频繁查询结果对性能影响微乎其微,但是却并非必要。

 

  1. 终止运行

无论程序运行是由于时间限制自然结束还是提前终止都需要调用yt_close来清理资源

调用yt_close只是清理资源,并不会提前终止引擎。

extern "C" LIBYOTU_EXPORT int

yt_close();

   yt_close采用安全设计,无论何时调用都不会有负面效应

   当返回值为0时,表示引擎运行已经终止并且资源已经清理,如果返回1则表示当前正在运行.

   需要提前终止运行需要调用yt_stop

   extern "C" LIBYOTU_EXPORT void

yt_stop();

   该函数会发出终止信号,调用yt_stop之后需调用yt_close以确认引擎是否结束并且资源已经清理。一般情况下引擎在收到终止信号后会很快终止运行,但是当样片种类和数量很多时,引擎终止可能会需要数秒时间,所以安全的确认引擎终止的方式是一直调用yt_close直到它返回0为止(这也是运行新的排料计算的前提)。

示例:

yt_stop(); //按照运行时间限制自然结束的不必调用yt_stop

while(yt_close())

{

   //sleep or do anything else

cout << “engine closed”<< endl;

    引擎非正常结束除了调用yt_stop主动终止外,还有一种可能是加密狗在运行过程中被拔出,引擎在启动和运行时会随机检测加密狗是否存在和有效,一旦检测到加密狗不存在或者无效会立即终止。 所以最好在查询结果前调用yt_close确认引擎是否结束。

 

 

 

 

 

附录

 

测试数据使用YotuNester V1.0版本

 

Benchmark

 

 

CPU: AMD 2700

OS : Windows 10 64bit

 

NestFab 八线程     YotuNester 四线程

 

 

Pieces

NestFab

YotuNester

 

R0

R0/R180

R0

R0/R180

Shape0

43

67.63%

74.12%

68.79%

76.73%

swim

48

74.32%

78.18%

74.09%

78.19%

shirt

99

86.01%

88.63%

87.79%

89.62%

trouser

64

86.12%

91.51%

87.46%

92.28%

Average

 

78.52%

83.11%

79.53

84.205%

Improved

 

 

 

+1.01%

+1.095%

 

头文件清单

 

yotu_global.h

 

/****************************************************************************

**

** Copyright (C) 2020 The YotuTech Company Ltd.

** Contact: 18917360310

**

** yotu_global.h:  basic data structure of yotu-nester.

** Date:    2020-01-31

** Author:  Youlin

** Version: 1.0

****************************************************************************/

 

#ifndef LIBYOTU_GLOBAL_H

#define LIBYOTU_GLOBAL_H

 

// Return Code

const unsigned YT_NONE_ERROR                = 0;

const unsigned YT_DOG_ERROR                 = 1;

const unsigned YT_WIDTH_ERROR               = 2;

const unsigned YT_IN_PROCESS_ERROR          = 4;

const unsigned YT_IN_PROGRESS_ERROR         = 5;

const unsigned YT_UNKNOW_ERROR              = 9;

 

 

// Transform Detail

const unsigned YT_R0      = 0;

const unsigned YT_R180    = 1;

const unsigned YT_FX      = 2;

const unsigned YT_FY      = 3;

const unsigned YT_R90     = 4;

const unsigned YT_R270    = 5;

const unsigned YT_R90_FX  = 6;

const unsigned YT_R90_FY  = 7;

 

// Transform Setting

const unsigned YT_TR_KIND_0   = 0;

const unsigned YT_TR_KIND_180 = 1;

const unsigned YT_TR_KIND_90  = 2;

const unsigned YT_TR_KIND_ANY = 3;

 

struct Point2F

{

  double X;

  double Y;

};

 

struct Polygon2F

{

  Point2Fpts;

  unsigned size;

};

 

struct Line2F

{

  Point2F start;

  Point2F end;

};

 

struct Box2F

{

  double left;

  double right;

  double bottom;

  double top;

};

 

struct UserShape

{

  unsigned     idType

  unsigned       tran;

  unsigned      flipX;

  unsigned      flipY;

  unsigned   quantity;

  Polygon2F      ring;

};

 

struct UserSources

{

  UserShape* shapes;

  unsigned     size;

};

 

struct UserPlacement

{

  unsigned  idType;

  unsigned   idGrp;

  unsigned  idTran;

  unsigned idSheet;

  Point2F      pos;

};

 

struct UserSolution

  unsigned        shapeSize;

  unsigned        sheetSize;

  UserPlacement* placements;

  Point2F           lbOfMat;

  double         widthOfMat;

  double        lengthOfMat;

  double    lastLengthOfMat;

  double        utilization;

};

 

#endif // LIBYOTU_GLOBAL_H

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

 

 

 

 

 

 

 

yotu.h

 

 

/****************************************************************************

**

** Copyright (C) 2020 The YotuTech Company Ltd.

** Contact: 18917360310

**

** yotu.h:  interface of yotu-nester.

** Date:    2020-01-31

** Author:  Youlin

** Version: 1.0

****************************************************************************/

 

#ifndef LIBYOTU_H

#define LIBYOTU_H

 

#include "yotu_global.h"

 

#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)

#  define Q_DECL_EXPORT __declspec(dllexport)

#  define Q_DECL_IMPORT __declspec(dllimport)

#else

#  define Q_DECL_EXPORT     __attribute__((visibility("default")))

#  define Q_DECL_IMPORT     __attribute__((visibility("default")))

#endif

 

#if defined(YOTU_LIBRARY)

#  define LIBYOTU_EXPORT Q_DECL_EXPORT

#else

#  define LIBYOTU_EXPORT Q_DECL_IMPORT

#endif

 

extern "C" LIBYOTU_EXPORT bool

yt_query_solution(UserSolution* sln);

 

extern "C" LIBYOTU_EXPORT unsigned

yt_init(const UserSources* source, double height, double length, double space_piece, double space_mat);

 

extern "C" LIBYOTU_EXPORT void

yt_run(unsigned threads_number, unsigned run_seconds, unsigned random_base = 0);

 

extern "C" LIBYOTU_EXPORT void

yt_stop();

 

extern "C" LIBYOTU_EXPORT int

yt_close();

 

#endif // LIBYOTU_H

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值