Halcon - find edge position, draw line and lineintersection - edge-detection

I'm starting from scratch with Halcon, and I'm not able to solve a problem. I have a Object, need to extract edges from this object, draw a line along the borders and draw a point on the intersection of the lines.
I've tried tresholding, edge, color edge, but It extracts borders everywhere, except the ones I need..
Its just a test i am doing as it is similar to what I have to do later on a real project. But in two days I didnt manage to solve it..
Here is the base image, and the desired result image:
what I have so far:
open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'S1204667', 0, -1, AcqHandle)
set_framegrabber_param (AcqHandle, 'Gain', 1.0)
set_framegrabber_param (AcqHandle, 'ExposureTime', 20000)
set_framegrabber_param (AcqHandle, 'timerDuration', 1)
set_framegrabber_param (AcqHandle, 'BalanceWhiteAuto', 'Off')
set_framegrabber_param (AcqHandle, 'BalanceRatioSelector', 'Red')
set_framegrabber_param (AcqHandle, 'BalanceRatio', 1.22)
set_framegrabber_param (AcqHandle, 'BalanceRatioSelector', 'Green')
set_framegrabber_param (AcqHandle, 'BalanceRatio', 1.00)
set_framegrabber_param (AcqHandle, 'BalanceRatioSelector', 'Blue')
set_framegrabber_param (AcqHandle, 'BalanceRatio', 1.95)
grab_image (Image, AcqHandle)
threshold (Image, Region, 0, 128)
expand_region (Region, Region, RegionExpanded, 15, 'image')
close_framegrabber (AcqHandle)

