openscad螺栓数据生成

python/lib/python3.6/site-packages/viewscad/renderer.py

如果你使用的openscad版本过旧(ubuntu16.04 是openscad2015)

通过python代码viewscad调用的时候,可能会出现错误,需要新版本的openscad

你可以修改renderer.py

    def _try_detect_openscad_exec(self):
        self.openscad_exec = None
        platfm = platform.system()
        if platfm == 'Linux':
            self._try_executable('/usr/bin/openscad')
            if self.openscad_exec is None:
                #self._try_executable('/usr/local/bin/openscad')
                self._try_executable('~/下载/OpenSCAD-2021.01-x86_64.AppImage')
        elif platfm == 'Darwin':
            self._try_executable('/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD')
        elif platfm == 'Windows':
            self._try_executable(os.path.join(
                    os.environ.get('Programfiles(x86)','C:'),
                    'OpenSCAD\\openscad.exe'))

https://stackoverflow.com/questions/60562858/best-libraries-for-openscad

0)
https://openscad.org/libraries.html

1)
https://github.com/cznewt/openscad-model-library
https://openscad-model-library.readthedocs.io/en/latest/primitives.html#bolts-and-nuts

2)
https://github.com/revarbat/BOSL
https://github.com/revarbat/BOSL/wiki

3)
https://github.com/revarbat/BOSL2
https://github.com/revarbat/BOSL2/wiki

4)
https://github.com/Irev-Dev/Round-Anything
https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design/

5)
https://github.com/nophead/NopSCADlib
https://github.com/nophead/NopSCADlib/blob/master/readme.md

6)
https://github.com/boltsparts/BOLTS   推荐这个
https://www.bolts-library.org/en/index.html

7)
https://github.com/JustinSDK/dotSCAD

8)
https://github.com/openscad/MCAD
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/MCAD#Overview_of_MCAD_Library

9)
https://www.thingiverse.com/thing:3215997

10)
https://github.com/openscad/list-comprehension-demos

11)
https://github.com/davidson16807/relativity.scad

模型下载:

GitHub - rcolyer/threads-scad: OpenSCAD threading library

GitHub - GillesBouissac/agentscad: My utilities for OpenSCAD

GitHub - openscad/MCAD: OpenSCAD Parametric CAD Library (LGPL 2.1)

GitHub - JohK/nutsnbolts: A OpenSCAD library that allows for simple creation of nuts and bolts and respective nut catches and screw holes

https://www.bolts-library.org/en/index.html

https://github.com/boltsparts/BOLTS

https://www.bolts-library.org/en/parts/index.html

Metric bolts with OpenSCAD by Claymore - Thingiverse

Nuts and Bolts v1.95 OpenSCAD library by biomushroom - Thingiverse

OpenSCAD教程-初学者的5个简单步骤

OpenSCAD教程-初学者的5个简单步骤

OpenSCAD中使用include或use引入外部库

OpenSCAD中使用include或use引入外部库_weixin_34221112的博客-CSDN博客

hex_head2.scad

$fn=100;

module hex_head(diameter,height) {

  intersection() {

    union() {
      translate([0,0,0.801*height])
      cylinder(d1=diameter,d2=0.8*diameter,h=0.2*height);
      cylinder(d=diameter,h=0.801*height);
    }
    
    cylinder(d=diameter,h=height,$fn=6);
  }
}

module washer(outside_diameter,inside_diameter,height) {

  difference() {
    cylinder(d=outside_diameter,h=height);
    translate([0,0,-0.01])
    cylinder(d=inside_diameter,h=height+0.02);

  }
}

module steel_plate(side,thickness) {
  translate([-side/2,-side/2,0])
    cube([side,side,thickness]);
}


module screw(diameter,height) {
        cylinder(d=diameter, h=height);    
}


module hex_head2(tran_init,plate_side,plate_height,washer_out_diam,washer_in_diam,washer_height,bolt_height,bolt_diam,bolt_tran,bolt_rot,screw_diam,screw_height){
  union(){
    translate([0,0,tran_init])
        steel_plate(plate_side,plate_height);

    translate([0,0,tran_init+plate_height]) 
        washer(washer_out_diam, washer_in_diam, washer_height);

    translate([0,0,tran_init+plate_height+washer_height+bolt_tran])
        rotate([0,0,bolt_rot])
            hex_head(bolt_diam, bolt_height);

