How to make hexagon shape image in react native? - react-native

I want to make hexagon shape image like as below image but didn't find any perticular way to make it customized.
Here is the image:
Suggest any library to make it possible in react-native.

So I thought that the usage of a hexagon path was so common that finding an existing one online would be a simple task, but searches proved to be fruitless so I decided to make a function that would make a regular hexagon Skia Path. I found a really cool quora answer on how to get the points any size hexagon(regular hexagon), and then I just connect the points:
import {Skia} from "#shopify/react-native-skia";
function makeHexagonPath(size, offset) {
const path = Skia.Path.Make();
let [xOffset, yOffset] = offset || [0, 0];
if (!size) size = 10;
// https://www.quora.com/How-can-you-find-the-coordinates-in-a-hexagon
const halfed = size / 2;
const sqrt = (Math.sqrt(3) * size) / 2;
const points = [
[size, 0], //a
[halfed, sqrt], //b
[-halfed, sqrt], //c
[-size, 0], //d
[-halfed, -sqrt], //e
[halfed, -sqrt], //f
].map(([x, y]) => [x + xOffset, y + yOffset]);
console.log(points);
path.moveTo(...points[0]);
points.forEach((point) => path.lineTo(...point));
path.close();
return path;
}
With the makeHexagonPath function you draw a clipping of any size and use the offset parameter to move the hexagon to desired location:
const imageSize = 200
export default function ClipImage({imageSrc}) {
const image = useImage(imageSrc);
const hexagonSize = imageSize / 2;
const path = makeHexagonPath(hexagonSize, [hexagonSize, hexagonSize]);
if (!image) return null;
return (
<Group clip={path}>
/*<Path path={path} color="lightblue" />*/
<Image
image={image}
fit="cover"
x={0}
y={0}
width={imageSize}
height={imageSize}
/>
</Group>
);
}
I would post an expo snack demoing it but react-native-skia wasnt working for me on expo but in the default react-native environment it worked like a charm

Related

Delete rather than hide area outside clipPath SVG (React-Native)

I am creating a jigsaw puzzle in React Native ios. I have paths and one image, I am using a loop to create SVG elements of each puzzle piece. However, when I use Draggable from 'react-native-draggable' to drag each piece, I realize that the clipPath to create the pieces only hides the rest of the image rather than deleting it, so the draggable is available on the whole image rather than just the piece.
This is my code for generating a list of the pieces which I just render in my return function as {pieces}:
const gen = () => {
let xC = 5, yC = 7;
let width = 380, height = 540;
let piece_width = Math.floor(width / xC), piece_height = Math.floor(height / yC);
const out = new JigsawGenerator({ width: width, height: height, xCount: xC, yCount: yC, radius: 20, fixedPattern: false });
let cells = out["cells"]; // 2d array of paths
let pieces = [];
let k = 0;
for (let i = 0; i < cells.length; i++) {
for (let j = 0; j < cells[i].length; j++) {
let p = cells[i][j];
pieces.push(
<Draggable>
<SVG.Svg
width={width}
height={height}
fill="none"
key={k}
>
<SVG.Defs>
<SVG.ClipPath id="clip" >
<SVG.Path
d={p}
stroke="black"
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
/>
</SVG.ClipPath>
</SVG.Defs>
<SVG.Image
width={width}
height={height}
preserveAspectRatio="xMidYMid slice"
href={img_src}
clipPath="url(#clip)"
/>
</SVG.Svg>
</Draggable>
);
k++;
}
};
return pieces;
};
This is an example of what one of the pieces looks like, with a border around the SVG element. I just want the piece itself to be clickable/draggable, but I can drag from anywhere inside the border and the whole rectangle is dragged:
example singular jigsaw piece
I would really appreciate if anyone had any ideas on how to make it so that the area outside the clipPath is deleted/won't be included in Draggable!

Implement custom WebGL shaders in vuejs

