How can I plot a 3D wireframe in Excel? - excel

using Excel interface, how can I plot a 3D wireframe? I have no clue how to do this ! but wanted to do by giving the coordinates of the joints where there are frames between them.
for simplicity of the Example, just immagine a 3D wireframe in the form of a cube.

Ok, so I previously gave advice as to switch on the macro recorder but this would not give you the 3D mathematics required to draw wireframes. For that you need a library, Philip Rideout's SVG wireframes Python library which is written up here on his blog.
On my blog I have added some code which parses the SVG file generated by Philip Rideout's code and then converts the Polygon directives to Excel free from shapes on the worksheet. This is a screenshot of the output.
I have added the code here as well
class ScreenUpdatingRAII(object):
def __init__(self, app, visible:bool=False):
self.app = app
self.saved = app.ScreenUpdating
app.ScreenUpdating = visible
def restore(self):
self.app.ScreenUpdating = self.saved
self.app = None
def convertSvgToExcelShapes(filename):
import xml.etree.ElementTree as ET
from win32com.client import GetObject,Dispatch
# code below is highly dependent on the child
# structure because xpath was not working for me (my bad)
dom = ET.parse(filename)
rootxml = dom.getroot()
g = rootxml[1] # second child
wb = Dispatch(GetObject(r"C:\Users\Simon\source\repos\WireframeExcelShapes\WireframeExcelShapes\WireframeExcelShapes.xlsx"))
app = Dispatch(wb.Parent)
ws = Dispatch(wb.Worksheets.Item("WireFrame"))
shps = Dispatch(ws.Shapes)
for x in shps:
Dispatch(x).Delete()
idx =0
scale, xoffset, yoffset = 500, 300,300
screenUpdates = ScreenUpdatingRAII(app)
for polygon in g:
# triple nested list comprehension parsing the points by splitting
# first by space then by comma then converting to float
points = [[float(z[0])*scale+xoffset, float(z[1])*scale+yoffset] for z in [y.split(',') for y in [x for x in polygon.attrib['points'].split()]]]
#print(points)
msoEditingAuto,msoSegmentLine, msoFalse, msoTrue = 0,0,0, -1
freeformbuilder=shps.BuildFreeform(msoEditingAuto, points[0][0] , points[0][1])
freeformbuilder.AddNodes(msoSegmentLine, msoEditingAuto, points[1][0] , points[1][1])
freeformbuilder.AddNodes(msoSegmentLine, msoEditingAuto, points[2][0] , points[2][1])
freeformbuilder.AddNodes(msoSegmentLine, msoEditingAuto, points[0][0], points[0][1])
newShp = Dispatch(freeformbuilder.ConvertToShape())
shpFill = Dispatch(newShp.Fill)
shpFill.Visible = msoTrue
shpFill.Transparency = 0.25
shpFill.Solid
shpFill.ForeColor.RGB = 0xFFFFFF
idx=+1
screenUpdates.restore()
pass
filename = "octahedron.svg"
generate_svg(filename)
convertSvgToExcelShapes(filename)
You still have a little work to do to generate your own shape as the sample shape is an octahedron.

Related

How to display variables in python without using print()

