Points or spheres in 3D cube with Perl - perl

Let's say I have #points[$number][$x][$y][$z][$color] and I just for debug purposes want them visualized in 3D cube to better observe what I have. Typically I export them to *.txt and use R 3D plotting, but maybe there is easy way to do this in Perl?
It would be even better to have spheres with radius.

My answer: use OpenGL perl bindings
I haven't quite done an exact answer to your question but I'm sure you can adopt this code
I haven't done OpenGL before but it was a fun little evening project
use OpenGL qw/ :all /;
use constant ESCAPE => 27;
# Global variable for our window
my $window;
my $CubeRot = 0;
my $xCord = 1;
my $yCord = 1;
my $zCord = 0;
my $rotSpeed = 0.02 ;
($width, $height) = (1366,768);
#points = ( [ 30,40,40,[100,0,0]], #red
[ 100,100,40,[0,100,0]], #green
[ 100,10,60,[0,100,100]], #turquoise
[ 200,200,100,[0,0,100]] #blue
);
sub reshape {
glViewport(0, 0, $width, $height); # Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); # Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); # Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, $width / $height, 1.0, 100.0); # Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); # Switch back to the model view matrix, so that we can start drawing shapes correctly
glOrtho(0, $width, 0, $height, -1, 1); # Map abstract coords directly to window coords.
glScalef(1, -1, 1); # Invert Y axis so increasing Y goes down.
glTranslatef(0, -h, 0); # Shift origin up to upper-left corner.
}
sub keyPressed {
# Shift the unsigned char key, and the x,y placement off #_, in
# that order.
my ($key, $x, $y) = #_;
# If escape is pressed, kill everything.
if ($key == ESCAPE)
{
# Shut down our window
glutDestroyWindow($window);
# Exit the program...normal termination.
exit(0);
}
}
sub InitGL {
# Shift the width and height off of #_, in that order
my ($width, $height) = #_;
# Set the background "clearing color" to black
glClearColor(0.0, 0.0, 0.0, 0.0);
# Enables clearing of the Depth buffer
glClearDepth(1.0);
glDepthFunc(GL_LESS);
# Enables depth testing with that type
glEnable(GL_DEPTH_TEST);
# Enables smooth color shading
glShadeModel(GL_SMOOTH);
# Reset the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
# Reset the modelview matrix
glMatrixMode(GL_MODELVIEW);
}
sub display {
glClearColor(1.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity;
glTranslatef(0.0, 0.0, -5.0); # Push eveything 5 units back into the scene, otherwise we won't see the primitive
#glPushMatrix();
#glRotatef($CubeRot, $xCord, $yCord, $zCord);
# this is where the drawing happens, adjust glTranslate to match your coordinates
# the centre is is 0,0,0
for my $sphere ( #points ) {
glPushMatrix();
glColor3b( #{$sphere->[3]}) ;
glRotatef($CubeRot, $xCord, $yCord, $zCord);
glTranslatef($sphere->[0]/50 -2 ,$sphere->[1]/50 -2 ,$sphere->[2]/50 -2);
glutWireSphere(1.0,24,24); # Render the primitive
glPopMatrix();
}
$CubeRot += $rotSpeed;
glFlush; # Flush the OpenGL buffers to the window
}
# Initialize GLUT state
glutInit;
# Depth buffer */
glutInitDisplayMode(GLUT_SINGLE);
# The window starts at the upper left corner of the screen
glutInitWindowPosition(0, 0);
# Open the window
$window = glutCreateWindow("Press escape to quit");
# Register the function to do all our OpenGL drawing.
glutDisplayFunc(\&display);
# Go fullscreen. This is as soon as possible.
glutFullScreen;
glutReshapeFunc(\&reshape);
# Even if there are no events, redraw our gl scene.
glutIdleFunc(\&display);
# Register the function called when the keyboard is pressed.
glutKeyboardFunc(\&keyPressed);
# Initialize our window.
InitGL($width, $height);
# Start Event Processing Engine
glutMainLoop;
return 1;

Related

What unicode characters are suitable for making a heatmap in console?

I want to make a command that takes a matrix or vectors of numbers and prints a string-representation where every number is mapped to a character with varying darkness depending on its value.
The only characters I've found that gives a consistent shape but varying color is the unicode block elements ' ░▒▓█' (see e.g. wikipedia), but this only gives me 5 possible shades (space, 3 shades, 1 filled block). I use every character twice so the widhts is approximately the same as the height.
What other characters are suitable for drawing a heatmap in console?
See example code in python below. The question is of course applicable for other languages as well.
import numpy as np
def ascii_heatmap(matrix: np.ndarray, disp=True):
assert matrix.ndim <= 2
matrix = np.atleast_2d(matrix)
vmax = matrix.max()
vmin = matrix.min()
symbolrange= ' ░▒▓█'
symbol_index_matrix = (matrix - vmin) * (len(symbolrange)-1) / (vmax-vmin)
heatmap_rows = []
for row in symbol_index_matrix:
heatmap_rows.append("".join(map(lambda x: symbolrange[int(x)]*2, row)))
heatmap = "\n".join(heatmap_rows)
if disp==True:
print(heatmap)
return heatmap
#Examples with vector and matrix
ascii_heatmap(np.array([1,2,3,4,5]))
ascii_heatmap(np.arange(9).reshape((3,3)))
Use the turbo ramp (Python code) and true colour terminal output.
#!/usr/bin/env perl
my $step = 17;
for (my $r = 0; $r <= 255; $r += $step) {
for (my $g = 0; $g <= 255; $g += $step) {
for (my $b = 0; $b <= 255; $b += $step) {
print "\e[48;2;$r;$g;${b}m ";
# ↑ escape char ↑ coloured space char
}
}
}

How to write a Three-view drawings shader(in Unity3d)?

I attempt implement a Three-view drawings shader for simple geometry and have test with this shader,simple as:
float doCos = dot(viewDirection, normalDirection);
float4 texColor;
//2. need board
bool isBackSide = doCos < 0;
if(isBackSide) {
//_Dotted is a 2d texture the board is black dot line
texColor = tex2D(_Dotted, i.tex.xy * _Dotted_ST.xy + _Dotted_ST.zw);
} else {
//_Dotted is a 2d texture the board is black entity line
texColor = tex2D(_Entity, i.tex.xy * _Entity_ST.xy + _Entity_ST.zw);
}
//ignore some color
if(texColor.x > 0.5f) {
discard;
}
return texColor;
There have a problem the dotted line will obvious then entity line when forward plane become steep.also, I add a rim line but its not point so I ignore it.
in order to clarify, I use two questions:
1. how to modify that shader solve dotted line problem?
2. Does have exist professional Three-view drawings shader?(not find yet)

Failing to draw on a Gtk.DrawingArea

I'm not able to draw, i've already read tutorials, i can't find the problem.
I simply have a correct UI drawed by Glade. Then i want to draw, for example 50 drawing areas. So i create a Grid with 50 cells; for each cell there is a vertical box (with a drawing area and a label inside each one). But i can't seen anything drawed.
class collega_GUI:
def __init__(self):
try:
self.__builder = Gtk.Builder()
self.__builder.add_from_file('UI2.glade')
self.__Grid = Gtk.Grid()
self.__Grid.set_margin_left(20)
self.__Grid.set_margin_right(20)
self.__Grid.set_row_spacing(10)
self.__Grid.set_column_spacing(15)
self.__Grid.set_column_homogeneous(True)
self.__GridBox = self.__builder.get_object('box11')
self.__GridBox.pack_end(self.__Grid, 1, 1, 20)
indirizzi_ip = []
for i in range(50):
indirizzi_ip.append(str(i))
cpu_info = {}
for ip in indirizzi_ip:
cpu_info[ip] = dict()
left = 0
right = 0
for ip in indirizzi_ip:
cpu_info[ip]['drawing_area'] = Gtk.DrawingArea()
cpu_info[ip]['drawing_area'].set_size_request(100, 100)
cpu_info[ip]['drawing_area'].set_name(ip)
box = Gtk.VBox(False, 5)
box.add(cpu_info[ip]['drawing_area'])
label = Gtk.Label(ip)
box.add(label)
self.__Grid.attach(box, left, right, 1, 1) #object,left,right,top,bottom
cpu_info[ip]['drawing_area'].connect("draw", self.__draw)
label.show()
cpu_info[ip]['drawing_area'].show() #the draw should start now!
box.show()
# 5 drawing areas in a row
left += 1
if left == 5:
right += 1
left = 0
self.__builder.get_object('Azioni_Window').show()
Gtk.main()
except Exception as xe:
logging.error('%s' % str(xe))
sys.exit()
def __draw(self, widget, context):
context.set_source_rgb(0.9, 0, 0.1) #rosso
context.set_source_rgb(0.1, 0.9, 0) #verde
context.set_source_rgb(0.8, 0.7, 0) #giallo
context.set_source_rgb(0.8, 0.7, 0.8) #inattivo
context.rectangle(0, 0, widget.get_allocated_width(), widget.get_allocated_height())
context.fill()
if __name__=='__main__':
try:
UINX=collega_GUI()
except Exception:
sys.exit()
You're missing
self.__Grid.show()
And hence nothing in the grid is shown.
In general it's easier to just call show_all() on some top-level container rather than trying to remember to show() every individual widget.

Drawing text on screen with GLUT not working at all

I don't know what i'm doing wrong, this is perl but it's language agnostic(methinks):
This example draws a lot of snowmans from an example i've found in the web:
(I ported it to perl)
The problems is that there's no sign of the text being rendered.
sub renderScene{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gluLookAt( $x, 1.0, $z,
$lx,1.0,$lz,
0.0,1.0,0.0);
glColor3f(0.5, 0.2, 0.4);
glMatrixMode(GL_MODELVIEW);
#Draw ground
glBegin(GL_QUADS);
glVertex3f(-100.0, 0.0, -100.0);
glVertex3f(-100.0, 0.0, 100.0);
glVertex3f( 100.0, 0.0, 100.0);
glVertex3f( 100.0, 0.0, -100.0);
glEnd();
for ($i=-3; $i<3; $i++)
{
for ($j=-3 ; $j<3; $j++)
{
glPushMatrix();
glTranslatef($i*10.0,0,$j*10.0);
drawSnowMan();
glPopMatrix();
}
}
glColor3f(0.5, 0.5, 0.0);
glRasterPos2f(0.5, 0.5);
my $strin = "Viva peron carajo!";
my #string = split('',$strin);
for my $char(#string){
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, $char);
}
glutSwapBuffers();
}
Your code is incomplete. $x, $z, $lx and $lz are undefined and there are no calls to set up the projection matrix. As such, I can only give suggestions:
glRasterPos coordinates are world/model coordinates. Did you really mean to put the text close to the origin, possibly inside the center snowman?
If you want to work in screen coordinates, you should set up an appropriate orthogonal "2D" projection matrix. Remember that you can push/pop/play around with the projection matrix in between drawing commands.
Also, start by rendering a triangle/quad in the text position.

fpdf multicell issue

how we display fpdf multicell in equal heights having different amount of content
Great question.
I solved it by going through each cell of data in your row that will be multicelled and determine the largest height of all these cells. This happens before you create your first cell in the row and it becomes the new height of your row when you actually go to render it.
Here's some steps to achieve this for just one cell, but you'll need to do it for every multicell:
This assumes a $row of data with a String attribute called description.
First, get cell content and set the column width for the cell.
$description = $row['desciption']; // MultiCell (multi-line) content.
$column_width = 50;
Get the width of the description String by using the GetStringWidth() function in FPDF:
$total_string_width = $pdf->GetStringWidth($description);
Determine the number of lines this cell will be:
$number_of_lines = $total_string_width / ($column_width - 1);
$number_of_lines = ceil( $number_of_lines ); // Round it up.
I subtracted 1 from the $column_width as a kind of cell padding. It produced better results.
Determine the height of the resulting multi-line cell:
$line_height = 5; // Whatever your line height is.
$height_of_cell = $number_of_lines * $line_height;
$height_of_cell = ceil( $height_of_cell ); // Round it up.
Repeat this methodology for any other MultiCell cells, setting the $row_height to the largest $height_of_cell.
Finally, render your row using the $row_height for your row's height.
Dance.
I was also getting same issue when i have to put height equal to three lines evenif i have content of half or 1 and half line, i solved this by checking if in a string no of elements are less than no of possible letters in a line it is 60. If no of letters are less or equal to one line then ln() is called for two empty lines and if no of words are equal to or less than 2 line letters then one ln() is called.
My code is here:
$line_width = 60; // Line width (approx) in mm
if($pdf->GetStringWidth($msg1) < $line_width)
{
$pdf->MultiCell(75, 8, $msg1,' ', 'L');
$pdf->ln(3);
$pdf->ln(3.1);
}
else{
$pdf->MultiCell(76, 4, $msg1,' ', 'L');
$pdf->ln(3.1);
}
Ok I've done the recursive version. Hope this helps even more to the cause!
Take in account these variables:
$columnLabels: Labels for each column, so you'll store data behind each column)
$alturasFilas: Array in which you store the max height for each row.
//Calculate max height for each column for each row and store the value in //////an array
$height_of_cell = 0;
$alturasFilas = array();
foreach ( $data as $dataRow ) {
for ( $i=0; $i<count($columnLabels); $i++ ) {
$variable = $dataRow[$i];
$total_string_width = $pdf->GetStringWidth($variable);
$number_of_lines = $total_string_width / ($columnSizeWidth[$i] - 1);
$number_of_lines = ceil( $number_of_lines ); // Redondeo.
$line_height = 8; // Altura de fuente.
$height_of_cellAux = $number_of_lines * $line_height;
$height_of_cellAux = ceil( $height_of_cellAux );
if($height_of_cellAux > $height_of_cell){
$height_of_cell = $height_of_cellAux;
}
}
array_push($alturasFilas, $height_of_cell);
$height_of_cell = 0;
}
//--END--
Enjoy!
First, it's not the question to get height of Multicell. (to #Matias, #Josh Pinter)
I modified answer of #Balram Singh to use this code for more than 2 lines.
and I added new lines (\n) to lock up height of Multicell.
like..
$cell_width = 92; // Multicell width in mm
$max_line_number = 4; // Maximum line number of Multicell as your wish
$string_width = $pdf->GetStringWidth($data);
$line_number = ceil($string_width / $cell_width);
for($i=0; $i<$max_line_number-$line_number; $i++){
$data.="\n ";
}
Of course you have to assign SetFont() before use GetStringWidth().
My approach was pretty simple. You already need to override the header() and footer() in the fpdf class.
So i just added a function MultiCellLines($w, $h, $txt, $border=0, $align='J', $fill=false).
That's a very simple copy of the original MultiCell. But it won't output anything, just return the number of lines.
Multiply the lines with your line-height and you're safe ;)
Download for my code is here.
Just rename it back to ".php" or whatever you like. Have fun. It works definitely.
Mac
My personal solution to reduce the font size and line spacing according to a preset box:
// set box size and default font size
$textWidth = 100;
$textHeight = 100;
$fontsize = 12;
// if you get text from html div (using jquery and ajax) you must replace every <br> in a new line
$desc = utf8_decode(str_replace('<br>',chr(10),strip_tags($_POST['textarea'],'<br>')));
// count newline set in $desc variable
$countnl = substr_count($desc, "\n");
// Create a loop to reduce the font according to the contents
while($pdf->GetStringWidth($desc) > ($textWidth * (($textHeight-$fontsize*0.5*$countnl) / ($fontsize*0.5)))){
$fontsize--;
$pdf->SetFont('Arial','', $fontsize);
}
// print multicell and set line spacing
$pdf->MultiCell($textWidth, ($fontsize*0.5), "$desc", 0, 'L');
That's all!
A better solution would be to use your own word wrap function, as FPDF will not cut words, so you do not have to rely on the MultiCell line break.
I have wrote a method that checks for every substring of the text if the getStringWidth of FPDF is bigger than the column width, and splits accordingly.
This is great if you care more about a good looking PDF layout, than performance.
public function wordWrapMultiCell($text, $cellWidth = 80) {
$explode = explode("\n", $text);
array_walk($explode, 'trim');
$lines = [];
foreach($explode as $split) {
$sub = $split;
$char = 1;
while($char <= strlen($sub)) {
$substr = substr($sub, 0, $char);
if($this->pdf->getStringWidth($substr) >= $cellWidth - 1) { // -1 for better getStringWidth calculating
$pos = strrpos($substr, " ");
$lines[] = substr($sub, 0, ($pos !== FALSE ? $pos : $char)).($pos === FALSE ? '-' : '');
if($pos !== FALSE) { //if $pos returns FALSE, substr has no whitespace, so split word on current position
$char = $pos + 1;
$len = $char;
}
$sub = ltrim(substr($sub, $char));
$char = 0;
}
$char++;
}
if(!empty($sub)) {
$lines[] = $sub;
}
}
return $lines;
}
This returns an array of text lines, which you could merge by using implode/join:
join("\r\n", $lines);
And what it was used for in the first place, get the line height:
$lineHeight = count($lines) * $multiCellLineHeight;
This only works for strings with a space character, no white spacing like tabs. You could replace strpos with a regexp function then.