I found a three.js example of an effect I'm attempting, but the implementation doesn't appear to be explained, and may be an older version of three/js. I'm also just getting into three.js/WebGL, so I may just be plain wrong about things.
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
The example uses "shaders", which are in script tags with the type="x-shader/x-vertex" attribute. I'm able to put the script tags in my head and everything works as advertised.
However, I'm using VueJS, and so I really would rather not put these in my index.html file. I tried to just inject the string directly to the material:
const vertexShader = `
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
And:
vertexShader: vertexShader,
But I get a
THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false
Program Info Log: Fragment shader is not compiled.
So I tried to actually put it in a created script tag thinking the browser must be doing some magic:
const createShader = (text) => {
const tag = document.createElement("script");
tag.setAttribute("type", "x-shader/x-vertex");
const tnode = document.createTextNode(text);
tag.appendChild(tnode);
document.head.appendChild(tag);
return tag.textContent;
}
And:
vertexShader: createShader(vertexShader),
But that has the same issue.
Is there a clean way to make this happen, or is direct code on page load the only option?

How do I access all the pixels for a Raster Source

I am attempting to calculate some statistics for pixel values using openlayers 6.3.1 & I am having an issue iterating over all pixels. I have read the docs for the pixels array that gets passed to the operation callback and it states:
For pixel type operations, the function will be called with an array
of * pixels, where each pixel is an array of four numbers ([r, g, b, a]) in the * range of 0 - 255. It should return a single pixel
array.
I have taken this to mean that the array passed contains all the pixels but everything I do seems to prove that I only get the current pixel to work on.
if(this.rasterSource == null) {
this.rasterSource = new Raster({
sources: [this.imageLayer],
operation: function (pixels, data) {
data['originalPixels'] = pixels;
if(!isSetUp) {
// originalPixels = pixels as number[][];
// const originalPixels = Array.from(pixels as number[][]);
// let originals = generateOriginalHistograms(pixels as number[][]);
isSetUp = true;
}
// console.log(pixels[0]);
let pixel = pixels[0];
pixel[data['channel']] = data['value'];
return pixel;
},
lib: {
isSetUp: isSetUp,
numBins: numBins,
// originalPixels: originalPixels,
// originalRed: originalRed,
// originalGreen: originalGreen,
// originalBlue: originalBlue,
generateOriginalHistograms: generateOriginalHistograms,
}
});
this.rasterSource.on('beforeoperations', function(event) {
event.data.channel = 0;
event.data.value = 255;
});
this.rasterSource.on('afteroperations', function(event) {
console.debug("After Operations");
});
I have realised that I cannot pass arrays through the lib object so I have had to stop attempting that. These are the declarations I am currently using:
const numBins = 256;
var isSetUp: boolean = false;
function generateOriginalHistograms(pixels: number[][]) {
let originalRed = new Array(numBins).fill(0);
let originalGreen = new Array(numBins).fill(0);
let originalBlue = new Array(numBins).fill(0);
for(let i = 0; i < numBins; ++i) {
originalRed[Math.floor(pixels[i][0])]++
originalGreen[Math.floor(pixels[i][1])]++;
originalBlue[Math.floor(pixels[i][2])]++;
}
return {red: originalRed, blue: originalBlue, green: originalGreen};
}
& they are declared outside of the current angular component that I am writing this in. I did have another question on this but I have since realised that I was way off in what I could and couldn't use here;
This now runs and, as it is currently commented will tint the image red. But the value of data['originalPixels'] = pixels; only ever produces one pixel. Can anyone tell me why this is & what I need to do to access the whole pixel array. I have tried to slice & spread the array to no avail. If I uncomment the line // let originals = generateOriginalHistograms(pixels as number[][]); I get an error ​
Uncaught TypeError: Cannot read properties of undefined (reading '0')
generateOriginalHistograms # blob:http://localhos…a7fa-b5a410582c06:6
(anonymous) # blob:http://localhos…7fa-b5a410582c06:76
(anonymous) # blob:http://localhos…7fa-b5a410582c06:62
(anonymous) # blob:http://localhos…7fa-b5a410582c06:83
& if I uncomment the line // console.log(pixels[0]); I get all the pixel values streaming in the console but quite slowly.
The answer appears to be change the operationType to 'image' and work with the ImageData object.
this.rasterSource = new Raster({
sources: [this.imageLayer],
operationType: "image",
operation: function (pixels, data) {
let imageData = pixels[0] as ImageData;
...
I now have no issues calculating the stats I need.

Retrieve center point the viewer is looking at from view/perspective matrix

I use the glMatrix's method: mat4.lookAt(out, eye, center, up) to set a camera view when a WebGL model is rendering.
After the model is rendered, I need to retrieve the center attribute from view/perspective matrix. I know how to retrieve the eye attribute, but I have no idea how to get the center one. How can I do that?
This is the mat4.lookAt method I use:
http://glmatrix.net/docs/mat4.js.html#line1366
you can't get the actual center since it was normalized. You can get a center position that will generate the same look at. I think it would be this.
camera = mat4.inverse(mat4.create(), view);
up = camera.slice(4, 7);
eye = camera.slice(12, 15);
negativeZ = camera.slice(8, 11);
center = vec3.scaleAndAdd(vec3.create(), eye, negativeZ, -1);
That center will be one unit in front of the eye. Change the -1 to -1 * units to make it further away from the eye. In other words -2 would be two units in front, -3 would be 3 units in front etc.
Here's a test
const eye = [1, 2, 3];
const center = [4, 5, 6];
const up = [0, 1, 0];
const view = mat4.lookAt(mat4.create(), eye, center, up);
const camera = mat4.invert(mat4.create(), view);
const newUp = camera.slice(4, 7);
const newEye = camera.slice(12, 15);
const negativeZ = camera.slice(8, 11);
const newCenter = vec3.scaleAndAdd(vec3.create(), newEye, negativeZ, -1);
const newView = mat4.lookAt(mat4.create(), newEye, newCenter, newUp);
// show difference between view and newView
console.log(view.map((v, i) => (v - newView[i]).toFixed(6)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.4.0/gl-matrix-min.js"></script>
This article explains the parts of a camera matrix and what a view matrix is.

Rotating camera around the X-axis (three.js)

I am trying to rotate the camera around to X-axis of the scene.
At this point my code is like this:
rotation += 0.05;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
This makes the camera move around but during the rotation something weird happens and either the camera flips, or it skips some part of the imaginary circle it's following.
You have only provided a snippet of code, so I have to make some assumptions about what you are doing.
This code:
rotation += 0.05;
camera.position.x = 0;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
camera.lookAt( scene.position ); // the origin
will cause the "flipping" you refer to because the camera is trying to remain "right side up", and it will quickly change orientation as it passes over the "north pole."
If you offset the camera's x-coordinate like so,
camera.position.x = 200;
the camera behavior will appear more natural to you.
Three.js tries to keep the camera facing up. When you pass 0 along the z-axis, it'll "fix" the camera's rotation. You can just check and reset the camera's angle manually.
camera.lookAt( scene.position ); // the origin
if (camera.position.z < 0) {
camera.rotation.z = 0;
}
I'm sure this is not the best solution, but if anyone else runs across this question while playing with three.js (like I just did), it'll give one step further.
This works for me, I hope it helps.
Rotating around X-Axis:
var x_axis = new THREE.Vector3( 1, 0, 0 );
var quaternion = new THREE.Quaternion;
camera.position.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
camera.up.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
Rotating around Y-Axis:
var y_axis = new THREE.Vector3( 0, 1, 0 );
camera.position.applyQuaternion(quaternion.setFromAxisAngle(y_axis, angle));
Rotating around Z-Axis:
var z_axis = new THREE.Vector3( 0, 0, 1 );
camera.up.applyQuaternion(quaternion.setFromAxisAngle(z_axis, angle));
I wanted to move my camera to a new location while having the camera look at a particular object, and this is what I came up with [make sure to load tween.js]:
/**
* Helper to move camera
* #param loc Vec3 - where to move the camera; has x, y, z attrs
* #param lookAt Vec3 - where the camera should look; has x, y, z attrs
* #param duration int - duration of transition in ms
**/
function flyTo(loc, lookAt, duration) {
// Use initial camera quaternion as the slerp starting point
var startQuaternion = camera.quaternion.clone();
// Use dummy camera focused on target as the slerp ending point
var dummyCamera = camera.clone();
dummyCamera.position.set(loc.x, loc.y, loc.z);
// set the dummy camera quaternion
var rotObjectMatrix = new THREE.Matrix4();
rotObjectMatrix.makeRotationFromQuaternion(startQuaternion);
dummyCamera.quaternion.setFromRotationMatrix(rotObjectMatrix);
dummyCamera.up.set(camera)
console.log(camera.quaternion, dummyCamera.quaternion);
// create dummy controls to avoid mutating main controls
var dummyControls = new THREE.TrackballControls(dummyCamera);
dummyControls.target.set(loc.x, loc.y, loc.z);
dummyControls.update();
// Animate between the start and end quaternions
new TWEEN.Tween(camera.position)
.to(loc, duration)
.onUpdate(function(timestamp) {
// Slerp the camera quaternion for smooth transition.
// `timestamp` is the eased time value from the tween.
THREE.Quaternion.slerp(startQuaternion, dummyCamera.quaternion, camera.quaternion, timestamp);
camera.lookAt(lookAt);
})
.onComplete(function() {
controls.target = new THREE.Vector3(scene.children[1].position-0.001);
camera.lookAt(lookAt);
}).start();
}
Example usage:
var pos = {
x: -4.3,
y: 1.7,
z: 7.3,
};
var lookAt = scene.children[1].position;
flyTo(pos, lookAt, 60000);
Then in your update()/render() function, call TWEEN.update();
Full example