Skip to content

Instantly share code, notes, and snippets.

@LingDong-
Last active December 26, 2019 22:57
Show Gist options
  • Save LingDong-/ed7f8b56b74d903afc73fffaf1e2cee0 to your computer and use it in GitHub Desktop.
Save LingDong-/ed7f8b56b74d903afc73fffaf1e2cee0 to your computer and use it in GitHub Desktop.
cli-ptcloud.c
//
// cli-ptcloud.c
// - Lingdong Huang 2019
//
// View point clouds (.ply format) in commandline
//
// Dependencies:
// - curses (-lcurses, comes with most unix systems)
// - rply (http://w3.impa.br/~diego/software/rply/, place in path below:
#define RPLY_PATH "include/rply/rply.h"
//
// Compile:
// $ gcc -O3 include/rply/rply.h include/rply/rply.c cli-ptcloud.c -lcurses -lm; mv ./a.out cli-ptcloud
//
// Usage:
// $ ./cli-ptcloud path/to/model.ply
//
// Controls:
// - Keys 0-6 to switch view
// - Mouse to rotate the model in 0th (free) view
//
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include RPLY_PATH
#define M_MOVE 0x8000000
#define LM_CLICK 0x1
#define LM_DRAG 0x80000
#define LM_UP 0x40000
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
#define MAT_IDEN [16]={1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}
#define MAT_ROTX(a) [16]={1,0,0,0, 0,cos(a),-sin(a),0, 0,sin(a),cos(a),0, 0,0,0,1}
#define MAT_ROTY(a) [16]={cos(a),0,sin(a),0, 0,1,0,0, -sin(a),0,cos(a),0, 0,0,0,1}
#define MAT_ROTZ(a) [16]={cos(a),-sin(a),0,0, sin(a),cos(a),0,0, 0,0,1,0, 0,0,0,1}
#define MAT_TRSL(x,y,z) [16]={1,0,0,x, 0,1,0,y, 0,0,1,z, 0,0,0,1}
#define MAT_SCAL(x,y,z) [16]={x,0,0,0, 0,y,0,0, 0,0,z,0, 0,0,0,1}
#define MAT_MULT(A,B) [16]={(A)[0]*(B)[0]+(A)[1]*(B)[4]+(A)[2]*(B)[8]+(A)[3]*(B)[12],(A)[0]*(B)[1]+(A)[1]*(B)[5]+(A)[2]*(B)[9]+(A)[3]*(B)[13],(A)[0]*(B)[2]+(A)[1]*(B)[6]+(A)[2]*(B)[10]+(A)[3]*(B)[14],(A)[0]*(B)[3]+(A)[1]*(B)[7]+(A)[2]*(B)[11]+(A)[3]*(B)[15],(A)[4]*(B)[0]+(A)[5]*(B)[4]+(A)[6]*(B)[8]+(A)[7]*(B)[12],(A)[4]*(B)[1]+(A)[5]*(B)[5]+(A)[6]*(B)[9]+(A)[7]*(B)[13],(A)[4]*(B)[2]+(A)[5]*(B)[6]+(A)[6]*(B)[10]+(A)[7]*(B)[14],(A)[4]*(B)[3]+(A)[5]*(B)[7]+(A)[6]*(B)[11]+(A)[7]*(B)[15],(A)[8]*(B)[0]+(A)[9]*(B)[4]+(A)[10]*(B)[8]+(A)[11]*(B)[12],(A)[8]*(B)[1]+(A)[9]*(B)[5]+(A)[10]*(B)[9]+(A)[11]*(B)[13],(A)[8]*(B)[2]+(A)[9]*(B)[6]+(A)[10]*(B)[10]+(A)[11]*(B)[14],(A)[8]*(B)[3]+(A)[9]*(B)[7]+(A)[10]*(B)[11]+(A)[11]*(B)[15],(A)[12]*(B)[0]+(A)[13]*(B)[4]+(A)[14]*(B)[8]+(A)[15]*(B)[12],(A)[12]*(B)[1]+(A)[13]*(B)[5]+(A)[14]*(B)[9]+(A)[15]*(B)[13],(A)[12]*(B)[2]+(A)[13]*(B)[6]+(A)[14]*(B)[10]+(A)[15]*(B)[14],(A)[12]*(B)[3]+(A)[13]*(B)[7]+(A)[14]*(B)[11]+(A)[15]*(B)[15]}
#define MAT_TFRM(A,v) [3]={((A)[0]*(v)[0]+(A)[1]*(v)[1]+(A)[2]*(v)[2]+(A)[3])/((A)[12]*(v)[0]+(A)[13]*(v)[1]+(A)[14]*(v)[2]+(A)[15]),((A)[4]*(v)[0]+(A)[5]*(v)[1]+(A)[6]*(v)[2]+(A)[7])/((A)[12]*(v)[0]+(A)[13]*(v)[1]+(A)[14]*(v)[2]+(A)[15]),((A)[8]*(v)[0]+(A)[9]*(v)[1]+(A)[10]*(v)[2]+(A)[11])/((A)[12]*(v)[0]+(A)[13]*(v)[1]+(A)[14]*(v)[2]+(A)[15])}
#define MAT_PROJ(f,v) [2]={(f)*(v)[0]/(v)[2],(f)*(v)[1]/(v)[2]}
#define MAX_W 256
#define MAX_H 218
#define MAX_BINS (MAX_W*MAX_H)
#define SHADE_PT " .:;!+*#"
#define VMODE_FREE 0
#define VMODE_FRONT 1
#define VMODE_LEFT 2
#define VMODE_RIGHT 3
#define VMODE_TOP 4
#define VMODE_BOTTOM 5
#define VMODE_BACK 6
char* ptbin;
float* ptcloud;
long ptcloud_idx = 0;
long n_vertices = 0;
float xmin, xmax, ymin, ymax, zmin, zmax;
float xrot, yrot, zrot;
int vmode = VMODE_FREE;
static int vertex_cb(p_ply_argument argument) {
long eol;
ply_get_argument_user_data(argument, NULL, &eol);
float f = ply_get_argument_value(argument);
ptcloud[ptcloud_idx]=f;
if (ptcloud_idx % 3 == 0){
xmin = MIN(f,xmin);
xmax = MAX(f,xmax);
}
if (ptcloud_idx % 3 == 1){
ymin = MIN(f,ymin);
ymax = MAX(f,ymax);
}
if (ptcloud_idx % 3 == 2){
zmin = MIN(f,zmin);
zmax = MAX(f,zmax);
}
ptcloud_idx++;
return 1;
}
int load_vertices(char* path){
p_ply ply = ply_open(path, NULL, 0, NULL);
if (!ply) return 1;
if (!ply_read_header(ply)) return 1;
p_ply_element element = NULL;
n_vertices =
ply_set_read_cb(ply, "vertex", "x", vertex_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "y", vertex_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "z", vertex_cb, NULL, 1);
ptcloud = (float *)malloc(n_vertices*3*sizeof(float));
if (!ptcloud){
exit(1);
}
if (!ply_read(ply)) return 1;
ply_close(ply);
return 0;
}
void normalize_ptcloud(){
float s = 2/MAX(MAX(xmax-xmin,ymax-ymin),zmax-zmin);
for (long i = 0; i < n_vertices; i++){
ptcloud[i*3+0] = (ptcloud[i*3+0]-xmin)*s-1;
ptcloud[i*3+1] = (ptcloud[i*3+1]-ymin)*s-1;
ptcloud[i*3+2] = (ptcloud[i*3+2]-zmin)*s-1;
}
}
void print_ptcloud(){
for (long i = 0; i < n_vertices; i++){
printf("[%ld] %f %f %f\n",i,ptcloud[i*3],ptcloud[i*3+1],ptcloud[i*3+2]);
}
printf("#vertices: %ld.\n",n_vertices);
}
void draw_ptcloud(){
int W = MIN(COLS,MAX_W);
int H = MIN(LINES,MAX_H);
for (int i = 0; i < W*H; i++){
ptbin[i] = 0;
}
int m = MIN(W/2,H);
int padx = 0;
int pady = 0;
if (W/2 > H){
padx = (W/2-H);
}else{
pady = (H-W/2)/2;
}
for (long i = 0; i < n_vertices; i++){
float x = ptcloud[i*3+0];
float y = ptcloud[i*3+1];
float z = ptcloud[i*3+2];
float rotx MAT_ROTX(xrot);
float roty MAT_ROTY(yrot);
float rotz MAT_ROTZ(zrot);
float rotxy MAT_MULT(roty,rotx);
float rot MAT_MULT(rotz,rotxy);
float trl MAT_TRSL(0,0,10);
float tfm MAT_MULT(trl,rot);
float v[3] = {x,y,z};
float u MAT_TFRM(tfm,v);
float w MAT_PROJ(10,u);
int r = round(pady-w[1] * (m/2) + (m/2));
int c = round(padx+w[0] * m + m);
if (0 <= c && c < W && 0 <= r && r < H){
ptbin[r*W+c]++;
}
}
for (int i = 0; i < H; i++){
for (int j = 0; j < W; j++){
int n = MIN(7,ptbin[i*W+j]);
if (!(i==H-1 && j==W-1)){
mvprintw(i,j,"%c",SHADE_PT[n]);
}
}
}
}
int mousex;
int mousey;
void on_keypress(int c){
if (48 <= c && c <= 54){
vmode = c-48;
}
}
void handle_events(){
int c = wgetch(stdscr);
if (c == ERR) {
}else if (c == KEY_MOUSE) {
MEVENT event;
if (getmouse(&event) == OK) {
mousex = event.x;
mousey = event.y;
}
} else {
on_keypress(c);
}
}
void init_curses(){
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
mouseinterval(0);
curs_set(0);
printf("\033[?1003h\n"); // Makes the terminal report mouse movement events
}
void exit_curses(){
printf("\033[?1003l\n"); // Disable mouse movement events, as l = low
endwin();
}
int main(int argc, char** argv){
load_vertices(argv[1]);
normalize_ptcloud();
print_ptcloud();
ptbin = (char*)malloc(MAX_BINS*sizeof(char));
init_curses();
long frame = 0;
char vdesc[10];
for(;;) {
int W = MIN(COLS,MAX_W);
int H = MIN(LINES,MAX_H);
if (frame == 0){
mousex = W/2;
mousey = H/2;
}else{
handle_events();
}
xrot = 0;
yrot = 0;
zrot = 0;
switch(vmode){
/*--*/ case VMODE_FREE:
xrot = ((float)mousey/(float)LINES-0.5)*M_PI*2;
yrot = ((float)mousex/(float)COLS-0.5)*M_PI*2;
strcpy(vdesc,"FREE\0");
break; case VMODE_TOP:
xrot = M_PI/2;
strcpy(vdesc,"TOP\0");
break; case VMODE_BOTTOM:
xrot = -M_PI/2;
strcpy(vdesc,"BOTTOM\0");
break; case VMODE_LEFT:
yrot = M_PI/2;
strcpy(vdesc,"LEFT\0");
break; case VMODE_RIGHT:
yrot = -M_PI/2;
strcpy(vdesc,"RIGHT\0");
break; case VMODE_FRONT:
strcpy(vdesc,"FRONT\0");
break; case VMODE_BACK:
yrot = M_PI;
strcpy(vdesc,"BACK\0");
break; default:;
}
draw_ptcloud();
mvprintw(0,0,"MODEL: %s",argv[1]);
mvprintw(1,0,"VIEW: (%d) %s",vmode,vdesc);
refresh();
frame++;
}
exit_curses();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment