What's the fewest number of steps needed to draw all of the cube's vertices, without picking up the pen from the paper?
So far I have reduced it to 16 steps:
0, 0, 0
0, 0, 1
0, 1, 1
1, 1, 1
1, 1, 0
0, 1, 0
0, 0, 0
1, 0, 0
1, 0, 1
0, 0, 1
0, 1, 1
0, 1, 0
1, 1, 0
1, 0, 0
1, 0, 1
1, 1, 1
I presume it can be reduced less than 16 steps as there are only 12 vertices to be drawn
You can view a working example in three.js javascript here:
http://jsfiddle.net/kmturley/5aeucehf/show/
Well I encoded a small brute force solver for this
the best solution is with 16 vertexes
took about 11.6 sec to compute
all is in C++ (visualization by OpenGL)
First the cube representation:
//---------------------------------------------------------------------------
#define a 0.5
double pnt[]=
{
-a,-a,-a, // point 0
-a,-a,+a,
-a,+a,-a,
-a,+a,+a,
+a,-a,-a,
+a,-a,+a,
+a,+a,-a,
+a,+a,+a, // point 7
1e101,1e101,1e101, // end tag
};
#undef a
int lin[]=
{
0,1,
0,2,
0,4,
1,3,
1,5,
2,3,
2,6,
3,7,
4,5,
4,6,
5,7,
6,7,
-1,-1, // end tag
};
// int solution[]={ 0, 1, 3, 1, 5, 4, 0, 2, 3, 7, 5, 4, 6, 2, 6, 7, -1 }; // found polyline solution
//---------------------------------------------------------------------------
void draw_lin(double *pnt,int *lin)
{
glBegin(GL_LINES);
for (int i=0;lin[i]>=0;)
{
glVertex3dv(pnt+(lin[i]*3)); i++;
glVertex3dv(pnt+(lin[i]*3)); i++;
}
glEnd();
}
//---------------------------------------------------------------------------
void draw_pol(double *pnt,int *pol)
{
glBegin(GL_LINE_STRIP);
for (int i=0;pol[i]>=0;i++) glVertex3dv(pnt+(pol[i]*3));
glEnd();
}
//---------------------------------------------------------------------------
Now the solver:
//---------------------------------------------------------------------------
struct _vtx // vertex
{
List<int> i; // connected to (vertexes...)
_vtx(){}; _vtx(_vtx& a){ *this=a; }; ~_vtx(){}; _vtx* operator = (const _vtx *a) { *this=*a; return this; }; /*_vtx* operator = (const _vtx &a) { ...copy... return this; };*/
};
const int _max=16; // know solution size (do not bother to find longer solutions)
int use[_max],uses=0; // temp line usage flag
int pol[_max],pols=0; // temp solution
int sol[_max+2],sols=0; // best found solution
List<_vtx> vtx; // model vertexes + connection info
//---------------------------------------------------------------------------
void _solve(int a)
{
_vtx *v; int i,j,k,l,a0,a1,b0,b1;
// add point to actual polyline
pol[pols]=a; pols++; v=&vtx[a];
// test for solution
for (l=0,i=0;i<uses;i++) use[i]=0;
for (a0=pol[0],a1=pol[1],i=1;i<pols;i++,a0=a1,a1=pol[i])
for (j=0,k=0;k<uses;k++)
{
b0=lin[j]; j++;
b1=lin[j]; j++;
if (!use[k]) if (((a0==b0)&&(a1==b1))||((a0==b1)&&(a1==b0))) { use[k]=1; l++; }
}
if (l==uses) // better solution found
if ((pols<sols)||(sol[0]==-1))
for (sols=0;sols<pols;sols++) sol[sols]=pol[sols];
// recursion only if pol not too big
if (pols+1<sols) for (i=0;i<v->i.num;i++) _solve(v->i.dat[i]);
// back to previous state
pols--; pol[pols]=-1;
}
//---------------------------------------------------------------------------
void solve(double *pnt,int *lin)
{
int i,j,a0,a1;
// init sizes
for (i=0;i<_max;i++) { use[i]=0; pol[i]=-1; sol[i]=-1; }
for(i=0,j=0;pnt[i]<1e100;i+=3,j++); vtx.allocate(j); vtx.num=j;
for(i=0;i<vtx.num;i++) vtx[i].i.num=0;
// init connections
for(uses=0,i=0;lin[i]>=0;uses++)
{
a0=lin[i]; i++;
a1=lin[i]; i++;
vtx[a0].i.add(a1);
vtx[a1].i.add(a0);
}
// start actual solution (does not matter which vertex on cube is first)
pols=0; sols=_max+1; _solve(0);
sol[sols]=-1; if (sol[0]<0) sols=0;
}
//---------------------------------------------------------------------------
Usage:
solve(pnt,lin); // call once to compute the solution
glColor3f(0.2,0.2,0.2); draw_lin(pnt,lin); // draw gray outline
glColor3f(1.0,1.0,1.0); draw_pol(pnt,sol); // overwrite by solution to visually check correctness (Z-buffer must pass also on equal values!!!)
List
is just mine template for dynamic array
List<int> x is equivalent to int x[]
x.add(5) ... adds 5 to the end of list
x.num is the used size of list in entries
x.allocate(100) preallocate list size to 100 entries (to avoid relocations slowdowns)
solve(pnt,lin) algorithm
first prepare vertex data
each vertex vtx[i] corresponds to point i-th point in pnt table
i[] list contains the index of each vertex connected to this vertex
start with vertex 0 (on cube is irrelevant the start point
otherwise there would be for loop through every vertex as start point
_solve(a)
it adds a vertex index to actual solution pol[pols]
then test how many lines is present in actual solution
and if all lines from lin[] are drawn and solution is smaller than already found one
copy it as new solution
after test if actual solution is not too long recursively add next vertex
as one of the vertex that is connected to last vertex used
to limit the number of combinations
at the end sol[sols] hold the solution vertex index list
sols is the number of vertexes used (lines-1)
[Notes]
the code is not very clean but it works (sorry for that)
hope I did not forget to copy something
Related
I am learning Vulkan and started having a problem where no vertices would get displayed.
After analyzing my program with RenderDoc (https://renderdoc.org/builds),
I realized that the buffer containing the vertex and index information contained the rights values.
At the end of the same buffer, the indices data:
The problem is that when I check the data that is transmitted to the vertex shader, it is empty:
Here is the command buffer section where it is supposed to send the data to the shader:
VkDeviceSize indicesOffset = sizeof(Vertex) * this->nbVertices;
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, &this->vertexBuffer, offsets);
vkCmdBindIndexBuffer(commandBuffers[i], this->vertexBuffer, indicesOffset, VK_INDEX_TYPE_UINT32);
for(size_t j = 0 ; j < this->models.size() ; j++){
Model *model = this->models[j];
uint32_t modelDynamicOffset = j * static_cast<uint32_t>(this->uniformDynamicAlignment);
VkDescriptorSet* modelDescriptorSet = model->getDescriptorSet(i);
vkCmdBindDescriptorSets(this->commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, modelDescriptorSet, 1, &modelDynamicOffset);
vkCmdDrawIndexed(commandBuffers[i], this->nbIndices, 1, 0, indicesOffset, 0);
}
Also, here is how I create the vertex buffer:
void Application::createVertexBuffers() {
for(Model *model : this->models){
for(Vertex vertex : model->getVertices()){
vertices.push_back(vertex);
}
for(uint32_t index : model->getIndices()){
indices.push_back(index);
}
}
VkDeviceSize vertexBufferSize = sizeof(vertices[0]) * vertices.size();
VkDeviceSize indexBufferSize = sizeof(uint32_t) * indices.size();
this->nbVertices = vertices.size();
this->nbIndices = indices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
//To CPU
this->createBuffer(vertexBufferSize + indexBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory);
void *data;
vkMapMemory(device, stagingBufferMemory, 0, vertexBufferSize, 0, &data);
memcpy(data, vertices.data(), (size_t)vertexBufferSize);
vkUnmapMemory(device, stagingBufferMemory);
//Add the index data after vertex data
vkMapMemory(device, stagingBufferMemory, vertexBufferSize, indexBufferSize, 0, &data);
memcpy(data, indices.data(), (size_t)indexBufferSize);
vkUnmapMemory(device, stagingBufferMemory);
//To GPU
this->createBuffer(vertexBufferSize + indexBufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
this->vertexBuffer,
this->vertexBufferMemory);
this->copyBuffer(stagingBuffer, this->vertexBuffer, vertexBufferSize + indexBufferSize);
vkDestroyBuffer(device, stagingBuffer, nullptr);
vkFreeMemory(device, stagingBufferMemory, nullptr);
}
If you need more information to help me solve my problem, please tell me.
Thank you.
The indices that renderdoc reports for the render are a bit high.
You pass indicesOffset as vertexOffset in your draw command. Which is:
vertexOffset is the value added to the vertex index before indexing into the vertex buffer.
So replace that with 0 and you should get your proper vertices again.
OS : Win 10
IDE: Visual Studio 2015
Language: C++
Others: I use OpenCV 3.4
I just create a Window Form using CLR empty project,
then put on a pictureBox & three buttons.
First button: load a local image and show on the pictureBox:
pictureBox1->Image = Image::FromFile("D:/something.png");
global_mat = imread("D:/something.png", 1); // global_mat is a global Mat.
zoom_in_counter = 0; // zoom_in_counter is a global int.
Second button: zoom in the image in the pictureBox
if (zoom_in_counter < 5) // You can only enlarge the image 5 times.
{
Mat new_mat = Mat::zeros(0, 0, CV_8UC3);
resize(global_mat, new_mat, cv::Size(global_mat.cols * 2, global_mat.rows * 2));
global_mat = new_mat;
if ((pictureBox1->Width != new_mat.cols) || (pictureBox1->Height != new_mat.rows))
{
pictureBox1->Width = new_mat.cols;
pictureBox1->Height = new_mat.rows;
pictureBox1->Image = gcnew System::Drawing::Bitmap(new_mat.cols, new_mat.rows);
}
System::Drawing::Bitmap^ bmpImage = gcnew Bitmap(
new_mat.cols, new_mat.rows, new_mat.step,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,
System::IntPtr(new_mat.data)
);
Graphics^ g = Graphics::FromImage(pictureBox1->Image);
g->DrawImage(bmpImage, 0, 0, new_mat.cols, new_mat.rows);
pictureBox1->Refresh();
delete g;
zoom_in_counter++;
}
Third buttin: zoom out the image in the pictureBox
if (zoom_in_counter > 0) // You can't shrink the image.
{
Mat new_mat = Mat::zeros(0, 0, CV_8UC3);
resize(global_mat, new_mat, cv::Size(global_mat.cols * 0.5, global_mat.rows * 0.5));
global_mat = new_mat;
if ((pictureBox1->Width != new_mat.cols) || (pictureBox1->Height != new_mat.rows))
{
pictureBox1->Width = new_mat.cols;
pictureBox1->Height = new_mat.rows;
pictureBox1->Image = gcnew System::Drawing::Bitmap(new_mat.cols, new_mat.rows);
}
System::Drawing::Bitmap^ bmpImage = gcnew Bitmap(
new_mat.cols, new_mat.rows, new_mat.step,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,
System::IntPtr(new_mat.data)
);
Graphics^ g = Graphics::FromImage(pictureBox1->Image);
g->DrawImage(bmpImage, 0, 0, new_mat.cols, new_mat.rows);
pictureBox1->Refresh();
delete g;
zoom_in_counter--;
}
And then,
every I zoom in or zoom out, it works,
excludeingthe image is zoomed back to the original size.
I'll get such error message:
An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
It's really odd!
Finally, my friend figure out what's wrong,
please refer to:
https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms536315(v=vs.85).aspx
stride [in]
Type: INT
Integer that specifies the byte offset between the beginning of one scan line and the next. This is usually (but not necessarily) the number of bytes in the pixel format (for example, 2 for 16 bits per pixel) multiplied by the width of the bitmap. The value passed to this parameter must be a multiple of four.
I have a matrix of 3D points (positions), in which every column represents a 3D point expressed in a local frame at a specific time instance.
The transforms (row)vector contains the transformation matrix of the moving local frame at each time instance, i.e. the ith transformation matrix corresponds with the ith column of positions.
I want to calculate the position in the global frame (transformed) by applying the transformation matrixes to their corresponding point.
This can be done with a for loop as follows:
Eigen::Matrix<Eigen::Isometry3d, 1, Eigen::Dynamic> transforms;
Eigen::Matrix<double, 3, Eigen::Dynamic> positions, transformed;
for (int i = 0; i < positions.cols(); ++i)
transformed.col(i) = transforms(i) * positions.col(i);
I was wondering if it is possible to perform the same operation avoiding the for loop. I tried the following two approaches, but they are giving me compilation errors:
Apply the transformation columnwise:
transformed = transforms.colwise() * positions.colwise ();
error: invalid operands to binary expression (ColwiseReturnType (aka VectorwiseOp<Eigen::Matrix<Eigen::Transform<double, 3, 1, 0>, 1, -1, 1, 1, -1>, Vertical>) and ColwiseReturnType (aka VectorwiseOp<Eigen::Matrix<double, 3, -1, 0, 3, -1>, Vertical>))
Apply the transformation using arrays:
transformed = transforms.array() * positions.array().colwise ();
error: invalid operands to binary expression (ArrayWrapper<Eigen::Matrix<Eigen::Transform<double, 3, 1, 0>, 1, -1, 1, 1, -1> > and ColwiseReturnType (aka VectorwiseOp<Eigen::ArrayWrapper<Eigen::Matrix<double, 3, -1, 0, 3, -1> >, Vertical>))
Question: How can I rewrite the for loop to eliminate the (explicit) for loop?
That's not easy but doable. First you have to tell Eigen that you allow scalar products between an Isometry3D and a Vector3d and that the result is a Vector3d:
namespace Eigen {
template<>
struct ScalarBinaryOpTraits<Isometry3d,Vector3d,internal::scalar_product_op<Isometry3d,Vector3d> > {
typedef Vector3d ReturnType;
};
}
Then, you need to interpret your 3xN matrices as a vector of Vector3d using Map:
auto as_vec_of_vec3 = [] (Matrix3Xd& v) { return Matrix<Vector3d,1,Dynamic>::Map(reinterpret_cast<Vector3d*>(v.data()), v.cols()); };
Finally, you can use cwiseProduct to carry out all products at once:
as_vec_of_vec3(transformed2) = transforms.cwiseProduct(as_vec_of_vec3(positions));
Putting everything together:
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;
namespace Eigen {
template<>
struct ScalarBinaryOpTraits<Isometry3d,Vector3d,internal::scalar_product_op<Isometry3d,Vector3d> > {
typedef Vector3d ReturnType;
};
}
int main()
{
int n = 10;
Matrix<Isometry3d, 1, Dynamic> transforms(n);
Matrix<double, 3, Dynamic> positions(3,n), transformed(3,n);
positions.setRandom();
for (int i = 0; i < n; ++i)
transforms(i).matrix().setRandom();
auto as_vec_of_vec3 = [] (Matrix3Xd& v) { return Matrix<Vector3d,1,Dynamic>::Map(reinterpret_cast<Vector3d*>(v.data()), v.cols()); };
as_vec_of_vec3(transformed) = transforms.cwiseProduct(as_vec_of_vec3(positions));
cout << transformed << "\n\n";
}
This answers extends ggaels accepted answer to be compatible with versions of Eigen older than 3.3.
Pre Eigen 3.3 compatibility
ScalarBinaryOpTraits is introduced in Eigen 3.3 as a replacement for internal::scalar_product_traits. Therefore, one should use internal::scalar_product_traits before Eigen 3.3:
template<>
struct internal::scalar_product_traits<Isometry3d,Vector3d> {
enum {Defined = 1};
typedef Vector3d ReturnType;
};
I'm trying to scroll some text larger than the screen.
The docs say newpad is not limited by the screen size, but initiating it with values greater than the terminal available columns or lines fails to print anything:
newpad(LINES + 1, COLS); // fails
newpad(LINES, COLS); // works
Entire code for reference:
extern crate ncurses;
use ncurses::*;
fn main() {
initscr();
start_color();
use_default_colors();
cbreak();
noecho();
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
let pad = newpad(1000, COLS);
refresh();
let mut x = 0;
while x < 1000 {
x += 1;
wprintw(pad, &format!("Line number {}\n", x));
}
prefresh(pad, 0, 0, 0, 0, LINES, COLS);
getch();
endwin();
}
The behavior is a bit odd.
If the number of lines or rows is greater than the viewport, the last two prefresh arguments must be at most LINES - 1 and COLS - 1 respectively:
prefresh(pad, 0, 0, 0, 0, LINES - 1, COLS - 1);
If it's less, there's no need to subtract 1, as the code will work as expected.
I am using androidplot-core-0.6.1.jar and I'm modifying the SimpleXYPlotActivity example from the quickstart tutorial. I have removed some items and margin/padding, but what I want to do and can not find a way how, is to remove the space to the left of the chart. I have marked the space I want to remove with red in the image below. Can this be done?
How it looks
Red shows what I want to remove
Code from the activity:
public class MyXYPlotActivity extends Activity {
private XYPlot plot;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.simple_xy_plot_example);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
Number[] series1Numbers = {1, 8, 5, 2, 7, 4};
XYSeries series1 = new SimpleXYSeries(
Arrays.asList(series1Numbers), // SimpleXYSeries takes a List so turn our array into a List
SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value
"Foo"); // Set the display title of the series
LineAndPointFormatter series1Format = new LineAndPointFormatter();
series1Format.setPointLabelFormatter(new PointLabelFormatter());
series1Format.configure(getApplicationContext(),
R.xml.line_point_formatter_with_plf1);
plot.addSeries(series1, series1Format);
plot.setDomainValueFormat(new DecimalFormat("#"));
plot.setRangeValueFormat(new DecimalFormat("#"));
plot.setTitle("Title");
plot.setRangeBoundaries(0,10, BoundaryMode.FIXED);
int length = 30;
plot.setDomainBoundaries(1,length, BoundaryMode.FIXED);
plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 1);
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);
plot.setMarkupEnabled(true);
plot.setTicksPerRangeLabel(1);
plot.setTicksPerDomainLabel(1);
plot.getGraphWidget().setDomainLabelOrientation(90);
plot.getLayoutManager().remove(plot.getLegendWidget());
plot.getLayoutManager().remove(plot.getRangeLabelWidget());
plot.getLayoutManager().remove(plot.getDomainLabelWidget());
plot.setPlotMargins(0, 0, 0, 0);
plot.setPlotPadding(0, 0, 0, 0);
plot.setBorderStyle(Plot.BorderStyle.SQUARE, null, null);
plot.getRangeLabelWidget().setHeight(0);
plot.getRangeLabelWidget().setWidth(0);
plot.getRangeLabelWidget().setPadding(0, 0, 0, 0);
plot.getRangeLabelWidget().setMargins(0, 0, 0, 0);
plot.getDomainLabelWidget().setHeight(0);
plot.getDomainLabelWidget().setWidth(0);
plot.getDomainLabelWidget().setPadding(0, 0, 0, 0);
plot.getDomainLabelWidget().setMargins(0, 0, 0, 0);
}
}
EDIT: I found the solution:
XYGraphWidget g = plot.getGraphWidget();
g.setRangeLabelWidth(25);
g.setDomainLabelWidth(25);
This above works.
Looks like you want to reduce/remove the graphWidget's margins and/or padding. Try adding these to your code to see if it is having the desired effect...you'll obviously need to use nonzero values suited for your application:
plot.getGraphWidget().setMarginLeft(0);
plot.getGraphWidget().setPaddingLeft(0);
Also take a look at these similar questions:
Have a GraphWidget fill the entire View in AndroidPlot
How do I remove all space around a chart in AndroidPlot?