How to add a shading pattern to a custom shape - itext

I have drawn an equilateral triangle as follows using iText
canvas.setColorStroke(BaseColor.BLACK);
int x = start.getX();
int y = start.getY();
canvas.moveTo(x,y);
canvas.lineTo(x + side,y);
canvas.lineTo(x + (side/2), (float)(y+(side*Math.sin(convertToRadian(60)))));
canvas.closePathStroke();
I wish to multi color gradient in this shape i.e. fill it with shading comprising of BaseColor.PINK and BaseColor.BLUE. I just can't find a way to do this with iText ?

I've created an example called ShadedFill that fills the triangle you are drawing using a shading pattern that goes from pink to blue as show in the shaded_fill.pdf:
PdfContentByte canvas = writer.getDirectContent();
float x = 36;
float y = 740;
float side = 70;
PdfShading axial = PdfShading.simpleAxial(writer, x, y,
x + side, y, BaseColor.PINK, BaseColor.BLUE);
PdfShadingPattern shading = new PdfShadingPattern(axial);
canvas.setShadingFill(shading);
canvas.moveTo(x,y);
canvas.lineTo(x + side, y);
canvas.lineTo(x + (side / 2), (float)(y + (side * Math.sin(Math.PI / 3))));
canvas.closePathFillStroke();
As you can see, you need to create a PdfShading object. I created an axial shading that varies from pink to blue from the coordinate (x, y) to the coordinate (x + side, y). With this axial shading, you can create a PdfShadingPattern that can be used as a parameter of the setShadingFill() method to set the fill color for the canvas.
See ShadedFill for the full source code.

Related

Swift: Get n numbers of points around a rounded rect / squircle with angle

I’m searching for a method that returns a CGPoint and Angle for each of n items around a rounded rect / squircle (I’m aware those shapes are different but suspect they don’t make a relevant visual difference in my case. Therefore I’m searching for the easiest solution).
Something like this:
func getCoordinates(of numberOfPoints: Int, in roundedRect: CGRect, with cornerRadius: CGFloat) -> [(CGPoint, Angle)] {
// ... NO IDEA HOW TO COMPUTE THIS
}
My ultimate goal is to draw something like this (points distributed with equal angles):
Unfortunately my math skills are not sufficient.
Pseudocode. Used center as cx, cy, w and h as half-width and half-height, r as corner radius.
Calculate angle in side for-loop, add phase to start from needed direction (0 from OX axis, Pi/2 from OY axis)
for (i = 0..n-1):
angle = i * 2 * math.pi / n + phase
Get unit vector components for this direction and absolute values
dx = cos(angle)
dy = sin(angle)
ax = abs(dx)
ay = abs(dy)
Find vertical or horizontal for this direction and calculate point relative to center (we work in the first quadrant at this moment):
if ax * h > ay * w:
x = w
y = w * ay / ax
else:
y = h
x = ax * h / ay
Now we have to correct result if point is in rounded corner:
if (x > w - r) and (y > h - r):
recalculate x and y as below
Here we have to find intersection of the ray with circle arc.
Circle equation
(x - (w-r))^2 + (y - (h-r))^2 = r^2
(x - wr)^2 + (y - hr)^2 = r^2 //wr = w - r, hr = h - r
Ray equation (t is parameter)
x = ax * t
y = ay * t
Substitute in circle eq:
(ax*t - wr)^2 + (ay*t - hr)^2 = r^2
ax^2*t^2 - 2*ax*t*wr + wr^2 + ay^2*t^2 -2*ay*t*hr + hr^2 -r^2 = 0
t^2*(ax^2+ay^2) + t*(-2*ax*wr - 2*ay*hr) + (wr^2 +hr^2 - r^2) = 0
t^2* a + t* b + c = 0
Solve this quadratic equation for unknown t, get larger root, and find intersection point substituting t into ray equation.
Now we want to put result into correct quadrant:
if dx < 0:
x = -x
if dy < 0:
y = -y
and shift them by center coordinates
dx += cx
dy += cy
That's all.

Calculate objects circumscribed radius and inscribed radius - Matlab

