Default texturecoordinates on MeshGeometry3D - c#

Is it possible(Without looping through all texturecoordinates) to set them as 0 as default?
I am creating a linear gradient ImageBrush
var colorBitmap = GetColorsBitmap(gradient.ToList()); // Create Colors from gray to my selected color
ImageBrush ib = new ImageBrush(colorBitmap)
{
ViewportUnits = BrushMappingMode.Absolute,
Viewport = new Rect(0, 0, 1, 1) // Matches the pixels in the bitmap.
};
myModel.Material = new DiffuseMaterial(ib);
Then depending on a condition I customize some of the Texturecoordinates like this, where colorvalue is the distance from an object:
var mesh = (MeshGeometry3D) myModel.Geometry;
//In a loop, depending on the distance I set a colorValue
mesh.TextureCoordinates[count] = new Point(colorValue, 0);
I want every texturecoordinate to default to a Point(0,0) but if I have to loop through all 1.2 million of them takes too much time. So is there a way to setup my ImageBrush so that they default to 0,0 or something similiar?

Related

how can i extract region to mesh or solid using eyeshot?

i'm trying to create navigation mesh on autodesk naviswork using Eyeshot.
convert vertices and IndexTriangle to vertice triangles, after create solid using Solid.FromTriangles().
var solidList = new List();
var Solid = Solid.FromTriangles(item.vertices, item.triangles);
but it doesn't work to boolean operators at i thought.
so i want extract region for using boolean operators.
how can i extract region to mesh or solid (or vertices triangles)?
It is very easy to do. You have to make sure your region vertese are sorted otherwise you might have some issues with it down the line but it's a simple parameter. If the shape isn't hollow here is an example :
// the verteses has to be in order and direction doesn't matter here
// i simply assume it's drawn on X/Y for the purpose of the example
public static Region CreateRegion(List<Point3D> verteses)
{
// create a curve list representing
var curves = new List<ICurve>();
// for each vertex we add them to the list
for (int i = 1; i < verteses.Count; i++)
{
curves.Add(new Line(verteses[i - 1], verteses[i]));
}
// close the region
curves.Add(new Line(verteses.Last(), verteses[0]));
return new Region(new CompositeCurve(curves, true), Plane.XY, true);
}
// this extrude in Z the region
public static Solid CreateSolidFromRegion(Region region, double extrudedHeight)
{
// extrude toward Z by the amount
return region.ExtrudeAsSolid(new Vector3D(0, 0, 1), extrudedHeight);
}
a simple example of creating a cube of 10 by 10 by 10 from vertese (there are much easier method to make a cube but for sake of simplicity i'll make a cube)
// create the 4 verteses
var verteses = new List<Point3D>()
{
new Point3D(0, 0, 0),
new Point3D(10, 0, 0),
new Point3D(10, 10, 0),
new Point3D(0, 10, 0)
}
// create the region on the XY plane using the static method
var region = CreateRegion(verteses);
// extrude the region in Z by 10 units
var solid = CreateSolidFromRegion(region, 10d);

How does one set an image as or along a chart axis?

I am trying to use a colored spectrum strip as an axis for a chart. The idea is to match the color on the image with its associated wavelength along the x-axis at the bottom. The strip needs to change in size to match changes of the chart area and expand and contract sections to match scroll-zooming in the chart area.
I have tried using image annotations but as the chart area changes, the annotation dimensions remain fixed. Also, the scroll zooming that focuses in on mouse position obviously has no effect on the annotation.
The approach that came closest was using the image as a background for the chart area. This automatically scaled the image as the chart area changed but scroll-zooming has no effect on the background image. Also, it would be ideal to have the background clear so as to avoid obscuring data plot points. I can edit the image to have a large transparent section and only a colored strip at the bottom but even then, that strip could obscure lower intensity data points.
Spectrum as annotation and background:
Annotation not scaling, background scales well:
Both annotation and background not scaling with zooming:
This is a nice idea.
The simplest way is to draw the image in a Paint event of the Chart, maybe PrePaint.
Let's go to work.. We will use the DrawImage overload that allows us zooming as well as cropping. For this we need two rectangles.
The first challenge is to always get the correct target rectangle.
For this we need to convert the InnerPlotPosition from relative positions to absolute pixels.
These two functions will help:
RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
RectangleF CAR = CA.Position.ToRectangleF();
float pw = chart.ClientSize.Width / 100f;
float ph = chart.ClientSize.Height / 100f;
return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}
RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
RectangleF CArp = ChartAreaClientRectangle(chart, CA);
float pw = CArp.Width / 100f;
float ph = CArp.Height / 100f;
return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y,
pw * IPP.Width, ph * IPP.Height);
}
With these numbers setting the destination rectangle is as simple as:
Rectangle tgtR = Rectangle.Round(new RectangleF(ipr.Left, ipr.Bottom - 15, ipr.Width, 15));
You can chose a height as you like..
The next challenge is the source rectangle.
Without zooming it would simply be:
Rectangle srcR = new Rectangle( 0, 0, bmp.Width, bmp.Height);
But for zooming and panning we need to scale it; for this we can use the x-axis and the ScaleView's Minimum and Maximum values.
We calculate factors for the first and last spot on the axis:
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
now we get the source rectangle maybe like this:
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Let's put it together:
private void chart_PrePaint(object sender, ChartPaintEventArgs e)
{
// a few short names
Graphics g = e.ChartGraphics.Graphics;
ChartArea ca = chart.ChartAreas[0];
Axis ax = ca.AxisX;
// pixels of plot area
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
// scaled first and last position
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
// actual drawing with the zooming overload
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imagePath))
{
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Rectangle tgtR = Rectangle.Round(
new RectangleF(ipr.Left , ipr.Bottom - 15, ipr.Width, 15));
g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
}
}
A few notes:
Of course I would recomend to use an Image resource instead of always loading from disk!
The Drawing will always overlay the data points and also the grids. You can either..
choose a different minimum to make room
make the image smaller
move it below the x-axis labels
make the image semi-transparent
make the x-axis so fat that it can hold the image strip : ax.LineWidth = 10
For the latter solution you would want to offset the y-position depending on the zoom state. Quick and dirty: int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);. To avoid black stripes also make the axis Transparent or chart.BackColor..
Update:
You can also revert to using a StripLine. It can scale its BackgroundImage and you would have to create a suitable image whenever changing the scaleview, i.e. when zooming or panning. For this much of the above code would be used to create the new images. See this post for examples of adding and replacing varying NamedImage to a Chart! (The relevant portion is close to the end about the marker images!)
In fact I found that way to be the best solution and have added a second answer.
Alternative and recommended solution:
I dabbled with the last option I mentioned in my other answer and found it to be rather nice; it is similarily extensive, so I decided to post a second answer.
The idea is to use a StripLine with just the right BackgroundImage.
The advantage is that is will display nicely under all chart elements and never draw over the axis, grid, datapoints or conflict with the zoom tools.
Since the StripLine must be updated repeatedly I put it in a function:
Here is the function; it makes use of the same two helper functions to calculate pixel positions as the other answer does..:
void updateStripLine(Chart chart, ChartArea ca, string name)
{
// find our stripline; one could pass in a class level variable as well
StripLine sl = ca.AxisY.StripLines.Cast<StripLine>()
.Where(s => s.Tag.ToString() == name).FirstOrDefault();
if (sl != null) // either clean-up the resources..
{
var oldni = chart.Images.FindByName(name);
if (oldni != null)
{
oldni.Image.Dispose();
chart.Images.Remove(oldni);
oldni.Dispose();
}
}
else // or, create the line
{
sl = new StripLine();
sl.Tag = name;
ca.AxisY.StripLines.Add(sl);
}
ca.RecalculateAxesScale();
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
Bitmap b0 = (Bitmap)chart.Images["spectrum"].Image;
int x = (int)(b0.Width * f1);
int xx = (int)(b0.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, b0.Height);
Rectangle tgtR = Rectangle.Round(new RectangleF(0,0, ipr.Width , 10));
// create bitmap and namedImage:
Bitmap bmp = new Bitmap( tgtR.Width, tgtR.Height);
using (Graphics g = Graphics.FromImage(bmp))
{ g.DrawImage(b0, tgtR, srcR, GraphicsUnit.Pixel); }
NamedImage ni = new NamedImage(name, bmp);
chart.Images.Add(ni);
sl.BackImageWrapMode = ChartImageWrapMode.Scaled;
sl.StripWidth = ay.PixelPositionToValue(0) - ay.PixelPositionToValue(12);
sl.Interval = 100; // make large enough to avoid another sLine showing up
sl.IntervalOffset = 0;
sl.BackImage = name;
}
Much of the comments and links apply, especially wrt to the NamedImage we use for the StripLine.
A few more notes:
I use one of the (four) axis conversion functions, PixelPositionToValue to calculate a pixel height of 12px; the StripLine takes values, so I use two pixel values to get the right difference value.
To identify the StripLine I use the Tag property. Of course the Name property would be much more natural, but it is read-only. No idea why?!
The function is called from the AxisViewChanged, the Resize event and also the the PrePaint event; this makes sure it will always be called when needed. To avoid invalid calls from the PrePaint there I do it like this: if (ay.StripLines.Count == 0) updateStripLine(chart, ca, "sl"); Of course you should adapt if you use other StripLines on this axis..
The code makes use of the same image as before; but I have put it into a first NamedImage called spectrum. This would be an option in the 1st answer as well.
NamedImage spectrum = new NamedImage("spectrum", Bitmap.FromFile(imagePath);
chart.Images.Add(spectrum);
It also makes sure to dispose of the old images properly, I hope..

Array random color not generating

I'm facing a problem where when I declare array of random colors. It shows random colors in a particle system on game start, but every time the game starts it shows white. I don't know why it happens, I didn't set white in my array.
public class A : MonoBehaviour
{
Color[] colors = {
new Color (170, 7, 107),
new Color (58, 96, 115),
new Color(81, 99, 149),
new Color(71, 118, 231)
};
void start()
{
GetComponent<ParticleSystem>().startColor = colors[Random.Range(0, colors.Length)];
}
In Unity, a color's ARGB components range between 0.0 to 1.0. So anything >1 will be considered 1 and so all the colors are white naturally.
To convert the colors, divide each component by 255. You can either do this yourself or leave it to the code itself. Also, don't forget to cast as float. Credit to #Masih Akbari for reminding me about this.
So, it should be :
Color[] colors = {
new Color (170f/255, 7f/255, 107f/255),
new Color (58f/255, 96f/255, 115f/255),
new Color(81f/255, 99f/255, 149f/255),
new Color(71f/255, 118f/255, 231f/255)
}
The reason for this is that colours are normalised in Unity. You have to divide each float you've set by 255 to get the actual value, e.g.
Color[] colors = {
new Color (170/255, 7/255, 107/255),
new Color (58/255, 96/255, 115/255),
new Color(81/255, 99/255, 149/255),
new Color(71/255, 118/255, 231/255)
};
Your Color values must be between 0 to 1. everything after 1 is considered white.
Don't forget to cast your number as a float

How do I implement this "color difference" algorithm?

As a follow up to this question: (How can I draw legible text on a bitmap (Winforms)?), I'm drawing legible but small text on top of a bitmap by calculating the "average" color beneath the text, and choosing an appropriately contrasting color for the text.
I've stolen Till's code from https://stackoverflow.com/a/6185448/3784949 for calculating "average" bmp color. Now I'm looking at the "color difference" algorithm suggested by http://www.w3.org/TR/AERT#color-contrast.
This suggests that I need to make my color brightness at least 125 "units" greater, and my color difference at least 500 units greater, where brightness and difference are calculated like this:
Color brightness is determined by the following formula:
((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
Color difference is determined by the following formula:
(maximum (Red value 1, Red value 2) - minimum (Red value 1, Red value 2)) + (maximum (Green value 1, Green value 2) - minimum (Green value 1, Green value 2)) + (maximum (Blue value 1, Blue value 2) - minimum (Blue value 1, Blue value 2))
How do I implement this? I can set my color by ARGB (I believe, it's a label foreground color); but how do I calculate how much to change each individual value to achieve the difference being required here? I'm not familiar with the math required to break the "difference" units down into their component parts.
As an example, my "average" for one bitmap is: Color [A=255, R=152, G=138, B=129]. How do I "add" enough to each part to achieve the two differences?
EDIT: To be specific, my confusion lies here:
it looks like I need to add to three separate values (R,G,B) to achieve two different goals (new RGB adds up to original plus 125, new RGB adds up to original plus 500
it looks like I may need to "weight" my added brighness values to add more to G than R than B.
I have no idea how to address #1. And I'm not positive I'm correct about #2.
EDIT: Proposed solution
I'm currently experimenting with this:
private Color GetContrastingFontColor(Color AverageColorOfBitmap,
List<Color> FavoriteColors)
{
IEnumerable<Color> AcceptableColors =
(IEnumerable<Color>)FavoriteColors.Where(clr =>
(GetColorDifferenceAboveTarget(AverageColorOfBitmap, clr, (float)200) > 0)
&& (GetBrightnessAboveTarget(AverageColorOfBitmap, clr, (float).125) > 0))
.OrderBy(clr => GetColorDifferenceAboveTarget(
AverageColorOfBitmap, clr, (float)200));
return AcceptableColors.DefaultIfEmpty(Color.Aqua).First();
}
It's a good framework, but I need to work on selecting the "best" candidate from the list. Right now it's just returning "the qualifying color with the greatest color difference that meets the brightness criteria". However, this allows me to modify the float values (W3's "500 color difference required" is complete crap, zero KnownColors qualify) and experiment.
Support code:
private float GetBrightnessAboveTarget(Color AverageColorOfBitmap,
Color proposed, float desiredDifference)
{
float result = proposed.GetBrightness() - AverageColorOfBitmap.GetBrightness();
return result - desiredDifference;
}
private float GetColorDifferenceAboveTarget(Color avg, Color proposed,
float desiredDifference)
{
float r1 = Convert.ToSingle(MaxByte(Color.Red, avg, proposed));
float r2 = Convert.ToSingle(MinByte(Color.Red, avg, proposed));
float r3 = Convert.ToSingle(MaxByte(Color.Green, avg, proposed));
float r4 = Convert.ToSingle(MinByte(Color.Green, avg, proposed));
float r5 = Convert.ToSingle(MaxByte(Color.Blue, avg, proposed));
float r6 = Convert.ToSingle(MinByte(Color.Blue, avg, proposed));
float result = (r1 - r2) + (r3 - r4) + (r5 - r6);
return result - desiredDifference;
}
private byte MaxByte(Color rgb, Color x, Color y)
{
if (rgb == Color.Red) return (x.R >= y.R) ? x.R : y.R;
if (rgb == Color.Green) return (x.G >= y.G) ? x.G : y.G;
if (rgb == Color.Blue) return (x.B >= y.B) ? x.B : y.B;
return byte.MinValue;
}
private byte MinByte(Color rgb, Color x, Color y)
{
if (rgb == Color.Red) return (x.R <= y.R) ? x.R : y.R;
if (rgb == Color.Green) return (x.G <= y.G) ? x.G : y.G;
if (rgb == Color.Blue) return (x.B <= y.B) ? x.B : y.B;
return byte.MinValue;
}
This is more an answer to the original question. I call it a homemeade outline.
Using transparency plus the maximum and minimum brightness you can get (white&black) it creates good contrast, at least it looks pretty good on my screen.
It is a mixture of shadowing and transparency. I have subtracted a little from the red component to get the aqua you thought about..
It is creating first a darker version of the background by printing the text 1 pixel up left and the 1 pixel down right. Finally it prints a bright version on top of that. Note that it is not really using black and white because with its semi-transparent pixels the hue really it that of each background pixel.
For an actual printout you will have to experiment, especially with the font but also with the two transparencies!
Also you should maybe switch between white on a black shadow and black on a white highlight, depending on the brightness of the spot you print on. But with this homemeade outline it really will work on both dark and bright backgrounds, it'll just look a little less elegant on a bright background.
using (Graphics G = Graphics.FromImage(pictureBox1.Image) )
{
Font F = new Font("Arial", 8);
SolidBrush brush0 = new SolidBrush(Color.FromArgb(150, 0, 0, 0))
SolidBrush brush1 = new SolidBrush(Color.FromArgb(200, 255, 255, 222))
G.DrawString(textBox1.Text, F, brush0 , new Point(x-1, y-1));
G.DrawString(textBox1.Text, F, brush0 , new Point(x+1, y+1));
G.DrawString(textBox1.Text, F, brush1, new Point(x, y));
}
Edit: This is called from a button click but really should be in the paint event.
There the Graphics object and its using block G would be replaced by simply the e.Graphics event parameter..
I noticed that you are using a 'transparent' label to display the data to avoid the details of Graphics.DrawString and the Paint event.
Well that can be done and the result looks rather similar:
string theText ="123 - The quick brown fox..";
Label L1, L2, L3;
pictureBox1.Controls.Add(new trLabel());
L1 = (trLabel)pictureBox1.Controls[pictureBox1.Controls.Count - 1];
L1.Text = theText;
L1.ForeColor = Color.FromArgb(150, 0, 0, 0);
L1.Location = new Point(231, 31); // <- position in the image, change!
L1.Controls.Add(new trLabel());
L2 = (trLabel)L1.Controls[pictureBox1.Controls.Count - 1];
L2.Text = theText;
L2.ForeColor = Color.FromArgb(150, 0, 0, 0);
L2.Location = new Point(2, 2); // do not change relative postion in the 1st label!
L2.Controls.Add(new trLabel());
L3 = (trLabel)L2.Controls[pictureBox1.Controls.Count - 1];
L3.Text = theText;
L3.ForeColor = Color.FromArgb(200, 255, 255, 234);
L3.Location = new Point(-1,-1); // do not change relative postion in the 2nd label!
However you will note that due to the impossiblity of having really transparent controls in Winforms we need a little extra effort. You probably use a label subclass like this:
public partial class trLabel : Label
{
public trLabel()
{
SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
Visible = true;
AutoSize = true;
}
}
This seems to work. But in reality it only seems that way, because upon creation each label gets a copy of its current background from its parent. Which never gets updated. Which is why I have to add the 2nd & 3rd label not to the picturebox I display the image in, but to the 1st and 2nd
'transparent' label respectively.
There simply is not real transparency between Winforms controls unless you draw things yourself.
So the DrawString solution is not really complicated. And it gives you the bonus of allowing you to twist several properties of the Graphics object like Smoothingmode, TextContrast or InterpolationMode
Short suggestion: Just use black or white.
The algorithms are giving you a passing criteria, but not an algorithm for determining what colors meet that criteria. So, you will have to create such an algorithm. A naive algorithm would be to loop through every possible color, and calculate the color difference, then see if the difference is greater than 125, and if so you have a good color to use. Better, you could search for the color with the maximum difference.
But that's foolish - if I gave you the color R=152, G=138, B=129 - what do YOU think is a very good color to contrast that with? Just by gut, I'm gonna guess 0,0,0. I picked a color with the farthest possible R value, G value, and B value. If you gave me the color 50,200,75 I'd pick R=255, G=0, B=255. Same logic. So my algorithm is if R<128 choose R = 255, else choose R = 0. Same thing for G, and B.
Now that algorithm only picks RGB values that are 0 or 255. But if you don't like that, now you need a mathematical definition for what is "pretty" and I'll leave you to figure that out on your own. :-)

BitmapEffect replace with Effects

I have some code to apply DropShadowBitmapEffect to UserControl.
Visual Studio says that this.BitmapEffect is deprecated and we have to use Effects.
How it should be done then?
DropShadowBitmapEffect myDropShadowEffect = new DropShadowBitmapEffect();
myDropShadowEffect.Color = GetShadowColorValue();
myDropShadowEffect.Direction = 250;
// Set the depth of the shadow being cast.
myDropShadowEffect.ShadowDepth = 0;
// Set the shadow softness to the maximum (range of 0-1).
myDropShadowEffect.Softness = 1;
// Set the shadow opacity to half opaque or in other words - half transparent.
// The range is 0-1.
myDropShadowEffect.Opacity = 0.7;
// Apply the bitmap effect to the Button.
this.BitmapEffect = myDropShadowEffect; // BitmapEffect is deprecated
DropShadowEffect myDropShadowEffect = new DropShadowEffect();
myDropShadowEffect.Color = GetShadowColorValue();
myDropShadowEffect.Direction = 250;
// Set the depth of the shadow being cast.
myDropShadowEffect.ShadowDepth = 0;
// Set the shadow softness to the maximum (range of 0-1).
// myDropShadowEffect.Softness = 1;
// Set the shadow opacity to half opaque or in other words - half transparent.
// The range is 0-1.
myDropShadowEffect.Opacity = 0.7;
// Apply the effect to the Button.
this.Effect = myDropShadowEffect;
I'm not sure what is the equivalent of Softness... you can try to adjust the BlurRadius value

Categories