I'm a reallyyyy new user to python, and mostly used Matlab before so still trying to understand the workings of python.
I've managed to write a program that detects ARUCO markers using a camera and calculates their pose vectors: a 3D translation list (# of markers, 1,3) and another for rotation (# of markers, 1,3).
I currently use print() to display the two vectors in "time" ( refresh with the camera's fps)...and trying to figure out if a separate window could be used to display them and refresh with each new frame.
I have done some looking into tkinter but it wasn't really working for me. I am not sure if there is a smarter or easier way to do this.
** only using arbitrary values right now to set up the code and then my plan was to create a function where I could feed in the marker ids, translationvectors, rotation vectors**
#Purpose: Create a Ouput GUI for output of Translation and Rotation Vectors
#def disp_PoseVal(ids, combpairs, id_rvec, id_tvec, r_rel, t_rel)
#marker id
a=[3.5, 2.5, 8]
a = " ".join(str(elem) for elem in a)
#a_str=''.join(a)
print(a)
#tranlation
#need to add an asterix to unpack range!
b = [[*range(3)], [*range(2,5)], [*range(8,11)]]
print(b)
#rotation
c=b
rootwindow = tk.Tk()
rootwindow.title("ARUCO Marker Pose information")
rootwindow.geometry('{}x{}'.format(800, 500))
#Frame 0: IDs
frame_0 = tk.LabelFrame(rootwindow,text="Detected Marker IDs")
frame_0.pack(side="top")
#Read IDs
rids = tk.Text(frame_0)
rids.insert("end",a)
rids.grid(row=0,columnspan=2)
#rids.pack()
#Frame 1: WRT to camera
frame_1 = tk.LabelFrame(rootwindow, height=100, text="Pose Vectors: Tranlation+ Rotation")
frame_1.pack()
#Translation Vector
Trans=tk.Text(frame_1)
for x in b:
ttemp = " ".join(str(x))
print(ttemp)
print('type',type(ttemp))
Trans.insert("end", ttemp + '\n')
Trans.grid(row=1,column=0)
#Rotation Vector
Rot=tk.Text(frame_1)
for y in c:
rtemp = " ".join(str(y))
print(rtemp)
print('type', type(rtemp))
Rot.insert("end", rtemp + '\n')
Rot.grid(row=1, column=1)
rootwindow.mainloop()```

Why does this code slow down? Graphics.py?

I have some code that reads a small BMP (128x96) file and puts the RGB values into a list.
I then run a nested loop and read the RGB values in reverse from the list and draw them on the screen.
It starts quite quickly and draws the first 20 lines in a second, but progressively slows down to such an extent I've never seen it finish. It only a small 128x96 image.
I feel it's the calls to the graphics.py library, buy why, or is it something else?
I'm running this on a raspberry pi, if that's of use. Python 3.4.2
If your interested in trying you can find the supporting files here https://drive.google.com/open?id=1yM9Vn1Nugnu79l1UNShamEAGd2VWF3T4
(It's the graphics.py library I'm using and the tiny bmp file, also the actual file in question called SlowDownWhy.py)
import math
import sys
from graphics import *
from PIL import Image
# Initialise Vars for Image width n height
iw=0
ih=0
img=Image.open("ani1.bmp","r") # Open Image
iw, ih = img.size # Set image width n height
ch = int(1000/ih) # Cube height set
cw = ch # Cube width set
win = GraphWin("My Window", iw*cw, ih*ch)
win.setBackground(color_rgb(128,128,128))
#Transfer Bitmap RGB vales to csv list - 'RGBlist'
pix_val = list(img.getdata())
RGBlist = [x for sets in pix_val for x in sets]
noe = (iw * ih * 3)-3
x = iw
y = ih
for vy in list(range(ih)):
y = y-1
x = iw
for vx in list(range(iw)):
x = x-1
r=RGBlist[noe]
g=RGBlist[noe+1]
b=RGBlist[noe+2]
noe=noe-3
cx=x*cw
cy=y*ch
aPoint = Rectangle(Point(cx,cy), Point(cx+cw,cy+ch))
aPoint.setFill(color_rgb(r,g,b))
aPoint.draw(win)
It should create a window no bigger than 1000 pixels in height and start drawing the picture from the bottom right to the top left, line by line. but slows down progressively.
Ignoring the invalid syntax, this is simply because of the way graphics.py is programmed: It is not designed to handle this many objects put onto the screen. (It uses tkinter in the back-end, which will slow down with 128*96=12,288 objects). For rendering images, you should either directly integrate them or use another library, as example pygame.
To integrate it into the graphics.py program, there is the Image-class, which you overwrote with the PIL.Image-library (this is the reason why you never do import *). Look here: Importing custom images into graphics.py

How to reduce image portion with numpy.compress method ? (numpy+scikit-image)

Hi using the sample image phantom.png I'm following some operations with numpy + skimage libraries and after some modifications the last one exercise ask for:
Compress the size of center spots by 50% and plot the final image.
These are the steps I do before.
I read the image doing
img = imread(os.path.join(data_dir, 'phantom.png'))
Then apply following to make it black and white
img[np.less_equal(img[:,:,0],50)] = 0
img[np.greater_equal(img[:,:,0],51)] = 255
Took couple of slices of the image (the black spots) with given coordinates
img_slice=img.copy()
img_slice=img_slice[100:300, 100:200]
img_slice2=img.copy()
img_slice2=img_slice2[100:300, 200:300]
Now flip them
img_slice=np.fliplr(img_slice)
img_slice2=np.fliplr(img_slice2)
And put them back into an image copy
img2=img.copy()
img2[100:300, 200:300]=img_slice
img2[100:300, 100:200]=img_slice2
And this is the resulting image before the final ("compress") excersise:
Then I'm asked to "reduce" the black spots by using the numpy.compress method.
The expected result after using "compress" method is the following image (screenshot) where the black spots are reduced by 50%:
But I have no clue of how to use numpy.compress method over the image or image slices to get that result, not even close, all what I get is just chunks of the image that looks like cropped or stretched portions of it.
I will appreciate any help/explanation about how the numpy.compress method works for this matter and even if is feasible to use it for this.
You seem ok with cropping and extracting, but just stuck on the compress aspect. So, crop out the middle and save that as im and we will compress that in the next step. Fill the area you cropped from with white.
Now, compress the part you cropped out. In order to reduce by 50%, you need to take alternate rows and alternate columns, so:
# Generate a vector alternating between True and False the same height as "im"
a = [(i%2)==0 for i in range(im.shape[0])]
# Likewise for the width
b = [(i%2)==0 for i in range(im.shape[1])]
# Now take alternate rows with numpy.compress()
r = np.compress(a,im,0)
# And now take alternate columns with numpy.compress()
res = np.compress(b,r,1)
Finally put res back in the original image, offset by half its width and height relative to where you cut it from.
I guess you can slice off the center spots first by :
center_spots = img2[100:300,100:300]
Then you can replace the center spots values in the original image with 255 (white)
img2[100:300,100:300] = 255
then compress center_spots by 50% along both axes and add the resultant back to img2
the compressed image shape will be (100,100), so add to img2[150:250,150:250]
Check the below code for the output you want. Comment if you need explanation for the below code.
import os.path
from skimage.io import imread
from skimage import data_dir
import matplotlib.pyplot as plt
import numpy as np
img = imread(os.path.join(data_dir, 'phantom.png'))
img[np.less_equal(img[:,:,0],50)] = 0
img[np.greater_equal(img[:,:,0],51)] = 255
img_slice=img[100:300,100:200]
img_slice2=img[100:300,200:300]
img_slice=np.fliplr(img_slice)
img_slice2=np.fliplr(img_slice2)
img2=img.copy()
img2[100:300, 200:300]=img_slice
img2[100:300, 100:200]=img_slice2
#extract the left and right images
img_left = img2[100:300,100:200]
img_right = img2[100:300,200:300]
#reduce the size of the images extracted using compress
#numpy.compress([list of states as True,False... or 1,0,1...], axis = (0 for column-wise and 1 for row-wise))
#In state list whatever is False or 0 that particular row should will be removed from that matrix or image
#note: len(A) -> number of rows and len(A[0]) number of columns
#reducing the height-> axis = 0
img_left = img_left.compress([not(i%2) for i in range(len(img_left))],axis = 0)
#reducing the width-> axis = 1
img_left = img_left.compress([not(i%2) for i in range(len(img_left[0]))],axis = 1)
#reducing the height-> axis = 0
img_right = img_right.compress([not(i%2) for i in range(len(img_right))],axis = 0)
#reducing the width-> axis = 1
img_right = img_right.compress([not(i%2) for i in range(len(img_right[0]))],axis = 1)
#clearing the area before pasting the left and right minimized images
img2[100:300,100:200] = 255 #255 is for whitening the pixel
img2[100:300,200:300] = 255
#paste the reduced size images back into the main picture(but notice the coordinates!)
img2[150:250,125:175] = img_left
img2[150:250,225:275] = img_right
plt.imshow(img2)
numpy.compress document here.
eyes = copy[100:300,100:300]
eyes1 = eyes
e = [(i%2 == 0) for i in range(eyes.shape[0])]
f = [(i%2 == 0) for i in range(eyes.shape[1])]
eyes1 = eyes1.compress(e,axis = 0)
eyes1 = eyes1.compress(f,axis = 1)
# plt.imshow(eyes1)
copy[100:300,100:300] = 255
copy[150:250,150:250] = eyes1
plt.imshow(copy)

Matplotlib and pie/donut chart labels

If yall had seen my previous question, I am coding a Python program to evaluate the data that I collect while playing a game of Clue. I have decided to implement a GUI (tkinter) into my program to make it faster and easier to work with. One of the main window's of the GUI illustrates the different cards that I know each player has in their hand, the cards that I know must be in the middle "the murder cards", and the unknown cards that are inconclusively placed in the above categories. I have decided to implement this data through a matplotlib pie chart, five wedges for each of the previously mentioned categories.
Right now, I am unconcerned with how I implement this matplotlib function into my tkinter widget. I am solely focused on the design of the chart.
So far, I have documented the cards that are within each player's hand within a dictionary, wherein the keys are the player names, and the values are a set of cards that are in their hand. For example...
player_cards = { 'player1':{'Mustard', 'Scarlet', 'Revolver', 'Knife', 'Ballroom', 'Library'}, 'player2':{}, 'player3':{} }
So the data for the first three wedges of the pie chart will be extracted from the dictionary. For the other two wedges, the data will be stored within similarly organized sets.
After looking at the matplotlib.org website I have seen a example that sorta demonstrates what I am looking for...
with the code...
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
recipe = ["225 g flour",
"90 g sugar",
"1 egg",
"60 g butter",
"100 ml milk",
"1/2 package of yeast"]
data = [225, 90, 50, 60, 100, 5]
wedges, texts = ax.pie(data, wedgeprops=dict(width=0.5), startangle=-40)
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
kw = dict(xycoords='data', textcoords='data', arrowprops=dict(arrowstyle="-"), bbox=bbox_props, zorder=0, va="center")
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1)/2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
kw["arrowprops"].update({"connectionstyle": connectionstyle})
ax.annotate(recipe[i], xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
horizontalalignment=horizontalalignment, **kw)
ax.set_title("Matplotlib bakery: A donut")
plt.show()
However, what is lacking from this example code is... (1) The label for each wedge is a single string rather than a set of strings (which is what stores the cards in each player's hand). (2) I cannot seem to control the color of the wedges. (3) the outline of each wedge is black, rather than white which is the background color of my GUI window. (4) I want to control the exact placement of the labels. And finally (5) I need the change the font/size of the labels. Other than that the example code is perfect.
Just note that the actual size of each wedge in the pie chart will be dictated by the size of each of the five sets (so they will add up to 21).
Just in case that you all need some more substantive code to work with, here are five sets that make up the data needed for this pie chart...
player1_cards = {'Mustard', 'Plum', 'Revolver', 'Rope', 'Ballroom', 'Library'}
player2_cards = {'Scarlet', 'White', 'Candlestick'}
player3_cards = {'Green', 'Library', 'Kitchen', 'Conservatory'}
middle_cards = {'Peacock'}
unknown_cards = {'Lead Pipe', 'Wrench', 'Knife', 'Hall', 'Lounge', 'Dining Room, 'Study'}
Okay that it, sorry for a rather long post, and thanks for those of you viewing and responding :)

Right orientation with z up

how to make axis x,y,z look like
from visual import *
f = frame()
# Axis
pointer_x = arrow(frame=f, pos=(0,0,0), axis=(40,0,0), shaftwidth=1, color = color.red)
pointer_y = arrow(frame=f, pos=(0,0,0), axis=(0,40,0), shaftwidth=1, color = color.blue)
pointer_z = arrow(frame=f, pos=(0,0,0), axis=(0,0,40), shaftwidth=1, color = color.green)
# Show X,Y,Z labels
label(frame=f,pos=(40,0,0), text='X')
label(frame=f,pos=(0,40,0), text='Y')
label(frame=f,pos=(0,0,40), text='Z')
This code solved it, if there is better approach comment it.
# Show it like ECEF
f.rotate(angle=radians(-90),axis=(1,0,0),origin=(0,0,0))
f.rotate(angle=radians(180),axis=(0,1,0),origin=(0,0,0))

Resources