    translate([0,0,tran_init+plate_height+washer_height]) 
        screw(screw_diam, screw_height);    
  }
}


//平面距离坐标原点的高度
tran_init = 0;

//底平面参数
plate_side   = 70;
plate_height = 2;

//垫片参数
washer_out_diam = 50;
washer_in_diam = 0;
washer_height = 3;

//螺母参数
bolt_height = 22; //大小-高度
bolt_diam   = 35; //大小-宽度
bolt_tran   = 20; //上下-高度
bolt_rot    = 130; // degrees

//螺柱参数
screw_diam   = 16;
screw_height = 44;


hex_head2(tran_init,plate_side,plate_height,washer_out_diam,washer_in_diam,washer_height,bolt_height,bolt_diam,bolt_tran,bolt_rot,screw_diam,screw_height);

demo_bolt_002.py

# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site: 
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib

scad_parameter={
    #平面距离坐标原点的高度
    'tran_init': 0,
    #底平面参数
    'plate_side': 70,
    'plate_height': 2,
    #垫片参数
    'washer_out_diam': 50,
    'washer_in_diam': 0,
    'washer_height': 3,
    #螺母参数
    'bolt_height': 22, #大小-高度
    'bolt_diam': 35, #大小-宽度
    'bolt_tran': 15, #上下-高度
    'bolt_rot': 15, #角度
    #螺柱参数
    'screw_diam': 16,
    'screw_height': 44,
}


def mkdir_os(path):
    if not os.path.exists(path):
        os.makedirs(path)

def main(args):
    scad_path = pathlib.Path(args.input_scad)
    if not scad_path.is_file():
        print("input_scad error")
        return

    mkdir_os(args.output_stl_folder)
    mkdir_os(args.output_ply_folder)

    scad_F = import_scad(args.input_scad)

    index = 0
    count = scad_parameter['screw_height'] * (180/10)
    for nut_height in range(0, scad_parameter['screw_height'], 1):
        for nut_angle in range(0, 180, 10):
            index += 1
            print(index, "/", count)

            save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
            save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.ply".format(index, nut_height, nut_angle)
            stl_path = os.path.join(args.output_stl_folder, save_stl_name)
            ply_path = os.path.join(args.output_ply_folder, save_ply_name)
            scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
                                plate_side=scad_parameter['plate_side'],
                                plate_height=scad_parameter['plate_height'],
                                washer_out_diam=scad_parameter['washer_out_diam'],
                                washer_in_diam=scad_parameter['washer_in_diam'],
                                washer_height=scad_parameter['washer_height'],
                                bolt_height=scad_parameter['bolt_height'],
                                bolt_diam=scad_parameter['bolt_diam'],
                                bolt_tran=nut_height,
                                bolt_rot=nut_angle,
                                screw_diam=scad_parameter['screw_diam'],
                                screw_height=scad_parameter['screw_height'])
            v_rander = viewscad.Renderer()
            v_rander.render(scad_model, outfile=stl_path)

            #pymeshlab
            import pymeshlab as ml
            ms = ml.MeshSet()
            ms.load_new_mesh(stl_path)
            filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
            ms.save_current_mesh(filename_mesh_out)
            #这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
            #https://github.com/intel-isl/Open3D/issues/2479


            #open3d
            mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
            #mesh_bolt.paint_uniform_color([0, 0, 1])

            pcd_mesh_bolt = mesh_bolt.sample_points_poisson_disk(number_of_points=15000)
            #pcd_mesh_bolt.paint_uniform_color([1, 1, 0])
            #print('pcd_mesh_bolt:', pcd_mesh_bolt)

            pcd_mesh_bolt = pcd_mesh_bolt.voxel_down_sample(voxel_size=1)
            #print('pcd_mesh_bolt', pcd_mesh_bolt)

            o3d.io.write_point_cloud(ply_path, pcd_mesh_bolt)

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description="creat bolt")
    parser.add_argument('-if',
                        "--input_scad",
                        default='./hex_head2.scad',
                        help="set input scad file")
    parser.add_argument('-os',
                        "--output_stl_folder",
                        default='./result_stl/',
                        help="set output stl folder")
    parser.add_argument('-op',
                        "--output_ply_folder",
                        default='./result_ply/',
                        help="set output ply folder")
    args = parser.parse_args()

    if args.input_scad is None:
        parser.print_help()
        exit()

    main(args)


'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab

python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid                  0.2.0
solidpython            0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple


jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/

https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''

demo_bolt_003.py

带标签的数据生成:

# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site: 
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib
import copy
import numpy as np


HEADER = '''\
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z label
SIZE 4 4 4 4
TYPE F F F I
COUNT 1 1 1 1
WIDTH {}
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS {}
DATA ascii
'''
def write_pcd_fun1(points, save_pcd_path):
    n = len(points)
    lines = []
    for i in range(n):
        x, y, z, label = points[i]
        lines.append('{:.6f} {:.6f} {:.6f} {}'.format(x, y, z, label))
    with open(save_pcd_path, 'w') as f:
        f.write(HEADER.format(n, n))
        f.write('\n'.join(lines))
def write_pcd_fun2(points, save_pcd_path):
    with open(save_pcd_path, 'w') as f:
        f.write(HEADER.format(len(points), len(points)) + '\n')
        np.savetxt(f, points, delimiter=' ', fmt='%f %f %f %d')


#https://blog.csdn.net/weixin_48994268/article/details/115682269
#open3d连续读取pcd文件及实现点云视角转换
def save_view_point(pcd, filename):
    vis = o3d.visualization.Visualizer()
    vis.create_window(window_name='pcd', width=800, height=600)
    vis.add_geometry(pcd)
    vis.run()  # user changes the view and press "q" to terminate
    param = vis.get_view_control().convert_to_pinhole_camera_parameters()
    o3d.io.write_pinhole_camera_parameters(filename, param)
    vis.destroy_window()
def load_view_point(pcd, filename):
    vis = o3d.visualization.Visualizer()
    vis.create_window(window_name='pcd', width=800, height=600)
    ctr = vis.get_view_control()
    param = o3d.io.read_pinhole_camera_parameters(filename)
    vis.add_geometry(pcd)
    ctr.convert_from_pinhole_camera_parameters(param)
    vis.run()
    vis.destroy_window()


scad_parameter={
    #平面距离坐标原点的高度
    'tran_init': 0,
    #底平面参数
    'plate_side': 70,
    'plate_height': 1,
    #垫片参数
    'washer_out_diam': 50,
    'washer_in_diam': 0,
    'washer_height': 2,
    #螺母参数
    'bolt_height': 22, #大小-高度
    'bolt_diam': 35, #大小-宽度
    'bolt_tran': 15, #上下-高度
    'bolt_rot': 15, #角度
    #螺柱参数
    'screw_diam': 16,
    'screw_height': 44,
}


label_dict = {
    "other": 0, #washer
    "stud": 1,
    "plane": 2,
    "nut": 3,
}


def mkdir_os(path):
    if not os.path.exists(path):
        os.makedirs(path)


def main(args):
    scad_path = pathlib.Path(args.input_scad)
    if not scad_path.is_file():
        print("input_scad error")
        return

    mkdir_os(args.output_stl_folder)
    mkdir_os(args.output_ply_folder)

    scad_F = import_scad(args.input_scad)

    index = 0
    count = scad_parameter['screw_height'] * (180/10)
    for nut_height in range(0, scad_parameter['screw_height'], 1):
        for nut_angle in range(0, 180, 10):
            index += 1
            print(index, "/", count)

            save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
            save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.ply".format(index, nut_height, nut_angle)
            stl_path = os.path.join(args.output_stl_folder, save_stl_name)
            ply_path = os.path.join(args.output_ply_folder, save_ply_name)
            scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
                                plate_side=scad_parameter['plate_side'],
                                plate_height=scad_parameter['plate_height'],
                                washer_out_diam=scad_parameter['washer_out_diam'],
                                washer_in_diam=scad_parameter['washer_in_diam'],
                                washer_height=scad_parameter['washer_height'],
                                bolt_height=scad_parameter['bolt_height'],
                                bolt_diam=scad_parameter['bolt_diam'],
                                bolt_tran=nut_height,
                                bolt_rot=nut_angle,
                                screw_diam=scad_parameter['screw_diam'],
                                screw_height=scad_parameter['screw_height'])

            #分开生成形成label
            #垫圈+other
            washer = copy.deepcopy(scad_model)
            washer.params["plate_height"] = 0
            washer.params["bolt_height"] = 0
            washer.params["screw_height"] = 0
            #螺柱
            stud = copy.deepcopy(scad_model)
            stud.params["plate_height"] = 0
            stud.params["washer_height"] = 0
            stud.params["bolt_height"] = 0
            #底平面
            plane = copy.deepcopy(scad_model)
            plane.params["washer_height"] = 0
            plane.params["bolt_height"] = 0
            plane.params["screw_height"] = 0
            #螺母
            nut = copy.deepcopy(scad_model)
            nut.params["plate_height"] = 0
            nut.params["washer_height"] = 0
            nut.params["screw_height"] = 0

            model_list = [washer, stud, plane, nut]
            mesh_list = []
            color_list = [
                [1, 0, 0],
                [0, 1, 0],
                [0, 0, 1],
                [1, 1, 0]
            ]
            for ind, mod in enumerate(model_list):
                v_rander = viewscad.Renderer()
                v_rander.render(mod, outfile=stl_path) #这里已经保存了stl,但是是ascii format
                #pymeshlab
                import pymeshlab as ml
                ms = ml.MeshSet()
                ms.load_new_mesh(stl_path)
                filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
                ms.save_current_mesh(filename_mesh_out)
                #这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
                #https://github.com/intel-isl/Open3D/issues/2479
                #open3d
                mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
                mesh_bolt.paint_uniform_color(color_list[ind])
                mesh_list.append(mesh_bolt)


            model = mesh_list[0] + mesh_list[1] + mesh_list[2] + mesh_list[3]

            model_point = model.sample_points_poisson_disk(number_of_points=15000)
            print('model_point:', model_point)
            model_point = model_point.voxel_down_sample(voxel_size=1)
            print('model_point', model_point)


            #螺柱在螺母中的也会显示
            # o3d.visualization.draw_geometries([#model,
            #                                    model_point,
            #                                    #model_point.get_axis_aligned_bounding_box(),
            #                                    #model.get_axis_aligned_bounding_box(),
            #                                    #o3d.geometry.TriangleMesh.create_coordinate_frame(size=20)
            #                                    ])

            #虽然这种方法可以保存视角,但是无法与hidden_point_removal联合使用
            #save_view_point(model_point, "viewpoint.json")
            #load_view_point(model_point, "viewpoint.json")

            #如果想要螺柱在螺母中不显示,需要用到视角遮挡消除,但是视角计算的时候使用的是计算出来的视角
            '''
             |  hidden_point_removal(...)
             |      hidden_point_removal(self, camera_location, radius)
             |      
             |      Removes hidden points from a point cloud and returns a mesh of the remaining points. Based on Katz et al. 'Direct Visibility of Point Sets', 2007.
             |      
             |      Args:
             |          camera_location (numpy.ndarray[float64[3, 1]]): All points not visible from that location will be reomved
             |          radius (float): The radius of the sperical projection
             |      
             |      Returns:
             |          Tuple[open3d.geometry.TriangleMesh, List[int]]
            '''
            diameter = np.linalg.norm(np.asarray(model_point.get_max_bound()) - np.asarray(model_point.get_min_bound()))
            #camera = [0, 0, diameter]
            camera = [ diameter/2, diameter/2, diameter]
            #camera = [0, 0, 44]
            radius = diameter*100
            print("Get all points that are visible from given view point")
            _, pt_map = model_point.hidden_point_removal(camera, radius)
            #https://github.com/intel-isl/Open3D/issues/1860
            #open3d-0.9.0
            #select_point = model_point.select_by_index(pt_map)
            select_point = model_point.select_down_sample(pt_map)
            o3d.visualization.draw_geometries([select_point,
                                               o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
                                               o3d.geometry.TriangleMesh.create_coordinate_frame(size=20, origin=camera),
                                               select_point.get_axis_aligned_bounding_box(),
                                               ])


            #通过颜色确定label,保存为pcd
            label = model_point.colors
            np_label = np.asarray(label).astype(np.int)
            points = model_point.points
            np_points = np.asarray(points)

            ind_washer = np.intersect1d(np.where(np_label[:,0] == 1), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 0))
            ind_stud = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 1), np.where(np_label[:,2] == 0))
            ind_plane = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 1))
            ind_nut = np.intersect1d(np.where(np_label[:,0] == 1), np.where(np_label[:,1] == 1), np.where(np_label[:,2] == 0))

            np_washer = np_points[ind_washer][:, :3]
            np_stud = np_points[ind_stud][:, :3]
            np_plane = np_points[ind_plane][:, :3]
            np_nut = np_points[ind_nut][:, :3]

            t_lab = np.ones((np_washer.shape[0], 1), dtype=np.int8) * 0
            p_washer = np.hstack((np_washer, t_lab))
            t_lab = np.ones((np_stud.shape[0], 1), dtype=np.int8) * 1
            p_stud = np.hstack((np_stud, t_lab))
            t_lab = np.ones((np_plane.shape[0], 1), dtype=np.int8) * 2
            p_plane = np.hstack((np_plane, t_lab))
            t_lab = np.ones((np_nut.shape[0], 1), dtype=np.int8) * 3
            p_nut = np.hstack((np_nut, t_lab))
            write_pcd_fun1(np.vstack((p_washer, p_stud, p_plane, p_nut)), ply_path.replace('.ply', '.pcd'))