Based off the original poster being worried about positional movement, I'm posting another answer which is more involved. This strategy might not be the easiest for this case but it is a general strategy that works for a lot of cases. Typically problems like this are solved as follows:
1) Perform a rough location of the part. This usually involves either a blob detection or a matching strategy (correlation, shape based etc). The output of this step is a transformation describing the location of the object (translation, orientation).
2) Based off the found location in step 1, the search regions for detecting features (lines, holes etc) are transformed or updated to new locations. Or the entire image is transformed.
I couldn't post all the code since it was too large. You will have to personal message me if you want me to email you the full HDevelop script. Here are some snippets to give you an idea:
Step 1: Threshold the image and setup search regions where the lines should be found. Only posting code for the first two regions but code is identical for the other three
threshold(Image, RegionThreshold, 0, 100)
region_to_bin(RegionThreshold, ImageBinary, 255, 0, Width, Height)
dev_display(ImageBinary)
*Use the mouse to draw region 1 around first line. Right click when finished.
draw_rectangle2(WindowHandle, Reg1Row, Reg1Column, Reg1Phi, Reg1Length1, Reg1Length2)
gen_rectangle2(Rectangle1, Reg1Row, Reg1Column, Reg1Phi, Reg1Length1, Reg1Length2)
*Use the mouse to draw region 2 around second line. Right click when finished.
draw_rectangle2(WindowHandle, Reg2Row, Reg2Column, Reg2Phi, Reg2Length1, Reg2Length2)
gen_rectangle2(Rectangle2, Reg2Row, Reg2Column, Reg2Phi, Reg2Length1, Reg2Length2)
The search regions look like this:
Step 2: Calculate the intersection of the lines. Only posting code for the first two lines but code is identical for the other three
*get line segment 1
reduce_domain(ImageBinary, Rectangle1, ImageReduced)
edges_sub_pix (ImageReduced, EdgesLine1, 'lanser2', 0.1, 20, 40)
fit_line_contour_xld (EdgesLine1, 'regression', -1, 0, 5, 2, RowBeginLine1, \
ColBeginLine1, RowEndLine1, ColEndLine1, Nr, Nc, Dist)
*get line segment 2
reduce_domain(ImageBinary, Rectangle2, ImageReduced)
edges_sub_pix (ImageReduced, EdgesLine2, 'lanser2', 0.1, 20, 40)
fit_line_contour_xld (EdgesLine2, 'regression', -1, 0, 5, 2, RowBeginLine2, \
ColBeginLine2, RowEndLine2, ColEndLine2, Nr, Nc, Dist)
*Calculate and display intersection line 1 to line 2
intersection_lines(RowBeginLine1, ColBeginLine1, RowEndLine1, ColEndLine1, \
RowBeginLine2, ColBeginLine2, RowEndLine2, ColEndLine2, \
Line1Line2IntersectRow, Line1Line2IntersectCol,
IsOverlappingLine1Line2)
This produces the following output:
Step 3: Create a normalized cross correlation model for finding the object when it undergoes translation or rotation. Here I choose a simple region on the bottom
gen_rectangle1 (ModelRegion, 271.583, 200, 349.083, 530)
reduce_domain (ImageBinary, ModelRegion, TemplateImage)
create_ncc_model (TemplateImage, 'auto', rad(0), rad(360), 'auto', 'use_polarity',
ModelID)
area_center (ModelRegion, ModelRegionArea, RefRow, RefColumn)
Output Image
Step 4: Now we consider what happens when the object is moved. To simulate this I warped the image using a affine transform. I then searched for the normalized cross correlation model created in step 3. Below you can see the object was found. The output is a row, column and angle where it was found. This is converted to a matrix called AlignmentHomMat2D
Some of the code:
threshold(TransImage, RegionThreshold, 0, 100)
region_to_bin(RegionThreshold, ImageBinaryScene, 255, 0, Width, Height)
* Matching 01: Find the model
find_ncc_model (ImageBinaryScene, ModelID, rad(0), rad(360), 0.8, 1, 0.5, 'true', 0,
Row, Column, Angle, Score)
* Matching 01: Display the centers of the matches in the detected positions
dev_display (TransImage)
set_line_width(WindowHandle, 3)
for I := 0 to |Score| - 1 by 1
* Matching 01: Display the center of the match
gen_cross_contour_xld (TransContours, Row[I], Column[I], 20, Angle)
dev_set_color ('green')
dev_display (TransContours)
hom_mat2d_identity (AlignmentHomMat2D)
hom_mat2d_translate (AlignmentHomMat2D, -RefRow, -RefColumn, AlignmentHomMat2D)
hom_mat2d_rotate (AlignmentHomMat2D, Angle[I], 0, 0, AlignmentHomMat2D)
hom_mat2d_translate (AlignmentHomMat2D, Row[I], Column[I], AlignmentHomMat2D)
* Matching 01: Display the aligned model region
affine_trans_region (ModelRegion, RegionAffineTrans, AlignmentHomMat2D,
'nearest_neighbor')
dev_display (RegionAffineTrans)
endfor
The output is as follows:
Step 5: Finally the search regions for locating the original lines are updated based off where the cross-correlation model was found.
Here is the code. Again I'm only showing the first two line segments:
*transform initial search regions
affine_trans_region(Rectangle1, Rectangle1Transformed,
AlignmentHomMat2D,'nearest_neighbor')
affine_trans_region(Rectangle2, Rectangle2Transformed,
AlignmentHomMat2D,'nearest_neighbor')
*get line segment 1
reduce_domain(ImageBinaryScene, Rectangle1Transformed, ImageReduced)
edges_sub_pix (ImageReduced, EdgesLine1, 'lanser2', 0.5, 20, 40)
fit_line_contour_xld (EdgesLine1, 'regression', -1, 0, 5, 2, RowBeginLine1, \
ColBeginLine1, RowEndLine1, ColEndLine1, Nr, Nc, Dist)
*get line segment 2
reduce_domain(ImageBinaryScene, Rectangle2Transformed, ImageReduced)
edges_sub_pix (ImageReduced, EdgesLine2, 'lanser2', 0.5, 20, 40)
fit_line_contour_xld (EdgesLine2, 'regression', -1, 0, 5, 2, RowBeginLine2, \
ColBeginLine2, RowEndLine2, ColEndLine2, Nr, Nc, Dist)
*Calculate and display intersection line 1 to line 2
intersection_lines(RowBeginLine1, ColBeginLine1, RowEndLine1, ColEndLine1, \
RowBeginLine2, ColBeginLine2, RowEndLine2, ColEndLine2, \
Line1Line2IntersectRow, Line1Line2IntersectCol,
IsOverlappingLine1Line2)
This produces the following output:

