Need help because I'm new to OpenGL.
So, my task is to create a control that will draw in real time the process of cutting a workpiece on a CNC machine.
I tried to do it classically through glBegin, glEnd and everything works fine, but due to the large number of vertices, it starts to work slowly at the end. Therefore, I decided to try using VertexBufferArray () and VertexBuffer () - this also works, but in this situation I do not understand how to change the line width and its type (specifically, I have two types - a regular line and a dash-dotted line).
Here is my method where i use array
private void CreateBufferAndDraw(OpenGL GL)
{
try
{
if (shaderProgram == null && vertexBufferArray == null)
{
var vertexShaderSource = ManifestResourceLoader.LoadTextFile("vertex_shader.glsl");
var fragmentShaderSource = ManifestResourceLoader.LoadTextFile("fragment_shader.glsl");
shaderProgram = new ShaderProgram();
shaderProgram.Create(gL, vertexShaderSource, fragmentShaderSource, null);
attribute_vpos = (uint)gL.GetAttribLocation(shaderProgram.ShaderProgramObject, "vPosition");
attribute_vcol = (uint)gL.GetAttribLocation(shaderProgram.ShaderProgramObject, "vColor");
shaderProgram.AssertValid(gL);
}
if (_data == null)
{
_data = new vec3[_Points.Count];
}
else
{
if (_data.Length != _Points.Count)
{
_data = (vec3[])ResizeArray(_data, _Points.Count);
}
}
if (_dataColor == null)
{
_dataColor = new vec3[_Points.Count];
}
else
{
if (_dataColor.Length != _Points.Count)
{
_dataColor = (vec3[])ResizeArray(_dataColor, _Points.Count);
}
}
for (int i = _dataTail; i < _Points.Count; i++)
{
_data[i].y = _Points[i].Y;
_data[i].x = _Points[i].X;
_data[i].z = _Points[i].Z;
_dataColor[i] = new vec3(_Points[i].ToolColor.R / 255.0f, _Points[i].ToolColor.G / 255.0f, _Points[i].ToolColor.B / 255.0f);
}
_dataTail = _Points.Count;
// Create the vertex array object.
vertexBufferArray = new VertexBufferArray();
vertexBufferArray.Create(GL);
vertexBufferArray.Bind(GL);
// Create a vertex buffer for the vertex data.
var vertexDataBuffer = new VertexBuffer();
vertexDataBuffer.Create(GL);
vertexDataBuffer.Bind(GL);
vertexDataBuffer.SetData(GL, attribute_vpos, _data, false, 3);
// Now do the same for the colour data.
var colourDataBuffer = new VertexBuffer();
colourDataBuffer.Create(GL);
colourDataBuffer.Bind(GL);
colourDataBuffer.SetData(GL, attribute_vcol, _dataColor, false, 3);
// Unbind the vertex array, we've finished specifying data for it.
vertexBufferArray.Unbind(GL);
// Bind the shader, set the matrices.
shaderProgram.Bind(GL);
// Set matrixs for shader program
float rads = (90.0f / 360.0f) * (float)Math.PI * 2.0f;
mat4 _mviewdata = glm.translate(new mat4(1f), new vec3(_track_X, _track_Y, _track_Z));
mat4 _projectionMatrix = glm.perspective(rads, (float)this.trackgl_control.Height / (float)this.trackgl_control.Width, 0.001f, 1000f);
mat4 _modelMatrix = glm.lookAt(new vec3(0, 0, -1), new vec3(0, 0, 0), new vec3(0, -1, -1));
shaderProgram.SetUniformMatrix4(GL, "projectionMatrix", _projectionMatrix.to_array());
shaderProgram.SetUniformMatrix4(GL, "viewMatrix", _modelMatrix.to_array());
shaderProgram.SetUniformMatrix4(GL, "modelMatrix", _mviewdata.to_array());
// Bind the out vertex array.
vertexBufferArray.Bind(GL);
GL.LineWidth(5.0f);
// Draw the square.
GL.DrawArrays(OpenGL.GL_LINE_STRIP, 0, _dataTail);
// Unbind our vertex array and shader.
vertexBufferArray.Unbind(GL);
shaderProgram.Unbind(GL);
}
catch (Exception ex) { MessageBox.Show("CreateAndPlotData" + "\n" + ex.ToString()); }
}
This is what i get
As you see, all lines have the same width. So, my question is: Does anyone know what i can do about this?
And another one: what if i need to show a point of the current location of the instrument? I should create another array with one Vertex ?
p.s. Sry for my english, and here here is picture of what i get with glBegin/glEnd
No tag spamming intended.
I'm doing this project partially guided by tutorials in C ++ and C #. Therefore, if someone knows how to do this in C ++, then in this case I will just try to implement the same in C#.
I am trying to create a collage of images with Magick.net. I am using MagickImageCollection and .Mosaic(). I tried already a few of the functions provided by MagickImageCollection but all of them increase the brightness of the final image. The only one that worked so far was .Montage(), but with .Montage() I don't get the padding right.
How do I need to configure it, that .Mosaic() keeps the colors as they are in the single images?
using (var collection = new MagickImageCollection())
{
for (var i = 0; i < thumbnailCount; i++)
{
var image = new MagickImage(TempThumbPathFor(i));
image.Resize(256, 0);
var posX = (image.Page.Width + margin) * (i % 2);
var posY = (image.Page.Height + margin) * (i / 2);
image.Page = new MagickGeometry(posX, posY, new Percentage(100), new Percentage(100));
collection.Add(image);
}
using (var result = collection.Mosaic())
{
result.Write(newPath);
}
}
Collage of images with washed out colors:
For more information why the problem occurred in the first place have a look at this issue: GitHub
Figured out how to create a montage with padding and proper color. Couldn't get it to work with .Mosaic but with .Montage().
The important part is to add the margin to X, Y, Height and Width and call .Trim() on the final image. You will most likely have to play around a bit with the margin to get a balanced looking padding between the images, but other than that it works quite well.
const int margin = 2;
MagickGeometry geometry = null;
using (var collection = new MagickImageCollection())
{
for (var i = 0; i < thumbnailCount; i++)
{
var image = new MagickImage(TempThumbPathFor(i));
image.Resize(256, 0);
collection.Add(image);
if (i == 0)
{
geometry = image.BoundingBox;
geometry.X += margin;
geometry.Width += margin;
geometry.Y += margin;
geometry.Height += margin - 1;
}
}
using (var result = collection.Montage(new MontageSettings()
{
Geometry = geometry,
BackgroundColor = MagickColor.FromRgb(255, 255, 255)
}))
{
result.Trim();
result.Write(newPath);
}
}
The system I am working on stamps PDF's with certain information. It does this by creating a lime green text box in the top right corner of the document. It then draws a certain string on top of the green space. This works for thousands of PDFs but for one the text is invisible even though the box is drawn. I can still select the text and copy it to something else, but it is invisible in the PDF.
Unfortunately, I cannot share the PDF but it is a PDF 1.4. What would cause this?
The code for stamping:
private static XGraphics drawString(XGraphics xgr, PdfPage page, string printString, int pageNumber = 0)
{
XFont font = new XFont("Verdana", 10, XFontStyle.BoldItalic);
var textSize = xgr.MeasureString(printString, font);
var width = textSize.Width;
var height = textSize.Height;
double xMin = 0;
double yMin = 0;
if (page.Rotate == 90)
{
xMin = page.Height - textSize.Width;
var state = xgr.Save();
xgr.DrawRectangle(XBrushes.LimeGreen, xMin, yMin, width, height);
xgr.Restore(state);
xgr.DrawString(printString, font, XBrushes.Black, new XRect(0, 0, page.Height, page.Width), topRight());
}
else
{
xMin = page.Width - textSize.Width;
var state = xgr.Save();
xgr.DrawRectangle(XBrushes.LimeGreen, xMin, yMin, width, height);
xgr.Restore(state);
xgr.DrawString(printString, font, XBrushes.Black, new XRect(0, 0, page.Width, page.Height), topRight());
}
return xgr;
}
private static XStringFormat topRight()
{
XStringFormat format = new XStringFormat();
format.Alignment = XStringAlignment.Far;
format.LineAlignment = XLineAlignment.Near;
return format;
}
I have tried using Dipose() on xgr and reinitialising it before each of its draw actions. I have tried saving and restoring the state of xgr between draw actions as seen in the code. I have tried various fonts and font sizes with no luck either.
Let me know what metadata about the PDF is relevant and I will share that.
Using PdfSharp 1.5 GDI, I have been having this issue as well. Some pdfs would be ok, others would have 1 or 2 pages that were ok, and then others would have no pages that were ok. Text could be selected, but text could not be seen. Changing the renderMode fixes the issue.
In PdfGraphicsState.cs, there is a condition to check if _realizedRenderingMode != renderMode. However, _realizedRenderingMode and renderMode is 0 by default, so it never enters codeblock, nor does it look like there is a method for changing the renderMode unless you change the font to italic, bold, strikeout, or underline:
int _realizedRenderingMode; // Reference: TABLE 5.2 Text state operators / Page 398
public void RealizeFont(XFont font, XBrush brush, int renderingMode)
{
const string format = Config.SignificantFigures3;
// So far rendering mode 0 (fill text) and 2 (fill, then stroke text) only.
RealizeBrush(brush, _renderer._colorMode, renderingMode, font.Size); // _renderer.page.document.Options.ColorMode);
// Realize rendering mode.
if (_realizedRenderingMode != renderingMode)
{
_renderer.AppendFormatInt("{0} Tr\n", renderingMode);
_realizedRenderingMode = renderingMode;
}
Removing the condition would suffice in fixing the issue, but the renderingMode seems to only be needed 1 time for the BT (Begin Text) in XGraphicsPdfRenderer.cs.
internal void BeginTextMode()
{
if (_streamMode != StreamMode.Text)
{
_streamMode = StreamMode.Text;
_content.Append("BT\n");
// Text matrix is empty after BT
_gfxState.RealizedTextPosition = new XPoint();
_gfxState.ItalicSimulationOn = false;
}
}
So, I ended up modifying XGraphics to include XGraphicsPdfRendererOptions and passing the variable to the various methods so it can be changed regardless of location:
private XGraphicsPdfRendererOptions _renderOptions { get; set; }
public XGraphicsPdfRendererOptions RenderOptions
{
get
{
if (_renderOptions == null)
{
_renderOptions = new XGraphicsPdfRendererOptions();
}
return _renderOptions;
}
set
{
_renderOptions = value;
}
}
Keep in mind that the renderMode is originally based on whether the font is italic, bold, strikeout, or underline, which I don't really see how those relate to renderMode, in XGraphicsPdfRenderer.cs:
//bool bold = (font.Style & XFontStyle.Bold) != 0;
//bool italic = (font.Style & XFontStyle.Italic) != 0;
bool italicSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.ItalicSimulation) != 0;
bool boldSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.BoldSimulation) != 0;
bool strikeout = (font.Style & XFontStyle.Strikeout) != 0;
bool underline = (font.Style & XFontStyle.Underline) != 0;
Realize(font, brush, boldSimulation ? 2 : 0);
Class and enum:
public class XGraphicsPdfRendererOptions
{
public XGraphicsPdfRenderMode RenderMode { get; set; }
public bool IncludeRenderModeForPage { get; set; }
public bool IncludeRenderModeForObject { get; set; }
}
public enum XGraphicsPdfRenderMode
{
Text_Render_Mode_Fill = 0,
Text_Render_Mode_Stroke = 1,
Text_Render_Mode_Fill_Stroke = 2,
Text_Render_Mode_Invisible = 3
}
Usage:
gfx.RenderOptions = new XGraphicsPdfRendererOptions() { RenderMode = XGraphicsPdfRenderMode.Text_Render_Mode_Fill, IncludeRenderModeForPage = true };
https://github.com/zaryk/PDFsharp
I've happened across this issue a couple of times now on separate projects. I found that the simplest fix is to dispose of the XGraphics object and then simply re-instantiate it using your current PdfPage instance.
PdfSharp.Pdf.PdfPage Page = Document.AddPage();
PdfSharp.Drawing.XGraphics gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(Page);
//Build your pdf here until transparency issue occurs
//Issue has occured so re-instantiate gfx object
gfx.Dispose();
gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(Page);
//Continue as normal
Below is the code I use to hide something in document and edit the document and again save. Let me know if it helps you
private PdfDocument FormatPdfDocument(PdfDocument document, List<string> packingTypes, string carrierName)
{
XFont PackingTypeFont = new XFont("Calibri", 10, XFontStyle.Bold);
var i = 0;
foreach (PdfPage page in document.Pages)
{
using (var gfx = XGraphics.FromPdfPage(page))
{
var packingType = packingTypes.ElementAtOrDefault(i++) ?? "PackingType Not Found";
if (carrierName == "xxxx")
{
var packingTypeBounds = new XRect(64, 62, 200, 12);
gfx.DrawRectangle(XBrushes.White, packingTypeBounds);
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
var logoBounds = new XRect(0, 0, 130, 50);
gfx.DrawRectangle(XBrushes.White, logoBounds);
}
else if (carrierName == "yyyy")
{
var packingTypeBounds = new XRect(200, 0, 200, 12);
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
}
else if (carrierName == "zzzz")
{
var packingTypeBounds = new XRect(410, 20, 200, 12);
var state = gfx.Save();
gfx.RotateAtTransform(90, new XPoint { X = 410, Y = 20 });
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
gfx.Restore(state);
}
}
}
return document;
}
This works fine for me till date without any issues
Some possible causes to answer your question "What would cause this?":
Are you using the latest version PDFsharp 1.50 beta 3b?
IIRC there is a bug in 1.32 that can lead to unexpected behavior because some properties are not reset.
Since you see the rectangle this might be the cause.
Since you see the rectangle, this probably does not apply:
There are two ways of modifying existing pages: "append" and "prepend". Your code snippet does not show how you do it.
With "prepend" your lime rectangle could be hidden under a white filled rectangle. Watch the PDF in Adobe Reader with active Transparency Grid and check that you see the grid where the rectangle should be.
Since you see the rectangle, this probably does not apply:
Maybe your text goes to the wrong position. Check the MediaBox and CropBox settings of the PDF page you are modifying. Normally pages start at (0,0), but you cannot be sure.
Locate your text in the PDF file and compare the text position with MediaBox and CropBox.
It could be an unknown bug in PDFsharp. If you do not find a PDF that allows to replicate the issue which you can share then it will be very difficult to fix the bug. But maybe one of the options above leads to success.
So I managed to find a way to make it work. I ran the stamping process twice on the document and it worked as expected. Fortunately, stamping twice does not affect regular documents which actually work normally.
Still an issue in 1.50 for some PDF files...
As a workaround, I create a PNG file using System.Drawing.DrawString.
Bitmap bmp = new Bitmap((int)p.Width.Point, 30);
Graphics gra = Graphics.FromImage(bmp);
gra.DrawString("Hello world", new Font("Verdana", 20), Brushes.Red, new PointF(0, 0));
bmp.Save("test.png", System.Drawing.Imaging.ImageFormat.Png);
Then I use XGraphics.DrawImage from this PNG file.
XGraphics xg = XGraphics.FromPdfPage(p, XGraphicsPdfPageOptions.Append);
XImage xi = XImage.FromFile("test.png");
// Add it at the bottom of the page
xg.DrawImage(xi, 0, p.Height-32, p.Width, 30);
This always ends up on top.
I'm trying to add a dashline for a graph but it keeps coming out as a solid line. How can I fix that?
static public NPlot.Bitmap.PlotSurface2D getDashlineGraph(int itemID, int width, int height)
{
NPlot.Bitmap.PlotSurface2D plot = new NPlot.Bitmap.PlotSurface2D(width, height);
plot.BackColor = Color.WhiteSmoke;
plot.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
LinePlot TotaldashLine = new LinePlot(straightline, timer);
float[] pattern = { 5.0f, 10.0f };
TotaldashLine.Pen = new Pen(Color.Green,0.5f);
TotaldashLine.Pen.DashPattern = pattern;
plot.Add(TotaldashLine, NPlot.PlotSurface2D.XAxisPosition.Bottom, NPlot.PlotSurface2D.YAxisPosition.Right, 100);
Grid oGrid = new Grid();
oGrid.HorizontalGridType = Grid.GridType.Coarse;
oGrid.VerticalGridType = Grid.GridType.Coarse;
plot.Add(oGrid);
plot.Refresh();
return plot;
}
You need to specify that you want your Pen to draw dashed using Pen.DashStyle:
TotaldashLine.Pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
Once you have done that, you can additionally customize the lengths of dashes and gaps using the Pen.DashPattern as you have.
I'm trying to create 1 complex composite shape on an InkCanvas, but I must be doing something wrong, as what I was expecting to happen, is not. I've tried several different incarnations of accomplishing this.
So I have this method.
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
Stroke stroke = e.Stroke;
// Close the "shape".
StylusPoint firstPoint = stroke.StylusPoints[0];
stroke.StylusPoints.Add(new StylusPoint() { X = firstPoint.X, Y = firstPoint.Y });
// Hide the drawn shape on the InkCanvas.
stroke.DrawingAttributes.Height = DrawingAttributes.MinHeight;
stroke.DrawingAttributes.Width = DrawingAttributes.MinWidth;
// Add to GeometryGroup. According to http://msdn.microsoft.com/en-us/library/system.windows.media.combinedgeometry.aspx
// a GeometryGroup should work better at Unions.
_revealShapes.Children.Add(stroke.GetGeometry());
Path p = new Path();
p.Stroke = Brushes.Green;
p.StrokeThickness = 1;
p.Fill = Brushes.Yellow;
p.Data = _revealShapes.GetOutlinedPathGeometry();
selectionInkCanvas.Children.Clear();
selectionInkCanvas.Children.Add(p);
}
But this is what I get:
http://img72.imageshack.us/img72/1286/actual.png
So where am I going wrong?
TIA,
Ed
The problem is that the Geometry returned by stroke.GetGeometry() is a path around the stroke, so the area you're filling with yellow is just the middle of the stroke. You can see this more clearly if you make the lines thicker:
_revealShapes.Children.Add(stroke.GetGeometry(new DrawingAttributes() { Width = 10, Height = 10 }));
You can do what you want if you convert the list of stylus points to a StreamGeometry yourself:
var geometry = new StreamGeometry();
using (var geometryContext = geometry.Open())
{
var lastPoint = stroke.StylusPoints.Last();
geometryContext.BeginFigure(new Point(lastPoint.X, lastPoint.Y), true, true);
foreach (var point in stroke.StylusPoints)
{
geometryContext.LineTo(new Point(point.X, point.Y), true, true);
}
}
geometry.Freeze();
_revealShapes.Children.Add(geometry);