if __name__ == "__main__":

    parser = argparse.ArgumentParser(description="creat bolt")
    parser.add_argument('-if',
                        "--input_scad",
                        default='./hex_head2.scad',
                        help="set input scad file")
    parser.add_argument('-os',
                        "--output_stl_folder",
                        default='./result_stl/',
                        help="set output stl folder")
    parser.add_argument('-op',
                        "--output_ply_folder",
                        default='./result_ply/',
                        help="set output ply folder")
    args = parser.parse_args()

    if args.input_scad is None:
        parser.print_help()
        exit()

    main(args)


'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab

python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid                  0.2.0
solidpython            0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple


jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/

https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''

右手坐标系统,X = Red, Y = Green, Z = Blue

 

demo_bolt_004.py

# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site: 
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib
import copy
import numpy as np


HEADER = '''\
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z label
SIZE 4 4 4 4
TYPE F F F I
COUNT 1 1 1 1
WIDTH {}
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS {}
DATA ascii
'''
def write_pcd_fun1(points, save_pcd_path):
    n = len(points)
    lines = []
    for i in range(n):
        x, y, z, label = points[i]
        lines.append('{:.6f} {:.6f} {:.6f} {}'.format(x, y, z, label))
    with open(save_pcd_path, 'w') as f:
        f.write(HEADER.format(n, n))
        f.write('\n'.join(lines))
def write_pcd_fun2(points, save_pcd_path):
    with open(save_pcd_path, 'w') as f:
        f.write(HEADER.format(len(points), len(points)) + '\n')
        np.savetxt(f, points, delimiter=' ', fmt='%f %f %f %d')


#https://blog.csdn.net/weixin_48994268/article/details/115682269
#open3d连续读取pcd文件及实现点云视角转换
def save_view_point(pcd, filename):
    vis = o3d.visualization.Visualizer()
    vis.create_window(window_name='pcd', width=800, height=600)
    vis.add_geometry(pcd)
    vis.run()  # user changes the view and press "q" to terminate
    param = vis.get_view_control().convert_to_pinhole_camera_parameters()
    o3d.io.write_pinhole_camera_parameters(filename, param)
    vis.destroy_window()
def load_view_point(pcd, filename):
    vis = o3d.visualization.Visualizer()
    vis.create_window(window_name='pcd', width=800, height=600)
    ctr = vis.get_view_control()
    param = o3d.io.read_pinhole_camera_parameters(filename)
    vis.add_geometry(pcd)
    ctr.convert_from_pinhole_camera_parameters(param)
    vis.run()
    vis.destroy_window()


scad_parameter={
    #平面距离坐标原点的高度
    'tran_init': 0,
    #底平面参数
    'plate_side': 70,
    'plate_height': 1,
    #垫片参数
    'washer_out_diam': 50,
    'washer_in_diam': 0,
    'washer_height': 2,
    #螺母参数
    'bolt_height': 22, #大小-高度
    'bolt_diam': 35, #大小-宽度
    'bolt_tran': 15, #上下-高度
    'bolt_rot': 15, #角度
    #螺柱参数
    'screw_diam': 16,
    'screw_height': 44,
}


label_dict = {
    "other": 0, #washer
    "stud": 1,
    "plane": 2,
    "nut": 3,
}


def mkdir_os(path):
    if not os.path.exists(path):
        os.makedirs(path)


def main(args):
    scad_path = pathlib.Path(args.input_scad)
    if not scad_path.is_file():
        print("input_scad error")
        return

    mkdir_os(args.output_stl_folder)
    mkdir_os(args.output_ply_folder)

    scad_F = import_scad(args.input_scad)

    index = 0
    count = scad_parameter['screw_height'] * (180/10)
    for nut_height in range(0, scad_parameter['screw_height'], 2):
        for nut_angle in range(0, 180, 75):
            index += 1
            print(index, "/", count)

            save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
            stl_path = os.path.join(args.output_stl_folder, save_stl_name)
            scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
                                plate_side=scad_parameter['plate_side'],
                                plate_height=scad_parameter['plate_height'],
                                washer_out_diam=scad_parameter['washer_out_diam'],
                                washer_in_diam=scad_parameter['washer_in_diam'],
                                washer_height=scad_parameter['washer_height'],
                                bolt_height=scad_parameter['bolt_height'],
                                bolt_diam=scad_parameter['bolt_diam'],
                                bolt_tran=nut_height,
                                bolt_rot=nut_angle,
                                screw_diam=scad_parameter['screw_diam'],
                                screw_height=scad_parameter['screw_height'])

            #分开生成形成label
            #垫圈+other
            washer = copy.deepcopy(scad_model)
            washer.params["plate_height"] = 0
            washer.params["bolt_height"] = 0
            washer.params["screw_height"] = 0
            #螺柱
            stud = copy.deepcopy(scad_model)
            stud.params["plate_height"] = 0
            stud.params["washer_height"] = 0
            stud.params["bolt_height"] = 0
            #底平面
            plane = copy.deepcopy(scad_model)
            plane.params["washer_height"] = 0
            plane.params["bolt_height"] = 0
            plane.params["screw_height"] = 0
            #螺母
            nut = copy.deepcopy(scad_model)
            nut.params["plate_height"] = 0
            nut.params["washer_height"] = 0
            nut.params["screw_height"] = 0

            model_list = [washer, stud, plane, nut]
            mesh_list = []
            color_list = [
                [1, 0, 0],
                [0, 1, 0],
                [0, 0, 1],
                [1, 1, 0]
            ]
            for ind, mod in enumerate(model_list):
                v_rander = viewscad.Renderer()
                v_rander.render(mod, outfile=stl_path) #这里已经保存了stl,但是是ascii format
                #pymeshlab
                import pymeshlab as ml
                ms = ml.MeshSet()
                ms.load_new_mesh(stl_path)
                filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
                ms.save_current_mesh(filename_mesh_out)
                #这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
                #https://github.com/intel-isl/Open3D/issues/2479
                #open3d
                mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
                mesh_bolt.paint_uniform_color(color_list[ind])
                mesh_list.append(mesh_bolt)


            model = mesh_list[0] + mesh_list[1] + mesh_list[2] + mesh_list[3]

            model_point = model.sample_points_poisson_disk(number_of_points=15000)
            print('model_point:', model_point)
            model_point = model_point.voxel_down_sample(voxel_size=1)
            print('model_point', model_point)



            radius = 11000
            camera3 = [60, 100]
            for cam_z, cameraZ in enumerate(camera3):

                save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}_camZ-{}.ply".format(index, nut_height, nut_angle, cameraZ)
                ply_path = os.path.join(args.output_ply_folder, save_ply_name)

                # o3d.visualization.draw_geometries([model_point,
                #                                    o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
                #                                    model_point.get_axis_aligned_bounding_box(),
                #                                    ])

                camera = [70, 70, cameraZ]
                _, pt_map = model_point.hidden_point_removal(camera, radius)
                select_point = model_point.select_down_sample(pt_map)

                # o3d.visualization.draw_geometries([select_point,
                #                                    o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
                #                                    select_point.get_axis_aligned_bounding_box(),
                #                                    ])

                #通过颜色确定label,保存为pcd
                label = select_point.colors
                np_label = np.asarray(label)
                #不要使用取整,会发生[1 1 0]变成[0 0 0]的情况,具体我还不知道为什么
                #np_label = np_label.astype(np.int)
                #np_label = np.floor(np_label)
                points = select_point.points
                np_points = np.asarray(points)


                ind_washer = np.intersect1d(np.where(np_label[:,0] >= 0.5), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 0))
                ind_stud = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] >= 0.5), np.where(np_label[:,2] == 0))
                ind_plane = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 0), np.where(np_label[:,2] >= 0.5))
                ind_nut = np.intersect1d(np.where(np_label[:,0] >= 0.5), np.where(np_label[:,1] >= 0.5), np.where(np_label[:,2] == 0))

                np_washer = np_points[ind_washer][:, :3]
                np_stud = np_points[ind_stud][:, :3]
                np_plane = np_points[ind_plane][:, :3]
                np_nut = np_points[ind_nut][:, :3]

                # pcd1 = o3d.geometry.PointCloud()
                # pcd1.points = o3d.utility.Vector3dVector(np_washer)
                # pcd1.paint_uniform_color(color_list[0])
                # pcd2= o3d.geometry.PointCloud()
                # pcd2.points = o3d.utility.Vector3dVector(np_stud)
                # pcd2.paint_uniform_color(color_list[1])
                # pcd3 = o3d.geometry.PointCloud()
                # pcd3.points = o3d.utility.Vector3dVector(np_plane)
                # pcd3.paint_uniform_color(color_list[2])
                # pcd4 = o3d.geometry.PointCloud()
                # pcd4.points = o3d.utility.Vector3dVector(np_nut)
                # pcd4.paint_uniform_color(color_list[3])
                # o3d.visualization.draw_geometries([pcd1,pcd2,pcd3,pcd4,
                #                                    o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
                #                                    select_point.get_axis_aligned_bounding_box(),
                #                                    ])

                t_lab = np.ones((np_washer.shape[0], 1), dtype=np.int8) * 0
                p_washer = np.hstack((np_washer, t_lab))
                t_lab = np.ones((np_stud.shape[0], 1), dtype=np.int8) * 1
                p_stud = np.hstack((np_stud, t_lab))
                t_lab = np.ones((np_plane.shape[0], 1), dtype=np.int8) * 2
                p_plane = np.hstack((np_plane, t_lab))
                t_lab = np.ones((np_nut.shape[0], 1), dtype=np.int8) * 3
                p_nut = np.hstack((np_nut, t_lab))
                write_pcd_fun1(np.vstack((p_washer, p_stud, p_plane, p_nut)), ply_path.replace('.ply', '.pcd'))