I am trying to calculate two objects circumscribed radius and inscribed radius using the code below. I used for the inscribedRadius parameter from this script and for the circumscribed radius I used this function
I do not understand why I get for the box shape that the inscribed radius is bigger than the circumscribed radius. Any idea what is wrong? And how to fix it?
Image
Code:
clc;
clear;
RGB = imcomplement(imread('https://i.stack.imgur.com/8WLAt.jpg'));
I = rgb2gray(RGB);
bw = imbinarize(I);
bw = imfill(bw,'holes');
imshow(bw)
hold on;
[B,L] = bwboundaries(bw,'noholes');
stats = regionprops(L,'Centroid','MajorAxisLength');
for i = 1 : numel(stats)
b = B{i};
c = stats(i).Centroid;
y = b(:,1);
x = b(:,2);
plot( b(:,2),b(:,1),'Color','red','linewidth',2);
text(c(1),c(2),num2str(i),'Color','red');
xMin = min(x);
xMax = max(x);
yMin = min(y);
yMax = max(y);
scalingFactor = 1000 / min([xMax-xMin, yMax-yMin]);
x2s = (x - xMin) * scalingFactor + 1;
y2s = (y - yMin) * scalingFactor + 1;
mask = poly2mask(x2s, y2s, ceil(max(y2s)), ceil(max(x2s)));
edtImage = bwdist(~mask);
inscribedRadius = max(edtImage(:));
[yCenter, xCenter] = find(edtImage == inscribedRadius);
xCenter = (xCenter - 1)/ scalingFactor + xMin;
yCenter = (yCenter - 1)/ scalingFactor + yMin;
inscribedRadius = inscribedRadius / scalingFactor
[circumscribedCenter,circumscribedRadius] = minboundcircle(x,y); % from https://www.mathworks.com/matlabcentral/fileexchange/34767-a-suite-of-minimal-bounding-objects?focused=3820656&tab=function
circumscribedRadius
end
The results are:
Object 1: inscribedRadius = 264, cumscribedRadius = 186.6762
Object 2: inscribedRadius = 130.4079, circumscribedRadius = 132.3831
The values of object 1 (box) are wrong as the inscribedRadius can not be bigger than the cumscribedRadius. They are fine for object 2 (circle)
If you look at the mask image, you'll notice that the square shape is drawn touching the right and bottom edges of the image. There's background only along the left and top of the shape. The distance transform bwdist(~mask) subsequently computes the distance to the background for each pixel within the shape, but since there's background only to the left and top, the pixel at the bottom right of the shape has a distance of 1000, rather than 1. The distance transform is supposed to have a maximum in the middle, at a point equidistant to at least the three nearest shape edge points.
The solution is simple: poly2mask must create an image that is one pixel wider and taller:
mask = poly2mask(x2s, y2s, ceil(max(y2s)) + 1, ceil(max(x2s)) + 1);
^^^ ^^^
With this change, the computed inscribedRadius for the square is 132, as expected.

javafx Shape3D with border

I'm making application with huge mass of 3D shapes and I need them fully transparent and with border. I tried find any way to apply border to Shape3D, specifically to Box and Sphere but I can't find anything. So my questions are:
Is there any way how to add border to Shape3D?
If yes, how to do it?
No, there is no option to add borders to 3d shapes, but you can use very thin cyllinders instead (only works for boxes though):
public void createBoxLines(double contW, double contH, double contD, double x, double y, double z) {
//You call this method to create a box with a size and location you put in
//This method calls the createLine method for all the sides of your rectangle
Point3D p1 = new Point3D(x, y, z);
Point3D p2 = new Point3D(contW + x, y, z);
Point3D p3 = new Point3D(x, contH + y, z);
Point3D p4 = new Point3D(contW + x, contH + y, z);
createLine(p1, p2);
createLine(p1, p3);
createLine(p3, p4);
createLine(p2, p4);
Point3D p5 = new Point3D(x, y, contD + z);
Point3D p6 = new Point3D(contW + x, y, contD + z);
Point3D p7 = new Point3D(x, contH + y, contD + z);
Point3D p8 = new Point3D(contW + x, contH + y, contD + z);
createLine(p5, p6);
createLine(p5, p7);
createLine(p7, p8);
createLine(p6, p8);
createLine(p1, p5);
createLine(p2, p6);
createLine(p3, p7);
createLine(p4, p8);
}
double strokewidth = 1;
public void createLine(Point3D origin, Point3D target) {
//creates a line from one point3d to another
Point3D yAxis = new Point3D(0, 1, 0);
Point3D diff = target.subtract(origin);
double height = diff.magnitude();
Point3D mid = target.midpoint(origin);
Translate moveToMidpoint = new Translate(mid.getX(), mid.getY(), mid.getZ());
Point3D axisOfRotation = diff.crossProduct(yAxis);
double angle = Math.acos(diff.normalize().dotProduct(yAxis));
Rotate rotateAroundCenter = new Rotate(-Math.toDegrees(angle), axisOfRotation);
Cylinder line = new Cylinder(strokewidth, height);
line.getTransforms().addAll(moveToMidpoint, rotateAroundCenter);
myGroup.getChildren().add(line);
}
The createLine method can be used seperately to make lines between different points.
I cant provide many comments for that method, because I basically copied it from some blog. Although I'm having a hard time finding that blog again.
Thanks Alex Quilliam thanks for the code i was able to improve my program.
https://i.imgur.com/HY2x9vF.png
Cylinder line = new Cylinder(strokewidth, height);
↓
Box line = new Box(strokewidth, height, strokewidth);
JavaFX_3D_Cube_Outline_Test