Halcon has a lot of ways this can be accomplished depending on the requirements. One of the most common techniques for detecting lines is to use the Hough Transform. I've attached a small HDevelop script showing how to get the intersection of two of the lines in your image. The same principle can be used for the others.
One of the most important concepts in Halcon is Regions. The example program first allows you to create two regions by drawing rectangles over top of two of the lines. The regions are black in the image below. On line 8 of the program (draw_rectangle2...) you will need to draw a bounding box around the first line. Right click when you are finished. Line 10 (draw rectangle2...) will expect you to draw a bounding box around the second line. Again right click when finished.
The regions are then combined on lines 13-16 by concatenation. On line 19 (reduce_domain) the domain of the image is reduced to the concatenated regions. You can think of this as a mask. Now when we search for the lines we will only search the part of the image where we created the regions.
emphasized text
read_image (Image, 'C:/Users/Jake/Documents/Stack Overflow/Halcon/Find Edge Position,
Draw Line and Line Intersection/FMuX1.jpg')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_display(Image)
*Use the mouse to draw region 1 around first line. Right click when finished.
draw_rectangle2(WindowHandle, Reg1Row, Reg1Column, Reg1Phi, Reg1Length1, Reg1Length2)
*Use the mouse to draw region 2 around second line. Right click when finished.
draw_rectangle2(WindowHandle, Reg2Row, Reg2Column, Reg2Phi, Reg2Length1, Reg2Length2)
*Generate a single region to search for two lines
gen_rectangle2(Rectangle1, Reg1Row, Reg1Column, Reg1Phi, Reg1Length1, Reg1Length2)
gen_rectangle2(Rectangle2, Reg2Row, Reg2Column, Reg2Phi, Reg2Length1, Reg2Length2)
concat_obj(Rectangle1, Rectangle2, Regions)
union1(Regions, RegionUnion)
*Reduce the domain of the image to the region created in lines 13-16
reduce_domain(Image, RegionUnion, ImageReduced)
* Detect edges (amplitude) using the Sobel operator
sobel_amp (ImageReduced, EdgeAmplitude1, 'thin_sum_abs', 3)
dev_set_color ('red')
threshold (EdgeAmplitude1, Region1, 100, 255)
hough_lines (Region1, 4, 50, 5, 5, Line1Angle, Line1Dist)
dev_set_color ('blue')
* Store input lines described in HNF
gen_region_hline (LineRegions, Line1Angle, Line1Dist)
*Select Line1
select_obj(LineRegions, Line1, 1)
*Select Line2
select_obj(LineRegions, Line2, 2)
*Calculate and display intersection
intersection(Line1, Line2, Line1Line2Intersection)
area_center(Line1Line2Intersection, Line1Line2IntersectArea, Line1Line2IntersectRow,
Line1Line2IntersectCol)
disp_circle (WindowHandle, Line1Line2IntersectRow, Line1Line2IntersectCol, 6)

Related

How to draw area/fill between two curved lines of different lengths (different x values) in d3

Problem
I am trying to build a line chart where I shade between two lines (different colors based on which line is above the other). This works perfectly for linear curving in all cases tested.
However, this needs to work with actual curving (such as curveBasis, as shown below). This works perfectly if the lines have the exact same x values; but we have cases in which a) one line is longer/shorter than another and/or b) one line may be missing one or more x values in the middle of the line that the other line isn't missing. This is because how a line is drawn between two points changes based on what points come before/after when it's non-linear.
Generally speaking, I understand why this is happening; but I'm having a hard time finding a good solution to make this actually work the way I'd like. I'd love to at least be pointed in the right direction or given some ideas (one idea I considered is listed at the bottom)!
Examples
Here's how it works with curveLinear (looks good):
Here's how it looks with curveBasis if the x values are the same for both lines (looks good):
Here's how it actually looks with curveBasis if the x values are not the same for both lines (doesn't look good):
Current Code/Strategy
Here's the current strategy (note that I refer to the lines as good/bad, where good line on top results in green fill and bad line on top results in red fill) (some stuff removed, like class names, etc to reduce clutter):
// I also set the domain and range appropriately for x/y--not shown here:
const x = d3.scaleTime();
const y = d3.scaleLinear();
// 1. Draw the lines "normally" (this is in a loop to build each line)
const lineData = d3.line()
.defined(point => !isNaN(point.y))
.x(point => x(point.x))
.y(point => y(point.y))
.curve(d3[lineCurve]);
d3Chart.append('path')
.datum(points)
.attr('d', lineData);
// 2. "Normalize" lines into the following format for each point (logic not shown here): {x, goodY, badY}
// Bind this data to a new svg group
const areaElement = d3Chart.append('g').datum(normlaizedData);
// 3. Clip paths and area paths
// Create the green fill clip path.
const goodLineClipPathId = `good-line-clip-path`;
areaElement.append('clipPath')
.attr('id', goodLineClipPathId)
.append('path')
.attr('d', d3.area()
.curve(lineCurve)
.x(point => x(point.x))
.y0(0)
.y1(point => y(point.badY))
);
// Create the red fill clip path.
const badLineClipPathId = `bad-line-clip-path`;
areaElement.append('clipPath')
.attr('id', badLineClipPathId)
.append('path')
.attr('d', d3.area()
.curve(lineCurve)
.x(point => x(point.x))
.y0(height)
.y1(point => y(point.badY))
);
// Create the red fill.
areaElement.append('path')
.attr('clip-path', `url(#${badLineClipPathId})`)
.attr('d', d3.area()
.curve(lineCurve)
.x(point => x(point.x))
.y0(point => y(point.goodY))
.y1(point => y(point.badY))
);
// Create the green fill.
areaElement.append('path')
.attr('clip-path', `url(#${goodLineClipPathId})`)
.attr('d', d3.area()
.curve(lineCurve)
.x(point => x(point.x))
.y0(point => y(point.badY))
.y1(point => y(point.goodY))
);
Considered Solutions
One idea I had was to "clone" the exact svg lines but cut off the beginning/end (leaving the remainder of the line the same) and use those lines as the top/bottom of the areas (and close the ends with straight vertical lines); but the path data itself makes use of curving, so changing the start/end would still affect the line (unless there's a way around this).
Here's another idea I thought of: don't "normalize" the lines and create additional clipping to "cut off" the ends (at the vertical black lines drawn on the screenshot); but even if I did that, there would still be issues (as indicated by the arrows).
This is not a great solution, but I'm posting because it is a solution (or a partial one at least).
Without getting into too much detail, I noticed that there were actually two possible causes of the issue (one or both could cause the gaps or bleeding):
The lines don't start and/or end at the same x values.
The lines don't share a 1 to 1 mapping of x values with actual y values (in other words, at least one of the two lines is "missing" a y value for a corresponding x value that is not missing in the other line).
"Solution"
Note: I've only tested this with curveBasis--other curving types may not work the same way.
"Fill in" the missing y values (fixes cause #2 above)--I did this by linear interpolation between closest left/right non-missing points (if the missing value was at the end or beginning of a line, the closest non-missing point value was re-used). Do this in both your normalized and original lines, but don't interpolate between a point in the original line and point that's not in the normalized line--just re-use in this case (see example below).
"Triplicate" the normalized start/end points (fixes cause #1 above)--I did this by literally cloning the same point two additional times at the x values where the normalized data starts and ends of the line data. Only need to do this in your original (non-normalized) lines--the normalized lines already start/end where they need to. This works for curveBasis, because of which points are taken into consideration for the curve--this solution essentially forces a sort of line "end" even if it's in the middle of a line (source for documentation quote below):
[curveBasis] Produces a cubic basis spline using the specified control points. The first and last points are triplicated such that the spline starts at the first point and ends at the last point...
Example
Line 1 (x values):
[3, 4, 5, 7, 8, 9, 10, 11] original
[3, 4, 5, 6 (null y), 7, 8, 9, 10, 11] normalized, pre-manipulation
[3, 4, 5, 6 (interpolated y), 7, 8, 9, 10, 11] normalized, post-manipulation
[3, 3, 3, 4, 5, 6 (interpolated y), 7, 8, 9, 10, 11, 11, 11] original, post-manipulation
Note that for line 1, we could skip triplicating the start/end points, since they're already the start/end of the original line
Line 2 (x values):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13] original
[3, 4, 5, 6, 7, 8, 9, 10, 11 (null y)] normalized, pre-manipulation
[3, 4, 5, 6, 7, 8, 9, 10, 11 (re-used y from 10)] normalized, post-manipulation
[1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11 (re-used y from 10), 11 (re-used y from 10), 11 (re-used y from 10), 13] original, post-manipulation (note that we do not interpolate between 10 and 13 to get 11, because 13 doesn't exist in the normalized line)
What does it look like?
Not really that great--but, hey, there are no gaps or bleed overs! The arrows point to where we've "triplicated" points to force the curve to "end" there.
Are we going to use this?
Not likely. I'm still searching for a better solution; but this is what I've come up with so far.
This is a far better solution--it actually works exactly how I would expect. My only complaint is that it seems to me there should be a better way (I end up building/manipulating path data manually, which I don't like--I can say that I learned a good bit about many of the commands for defining SVG paths).
Solution
Note: I changed the way I generate the clipPath elements below from how I did it in my original question, because I was running into some edge case problems where there was some bleed above/below the line--the comments throughout should hopefully explain my solution.
// Generate path definition `d` for the "bad" line in reverse
const badLineReversedD = myD3LineFunction(badLineData.slice().reverse());
// First shared x value.
const leftBoundaryX = myXFunction(firstSharedXValue);
// Last shared x value.
const rightBoundaryX = myXFunction(lastSharedXValue);
// Create the good clipPath to contain the line shading to its proper area.
// This clip is all of the area ABOVE the bad line.
const goodLineClipPathId = `good-line-clip-path`;
areaElement.append('clipPath')
.attr('id', goodLineClipPathId)
.append('path')
// Commands:
// Start with reversed bad line path
// Line to upper left corner (0,0) - L
// Line to upper right corner (width,0) - L
// Close path - Z
.attr('d', `${badLineReversedD}L0,0L${width},0Z`);
// Create the bad clipPath to contain the line shading to its proper area.
// This clip is all of the area BELOW the bad line.
const badLineClipPathId = `bad-line-clip-path`;
areaElement.append('clipPath')
.attr('id', badLineClipPathId)
.append('path')
// Commands:
// Start with reversed bad line path
// Line to lower left corner (0,height) - L
// Line to lower right corner (width,height) - L
// Close path - Z
.attr('d', `${badLineReversedD}L0,${height}L${width},${height}Z`);
// Create a rectangular mask so only that which is within is visible.
// This rectangle cuts off the left/right of good and bad shading if
// the good/bad lines don't start/end at the same points. This hides
// any bleeding of shading to the left/right.
const maskId = `good-bad-mask`;
areaElement.append('mask')
.attr('id', maskId)
.append('rect')
.attr('fill', 'white') // 'white' means show everything inside this rect (outside is hidden)
.attr('x', leftBoundaryX) // Start at the first x value shared by both good/bad lines
.attr('y', 0) // Start at the top
.attr('width', rightBoundaryX - leftBoundaryX) // End at the last x value shared by both good/bad lines
.attr('height', height); // End at the bottom
/*
Create SVG path data for a shape bounded by the good and bad lines with straight lines drawn from the
start/end of each line (start of good to start of bad--end of good to end of bad).
Grab the line data 'd' from the good line and concatenate it with the line data 'd' from the reversed
bad line with it's starting point 'M' replaced with a Line command 'L' (to draw a line from the end of
the good line to the end--since it's reversed--of the bad line) and add a close path command 'Z' at the
end (which will draw a straight line from the start--since it's reversed--of the bad line to the start
of the good line, which creates a closed path within the good/bad lines).
*/
const goodBadAreaD = goodLinePath.getAttribute('d') + badLineReversedD.replace('M', 'L') + 'Z';
// Create the bad fill.
badArea = areaElement.append('path')
.attr('clip-path', `url(#${badLineClipPathId})`)
.attr('mask', `url(#${maskId})`)
.attr('class', 'bad-area')
.attr('d', goodBadAreaD);
// Create the good fill.
goodArea = areaElement.append('path')
.attr('clip-path', `url(#${goodLineClipPathId})`)
.attr('mask', `url(#${maskId})`)
.attr('class', 'good-area')
.attr('d', goodBadAreaD);
What does it look like?
Exactly as expected--and it works for all curve types I've tested (linear, cardinal, basis), and I'd expect it to work for any curve!
Are We Going to Use This?
Absolutely! I'm open to hearing a "better" d3-way to do this, but this works just fine and isn't crazy complicated.

Why does ArcTo sometimes not update the current position

Background
I'm working a legacy MFC application which uses GDI draw its content.
I need to draw rounded rectangles where each corner has a (potentially) different radius.
This means that I can no longer use RoundRect and have to roll my own using ArcTo.
I'm using SetWindowExtEx, SetWindowOrgEx, SetViewportExtEx and SetViewportOrgExt to implement zooming.
This works fine in most situations.
Problem
On certain zoom levels, my code fails to construct a proper path of the outline of the roundrect.
The following screenshots is of my RoundRect code used to create a path, then used to clip a bigger rectangle (to get an idea of it's shape).
The clipping region created by this path is sometimes missing a corner, clips everything (two missing corners?) or clips nothing.
My guess is that due to rounding errors, the arcs are too small, and is skipped alltogether by GDI.
I find this hard to believe though since it is working correctly for smaller zoom factors than the ones pictured here.
Working correctly:
Missing a corner:
The Code
I have tried to reduce the code needed to reproduce it and have ended up with the following. Note that the number in the screenshots is the value of zoomFactor, the only variable.
You should be able to paste this code into the OnPaint function of a newly created Win32 application project and manually declare zoomFactor a constant.
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowOrgEx(hdc, 0, 40, nullptr);
SetWindowExtEx(hdc, 8000, 6000, nullptr);
SetViewportOrgEx(hdc, 16, 56, nullptr);
SetViewportExtEx(hdc, 16 + (396)*zoomFactor/1000,
48 + (279)*zoomFactor/1000, nullptr);
BeginPath(hdc);
MoveToEx(hdc, 70, 1250, nullptr);
ArcTo(hdc,
50, 1250, 90, 1290,
70, 1250,
50, 1270);
ArcTo(hdc,
50, 2311, 90, 2351,
50, 2331,
70, 2351);
ArcTo(hdc,
1068, 2311, 1108, 2351,
1088, 2351,
1108, 2331);
ArcTo(hdc,
1068, 1250, 1108, 1290,
1108, 1270,
1088, 1250);
CloseFigure(hdc);
EndPath(hdc);
SelectClipPath(hdc, RGN_AND);
HBRUSH br = CreateSolidBrush(RGB(255,0,255));
const RECT r = {0, 0, 8000, 6000};
FillRect(hdc, &r, br);
Here is a simpler bit of code to illustrate the problem:
const int r = 20;
MoveToEx(hdc, 200, 100, 0);
BOOL b = ArcTo(hdc,
100 + 2 * r, 100,
100, 100 + 2 * r,
100 + r, 100,
100, 100 + r);
POINT p;
GetCurrentPositionEx(hdc, &p);
This draws a single corner of radius r. This works fine for non-zero values of r and the position p is correctly updated to match the end of the arc: (100, 100+r), give or take a pixel.
However, when r is zero ArcTo returns TRUE but the position is not updated: p contains the starting position of (200,100).
The documentation states that "If no error occurs, the current position is set to the ending point of the arc." The function returned TRUE indicating success so the position should have been updated.
In my view this a bug. The function should return FALSE because the rectangle is empty so there is no arc and thus no well-defined endpoint. However, it would be more useful in practice if the function returned TRUE and updated the current position to match the final coordinate pair in the parameter list. But it does neither of these things. EDIT: An even better implementation in your case would be to calculate the arc end points in logical coordinates before converting to device coordinates, but GDI in general doesn't work like this.
The problem occurs in your code because your coordinate transformation collapses the second arc's rectangle to an empty rectangle when the zoom is 266. You can see this yourself by adding the following to your code to transform the coordinates of the second arc:
POINT points[4] = {{50,2311},{90,2351},{50,2331},{70,2351}};
LPtoDP(hdc, points, 4);
With the zoom set to 266 the points are transformed to (17,90), (17,91), (17,91), (17,91) so the rectangle has no width and is empty. And you hit the ArcTo bug.
I guess it works for smaller zooms when the rounding happens to put the x-coordinates into adjacent integers rather than the same integer.
A simple fix would be to create a MyArcTo function that replaces the arc with a LineTo when it is too small to be visible.

Exclude round rectangle from clipping region?

What's the correct way to exclude a round rectangle from the clipping gregion with Delphi / GDI?
There is ExcludeClipRect to exclude a rectangular region and there is CreateRoundRectRgn together with SelectClipRgn to set the clipping region to a round rectangle.
But how can I exclude a round rectange from the clipping region (something like ExcludeClipRoundRect or ExcludeClipRgn)? I experimented with CombineRgn but did not get it working.
Thanks to the comment by #TLama I was able to solve it like this:
Region := CreateRectRgn (0, 0, ClientWidth, ClientHeight);
ExcludedRegion := CreateRoundRectRgn (1, 1, ClientWidth - 1, ClientHeight - 1, 3, 3);
CombineRgn (Region, Region, ExcludedRegion, RGN_XOR);
SelectClipRgn (Canvas.Handle, Region);
The problem before was that the region passed as the first parameter to CombineRgn has not been created. One sentence from the linked tutorial provided the clue:
One more thing to point out is that the destination region in
CombineRgn can be one of the source regions.
together with this information from MSDN:
hrgnDest [in]: A handle to a new region with dimensions defined by combining two
other regions. (This region must exist before CombineRgn is called.)
As an alternative to the already given answer, which will allow to define one less region, is to use ExtSelectClipRgn:
ExcludedRegion := CreateRoundRectRgn (1, 1, ClientWidth - 1, ClientHeight - 1, 3, 3);
ExtSelectClipRgn(Canvas.Handle, ExcludedRegion, RGN_DIFF);
If you're not sure that the clipping region has been unmodified before or not, and want to reset the region, you can call
SelectClipRgn(Canvas.Handle, 0);
before calling ExtSelectClipRgn.

How to draw a line with changing intensity/grayscale

If I have matrix/data with line intensity values:
e.g.
0, 1, 2, 3, 4, 5, ..... M (where intensity value is gradually changing)
or
any random order of values
So if I use the first intensity set of data, (0, 1, 2, 3, 4, 5, ..... M), my line color should be gradually turning black to white. If I remember correctly, 0 is used to represent black and 255 is used to represent white? I would like to use a data of intensity values to draw 3D line with changing color/intensity.
How can I draw a 3D line with changing intensity/grayscale? I would appreciate any advice or recommendation.
You can use the 3D colored line plot tool from the file exchange and change the colormap to whatever you need.

How many colors in the colorbuffer of OpenGL ES we need?

If I have a cube mesh in OpenGL ES and I want to have a flat color for each side of the cube so that each side has a different color, do I need to specify color per vertex or color per triangle or color per side?
This 2 lines of code:
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
are related to this question.
How OpenGL ES knows which color I specified matches with which side of the cube?
Colours should be specified per-vertex, but as each face has a different colour, you are not going to be able to share vertices between faces. Instead of drawing a cube (8 vertices, 8 colours, 12 triangles), draw 6 quads that just happen to have coincident vertex positions (24 vertices, 24 colours, 12 triangles)
Edit: a quad is just 2 triangles that share some vertices. For example, a quad covering the unit square (in 2D) could have a vertex array and triangle index array like so:
// bottom left, top left, bottom right, top right order
float[] verts = new float[]{ 0, 0, 0, 1, 1, 0, 1, 1 };
// anti-clockwise vertex order
int[] tris = new int[]{ 0, 2, 1, 2, 3, 1 };

Resources