if __name__ == "__main__":

    parser = argparse.ArgumentParser(description="creat bolt")
    parser.add_argument('-if',
                        "--input_scad",
                        default='./hex_head2.scad',
                        help="set input scad file")
    parser.add_argument('-os',
                        "--output_stl_folder",
                        default='./result_stl/',
                        help="set output stl folder")
    parser.add_argument('-op',
                        "--output_ply_folder",
                        default='./result_ply/',
                        help="set output ply folder")
    args = parser.parse_args()

    if args.input_scad is None:
        parser.print_help()
        exit()

    main(args)


'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab

python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid                  0.2.0
solidpython            0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple


jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/

https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''

生成shapenet格式

# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: creat_train_txt.py.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site: 
# @Time: 5月 24, 2021
# ---
import os
import cv2
import open3d as o3d
import numpy as np
import json

def mkdir_os(path):
    if not os.path.exists(path):
        os.makedirs(path)

def get_plane(cloud, distance_threshold=0.01, ransac_n=10, num_iterations=200, rt_plane=False):
    coefficients, inliers = cloud.segment_plane(distance_threshold=distance_threshold,
                                                ransac_n=ransac_n,
                                                num_iterations=num_iterations)
    if rt_plane:
        mmin = np.min(pcd2npy(cloud), axis=0).reshape(-1, 1)
        mmax = np.max(pcd2npy(cloud), axis=0).reshape(-1, 1)
        bbox = np.concatenate([mmin, mmax], axis=1)
        x_range, y_range = bbox[0, :], bbox[1, :]
        cloud_plane = generate_plane_points(coefficients, x_range, y_range)
        return coefficients, cloud_plane
    return coefficients

def points_plane_dists(points, coef):
    ones = np.ones(shape=(len(points), 1)).astype(np.float32)
    points = np.concatenate([points, ones], axis=1)
    coef = np.array(coef).reshape(4, 1)
    dists = np.squeeze(np.matmul(points, coef))
    return dists

category = "luomu_openscad"
save_path = "shapenet"

mkdir_os(save_path)
mkdir_os(os.path.join(save_path, "train_test_split"))
mkdir_os(os.path.join(save_path, category))

data_path = "./result_ply"
lines = os.listdir(data_path)

'''
luomu模型:
label_dict = {
    "other": 0, 背景(垫圈噪声等属于背景类)
    "stud": 1, 螺柱(螺母上部分的螺栓点云才是我们的感兴趣点云)
    "plane": 2, 平面
    "nut": 3, 螺母
}

luoshuan模型:
label_dict = {
    "other": 0, 背景(垫圈噪声等属于背景类)
    "bolt": 1, 螺栓
    "plane": 2, 平面
}
'''