Creating a circular mask for my graph

I'm plotting a square image, but since my camera views out of a circular construction, I want the image to look circular as well. So to do this, I just wanted to create a mask for the image (basically create a matrix, and multiply my data by the mask, so if I want to retain my image I am multiplying by one, and if I want that part of the image to go to black, I multiply by 0).
I'm not sure the best way to make a matrix that will represent a circular opening though. I just want every element within the circle to be a "1" and every element outside the circle to be a "0" so I can color my image accordingly. I was thinking of doing a for loop, but I was hoping there was a faster way to do it. So...all I need is:
A matrix that is 1280x720
I need a circle that has a diameter of 720, centered in the middle of the 1280x720 matrix (what I mean by this is all elements corresponding to being within the circle have a "1" and all other elements have a "0"
My attempt
mask = zeros(1280,720)
for i = 1:1280
for j = 1:720
if i + j > 640 && i + j < 1360
mask(i,j) = 1;
end
end
end
Well the above obviously doesn't work, I need to look at it a little better to form a better equation for determing when to add a 1 =P but ideally I would like to not use a for loop
Thanks, let me know if anything is unclear!
#kol 's answer looks correct. You can do this with vectorized code using the meshgrid function.
width = 1280;
height = 720;
radius = 360;
centerW = width/2;
centerH = height/2;
[W,H] = meshgrid(1:width,1:height);
mask = ((W-centerW).^2 + (H-centerH).^2) < radius^2;
Here is a possible solution:
width = 160;
height = 120;
mask = zeros(width, height);
center_x = width / 2;
center_y = height / 2;
radius = min(width, height) / 2;
radius2 = radius ^ 2;
for i = 1 : width
for j = 1 : height
dx = i - center_x;
dy = j - center_y;
dx2 = dx ^ 2;
dy2 = dy ^ 2;
mask(i, j) = dx2 + dy2 <= radius2;
end;
end;
picture = randn(width, height); % test image :)
masked_image = picture .* mask;
imagesc(masked_image);

Finding min/max of quadratic bezier with CoreGraphics

I am using CoreGraphics to draw a quadratic bezier but want to computer the min/max value of the curve. I am not from a mathematical background so this has become a bit troublesome. Does anyone have any articles or ideas about how to solve this?
For a quadratic Bezier, this is actually quite simple.
Define your three control points as P0 = (x0,y0), P1 = (x1,y1) and P2 = (x2,y2). To find the extrema in x, solve this equation:
t = (x0 - x1) / (x0 - 2*x1 + x2)
If 0 <= t <= 1, then evaluate your curve at t and store the location as Px. Do the same thing for y:
t = (y0 - y1) / (y0 - 2*y1 + y2)
Again, if 0 <= t <= 1, evaluate your curve at t and store the location as Py. Finally, find the axis-aligned bounding box containing P0, P2, Px (if found) and Py (if found). This bounding box will also tightly bound your 2D quadratic Bezier curve.
Calculus gives the standard box of tricks for finding the min/max of continuous, differentiable curves.
Here is a sample discussion.
I have made a representation of this in javascript:
Jsfiddle link
function P(x,y){this.x = x;this.y = y; }
function pointOnCurve(P1,P2,P3,t){
if(t<=0 || 1<=t || isNaN(t))return false;
var c1 = new P(P1.x+(P2.x-P1.x)*t,P1.y+(P2.y-P1.y)*t);
var c2 = new P(P2.x+(P3.x-P2.x)*t,P2.y+(P3.y-P2.y)*t);
return new P(c1.x+(c2.x-c1.x)*t,c1.y+(c2.y-c1.y)*t);
}
function getQCurveBounds(ax, ay, bx, by, cx, cy){
var P1 = new P(ax,ay);
var P2 = new P(bx,by);
var P3 = new P(cx,cy);
var tx = (P1.x - P2.x) / (P1.x - 2*P2.x + P3.x);
var ty = (P1.y - P2.y) / (P1.y - 2*P2.y + P3.y);
var Ex = pointOnCurve(P1,P2,P3,tx);
var xMin = Ex?Math.min(P1.x,P3.x,Ex.x):Math.min(P1.x,P3.x);
var xMax = Ex?Math.max(P1.x,P3.x,Ex.x):Math.max(P1.x,P3.x);
var Ey = pointOnCurve(P1,P2,P3,ty);
var yMin = Ey?Math.min(P1.y,P3.y,Ey.y):Math.min(P1.y,P3.y);
var yMax = Ey?Math.max(P1.y,P3.y,Ey.y):Math.max(P1.y,P3.y);
return {x:xMin, y:yMin, width:xMax-xMin, height:yMax-yMin};
}