train_list, test_list, val_list = [], [], []
for ind, line in enumerate(lines):
    print(ind, "/", len(lines))
    pcd = os.path.join(data_path, line)
    with open(pcd, "r") as f:
        data = f.readlines()
        data_point3d = data[11:]
        #该数据存在问题,我想要的是螺母之上的螺栓为label==1,螺母之下的label==0(背景类),若螺母拧过螺柱,那么此时没有点云label==1
        other_list = []
        stud_list = []
        plane_list = []
        nut_list = []
        all_list = []
        for m_line in data_point3d:
            temp = m_line.split(" ")
            label = int(float(temp[3].strip()))
            if label == 0:
                other_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
                all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
            elif label == 1:
                stud_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
            elif label == 2:
                plane_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
                all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
            elif label == 3:
                nut_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
                all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
            else:
                exit()
        color_list = [
            [1, 0, 0],
            [0, 1, 0],
            [0, 0, 1],
            [1, 1, 0]
        ]
        np_data = np.array(all_list)
        other_o3d = o3d.geometry.PointCloud()
        other_o3d.points = o3d.utility.Vector3dVector(np.array(other_list)[:, 0:3])
        other_o3d.paint_uniform_color(color_list[0])
        stud_o3d= o3d.geometry.PointCloud()
        stud_o3d.points = o3d.utility.Vector3dVector(np.array(stud_list)[:, 0:3])
        stud_o3d.paint_uniform_color(color_list[1])
        plane_o3d = o3d.geometry.PointCloud()
        plane_o3d.points = o3d.utility.Vector3dVector(np.array(plane_list)[:, 0:3])
        plane_o3d.paint_uniform_color(color_list[2])
        nut_o3d = o3d.geometry.PointCloud()
        nut_o3d.points = o3d.utility.Vector3dVector(np.array(nut_list)[:, 0:3])
        nut_o3d.paint_uniform_color(color_list[3])
        # o3d.visualization.draw_geometries([other_o3d,stud_o3d,plane_o3d,nut_o3d,
        #                                    o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
        #                                    #select_point.get_axis_aligned_bounding_box(),
        #                                    ])
        coefficients = get_plane(plane_o3d)
        d_list = []
        a, b, c = coefficients[0], coefficients[1], coefficients[2]
        for index, pt in enumerate(np.array(nut_list)[:, 0:3]):
            x, y, z = pt
            if x != 0 and y != 0 and z != 0:
                d = (0 - (a * x + b * y + c * z))
                d_list.append(d)
        d_list = np.sort(np.array(d_list), axis=0)
        d_parameter = np.mean(d_list[5:10], axis=0, dtype=np.float32)
        coefficients[3] = d_parameter
        pp_dists = points_plane_dists(np.array(stud_list)[:, 0:3], coefficients)
        ind_below = np.where(pp_dists<0)
        ind_above = np.where(pp_dists>=0)
        points_below = np.array(stud_list)[ind_below]
        points_below[:, 3] = 0
        points_above = np.array(stud_list)[ind_above]
        if points_below.size != 0:
            data_point3d = np.vstack((np_data, points_below))
        if points_above.size != 0:
            data_point3d = np.vstack((np_data, points_above))

        with open(os.path.join(save_path, category, line.replace('.pcd', '.txt')), "w") as f:
            for m_val in data_point3d:
                f.write("{} {} {} 0.0 0.0 0.0 {}\n".format(m_val[0], m_val[1], m_val[2], m_val[3]))

    train_list.append(os.path.join(save_path, category, line.split(".pcd")[0]))

with open(os.path.join(save_path, "synsetoffset2category.txt"), "w") as f:
    f.write("luomu\tluomu_openscad\n")

with open(os.path.join(save_path, "train_test_split", "shuffled_test_file_list.json"), "w") as f_obj:
    json.dump(train_list, f_obj, indent=1)
with open(os.path.join(save_path, "train_test_split", "shuffled_train_file_list.json"), "w") as f_obj:
    json.dump(train_list, f_obj, indent=1)
with open(os.path.join(save_path, "train_test_split", "shuffled_val_file_list.json"), "w") as f_obj:
    json.dump(train_list, f_obj, indent=1)

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值