getting error message of Type 'UnsafeMutableRawPointer' has no subscript members' - malloc

i just need to alloc memory & set values there.Here is my code.
let radius:Float = 5.79
let sigma:Float = radius / 2
let size:Int = Int((round(radius) * 2) + 1)
var weights:UnsafeMutableRawPointer = malloc(MemoryLayout<Float>.size * size * size)
weights[some index] = some vale
but I am getting an error message in swift version 4 saying "UnsafeMutableRawPointer has no subscript members"
How can I fixed this.Any ideas please

You should better check the official documentation of UnsafeMutableRawPointer
You can write something like this:
let radius:Float = 5.79
let sigma:Float = radius / 2
let size:Int = Int((round(radius) * 2) + 1)
var weights:UnsafeMutableRawPointer = malloc(MemoryLayout<Float>.size * size * size)
weights.storeBytes(of: some value, toByteOffset: some offset, as: SomeType.self)
Or you should better use UnsafeMutablePointer<Float> instead, if all the elements are Float.
var weights:UnsafeMutablePointer<Float> = UnsafeMutablePointer.allocate(capacity: size * size)
weights[some index] = some vale

Related

Rearrange equation to solve for a different variable

I am looking at VBA code (function) written by someone else.
Here is the code:
Function EuropeanDelta(StrikePrice, MarketPrice, Volatility, InterestRate As Double, PC As String, ValueDate, ExpiryDate As Date, Optional PriceOrYield As String = "P") As Double
Rem Declare our working variables
Dim r As Double
Dim d1 As Double
Dim d2 As Double
Dim t As Double
Dim SqT As Double
Rem End of variable declaration
If PriceOrYield = "Y" Then
MarketPrice = 100 - MarketPrice
StrikePrice = 100 - StrikePrice
If PC = "C" Then
PC = "P"
Else
PC = "C"
End If
End If
Rem Initiase our working variables
t = (ExpiryDate - ValueDate) / 365
SqT = Sqr(t)
r = Application.WorksheetFunction.Ln(1 + InterestRate)
d1 = (Application.WorksheetFunction.Ln(MarketPrice / StrikePrice) + (Volatility * Volatility * 0.5) * t) / (Volatility * SqT)
Rem Quick logic to deal with Calls or Puts
If PC = "C" Then
EuropeanDelta = Exp(-r * t) * Application.WorksheetFunction.NormSDist(d1)
Else
EuropeanDelta = -Exp(-r * t) * Application.WorksheetFunction.NormSDist(-d1)
End If
If PriceOrYield = "Y" Then
EuropeanDelta = EuropeanDelta * -1
End If
End Function
The whole problem is based around the line for "d1". I would like to re-organise to solve for "StrikePrice". I have tried writing it out mathematically and then re-arranging, then swapping back to VBA.
#duffymo is correct, but am giving the answer directly in terms of VBA code
' d1 = (Log(MarketPrice / StrikePrice) + (Volatility * Volatility * 0.5) * t) / (Volatility * Sqr(t))
'
' Volatility * Sqr(t) * d1 = Log(MarketPrice / StrikePrice) + Volatility^2 * t/2
'
' Log(MarketPrice / StrikePrice) = Volatility * Sqr(t) * d1 - Volatility^2 * t/2
'
' MarketPrice / StrikePrice = Exp(Volatility * Sqr(t) * d1 - Volatility^2 * t/2)
'
StrikePrice = MarketPrice / Exp(Volatility * Sqr(t) * d1 - Volatility^2 * t/2)
Other Notes :
For brevity replace Application.WorksheetFunction.Ln() with Log()
There is no need cache SqT = Sqr(t) since it is only used once.
For clarity replace Volatility*Volatility with Volatility^2 as internally it does the same thing.
This is just algebra - high school math.
Take it in steps. Make sure you do the same operation to both sides to make sure that equality still holds.
Here's your starting equation:
d = {ln(m/s) + v*v*t/2}/(v*sqrt(t))
Multiply both sides by the denominator of the RHS:
d*v*sqrt(t) = ln(m/s) + v*v*t/2
Subtract v*v*t/2 from both sides:
(d*v*sqrt(t) - v*v*t/2) = ln(m/s)
Apply the exponential function to both sides, noting that exp(ln(x)) = x:
exp(d*v*sqrt(t) - v*v*t/2) = m/s
Multiply both sides by s:
s*exp(d*v*sqrt(t) - v*v*t/2) = m
Divide both sides by exp(d*v*sqrt(t) - v*v*t/2) to get the desired result:
s = m/exp(d*v*sqrt(t) - v*v*t/2)
Let's see if this function makes sense.
At t = 0 the denominator exp(0) = 1, so the strike price is equal to the market price.
As t -> infinity, we hope that the denominator gets large so s -> zero. L'Hospital's Rule will help here.

Convert 3 output to one input at NODE-RED

Question is about Node-RED for raspberry pi 3. I have 3 input that give acceleration of X,Y,Z axis. I want to make one output from these 3 inputs. For this , I use √X^2+Y^2+Z^2 formula. According to my function my output is still 3 piece and giving NaN output when i debug. What should i do in Acc to Freq function
Here is my collecting X,Y,Z info from my sql.
var str = msg.payload;
str = str[0]['IX']; // Choose last data from IX column
a = str * 10; // Scaling the value
msg.payload = a
return msg;
var str = msg.payload;
str = str[0]['IY']; // Choose last data from IY column
b = str * 10; // Scaling the value
msg.payload = b
return msg;
var str = msg.payload;
str = str[0]['IZ']; // Choose last data from IZ column
c = str * 10; // Scaling the value
msg.payload = c
return msg;
And the function that i m try to calculate one output ( Acc to Freq )
var str = msg.payload;
var a;
var b;
var c;
str = Math.pow(a^2+b^2+c^2);
d = str * 10;
msg.payload = d;
return msg;
The point to remember is that a function node runs every time a message arrives, if you send it 3 separate messages then it will run 3 times. Also each function node is totally independent of all others, you can't declare a variable in one and use it in another (well there is something called the Context, but that's not particularly useful here)
You've not actually shown your flow so we are going to have to guess a little here, but you imply that all the starting values are coming from a single SQL query that returns multiple columns. If this is the case then you have 2 options.
Just do all the calculations in one place e.g. one function node with the following:
var str = msg.payload;
var strA = str[0]['IX']; // Choose last data from IX column
var a = strA * 10; // Scaling the value
var strB = str[0]['IY']; // Choose last data from IY column
var b = strB * 10; // Scaling the value
var strC = str[0]['IZ']; // Choose last data from IZ column
var c = strC * 10; // Scaling the value
var strC = Math.pow(a^2+b^2+c^2);
var d = strC * 10;
msg.payload = d;
return msg;
You can run the output of your current 3 function nodes into a Join node set to collect 3 values. This will generate a new msg object with a payload containing an array of the 3 values. You can then modify your final function node as follows:
var a = msg.payload[0];
var b = msg.payload[1];
var c = msg.payload[2];
var d = Math.pow(a^2+b^2+c^2) * 10 ;
msg.payload = d;
return msg;

maximum volume of a box with perimeter and area given

Here's the link to the question..
http://www.codechef.com/problems/J7
I figured out that 2 edges have to be equal in order to give the maximum volume, and then used x, x, a*x as the lengths of the three edges to write the equations -
4*x + 4*x + 4*a*x = P (perimeter) and,
2*x^2 + 4*(a*x *x) = S (total area of the box)
so from the first equation I got x in terms of P and a, and then substituted it in the second equation and then got a quadratic equation with the unknown being a. and then I used the greater root of a and got x.
But this method seems to be giving the wrong answer! :|
I know that there isn't any logical error in this. Maybe some formatting error?
Here's the main code that I've written :
{
public static void main(String[] args)
{
TheBestBox box = new TheBestBox();
reader = box.new InputReader(System.in);
writer = box.new OutputWriter(System.out);
getAttributes();
writer.flush();
reader.close();
writer.close();
}
public static void getAttributes()
{
t = reader.nextInt(); // t is the number of test cases in the question
for (int i = 0; i < t; i++)
{
p = reader.nextInt(); // p is the perimeter given as input
area = reader.nextInt(); // area of the whole sheet, given as input
a = findRoot(); // the fraction by which the third side differs by the first two
side = (double) p / (4 * (2 + a)); // length of the first and the second sides (equal)
height = a * side; // assuming that the base is a square, the height has to be the side which differs from the other two
// writer.println(side * side * height);
// System.out.printf("%.2f\n", (side * side * height));
writer.println(String.format("%.2f", (side * side * height))); // just printing out the final answer
}
}
public static double findRoot() // the method to find the 2 possible fractions by which the height can differ from the other two sides and return the bigger one of them
{
double a32, b, discriminant, root1, root2;
a32 = 32 * area - p * p;
b = 32 * area - 2 * p * p;
discriminant = Math.sqrt(b * b - 4 * 8 * area * a32);
double temp;
temp = 2 * 8 * area;
root1 = (- b + discriminant) / temp;
root2 = (- b - discriminant) / temp;
return Math.max(root1, root2);
}
}
could someone please help me out with this? Thank You. :)
I also got stuck in this question and realized that can be done by making equation of V(volume) in terms of one side say 'l' and using differentiation to find maximum volume in terms of any one side 'l'.
So, equations are like this :-
P = 4(l+b+h);
S = 2(l*b+b*h+l*h);
V = l*b*h;
so equation in l for V = (l^3) - (l^2)P/4 + lS/2 -------equ(1)
After differentiation we get:-
d(V)/d(l) = 3*(l^2) - l*P/2 + S/2;
to get max V we need to equate above equation to zero(0) and get the value of l.
So, solutions to a quadratic equation will be:-
l = ( P + sqrt((P^2)-24S) ) / 24;
so substitute this l in equation(1) to get max volume.

Know slice number?

I have a DICOM series, with following origin, spacing and extent:
int nExtent[6];
double dSpacing[3];
double dOrigin[3];
m_pReader->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), nExtent);
m_pReader->GetOutput()->GetSpacing(dSpacing);
m_pReader->GetOutput()->GetOrigin(dOrigin);
where m_pReader is vtkDICOMReader object ...
dOrigin is 0, 0, 0;
dSpacing id 0.447266, 0.447266, 3.998718;
nExtent is 0, 511, 0, 511, 0, 43;
the series is AXIAL.
Now, if I slice the series through AXIAL plan, I could slice the series by 44 slices, like that:
double deltaY = 0.0;
delta += 1.0;
pReslice->Update();
double dSliceSpacing = pReslice->GetOutput()->GetSpacing()[2];
vtkMatrix4x4* pMatrix = pReslice->GetResliceAxes();
// move the dCenter dPoint that we are slicing through
double dPoint[4];
double dCenter[4];
dPoint[0] = 0.0;
dPoint[1] = 0.0;
dPoint[2] = dSliceSpacing * deltaY;
dPoint[3] = 1.0;
pMatrix->MultiplyPoint(dPoint, dCenter);
pMatrix->SetElement(0, 3, dCenter[0]);
pMatrix->SetElement(1, 3, dCenter[1]);
pMatrix->SetElement(2, 3, dCenter[2]);
Everything is allright ...
The problem: if I slice the series through CORONAL plan, the slices number are not 44 !! But how many ? How can I know the slice number if the plan is CORONAL or SAGITTAL ?
On Coronal and Sagittal slicing its more about position instead of slice index.
You need to calculate your min/max origin for each axis (x,y,z)
e.g.
myOriginMax.X = myOrigin.X + ((ImageDimension.SizeX - 1) * mySpacing.X);
You can compute your new origin in your slicing Event, where _positionDelta is your inc/dec value. (more or less pseudo code)
e.g.
double[] _origin = myImageReslice.GetResliceAxesOrigin();
if(_view == "SAGITTAL")
{
_origin[0] = Math.Min(_origin[0] + _positionDelta * mySpacing.X, myOriginMax.X);
}
else if(_view == "CORONAL")
{
_origin[1] = Math.Min(_origin[1] + _positionDelta * mySpacing.Y, myOriginMax.Y);
}
else //AXIAL
{
_origin[2] = Math.Min(_origin[2] + _positionDelta * mySpacing.Z, myOriginMax.Z);
}
myImageReslice.SetReliceAxesOrigin(_origin[0], _origin[1], _origin[2]);
Render();
Yes, it is another compute method ... in my case I have m_pReslice for axial plan, m_pReslice2 for coronal plan, and m_pReslice3 for sagittal plan ... I don't know if is the proper architecture, but applying your algorithm I arrive in the same place :)
m_pReslice->GetResliceAxesOrigin(dOrigin);
int nSizeX = nExtent[0] + nExtent[1];
int nSizeY = nExtent[2] + nExtent[3];
int nSizeZ = nExtent[4] + nExtent[5];
double dOriginMax[3];
dOriginMax[0] = dOrigin[0] + ((nSizeX - 1) * dSpacing[0]);
dOriginMax[1] = dOrigin[1] + ((nSizeY - 1) * dSpacing[1]);
dOriginMax[2] = dOrigin[2] + ((nSizeZ - 1) * dSpacing[2]);
dOrigin[0] = min(dOrigin[0] + 1.0 * dSpacing[0], dOriginMax[0]);
dOrigin[1] = min(dOrigin[1] + 1.0 * dSpacing[1], dOriginMax[1]);
dOrigin[2] = min(dOrigin[2] + 1.0 * dSpacing[2], dOriginMax[2]);
m_pReslice->SetResliceAxesOrigin(dOrigin);
this is the case for m_pReslice (axial plan) ... if I apply the algorithm for m_pReslice2 (coronal) and for m_pReslice3 (sagittal), I still don't know how may slices I have in coronal case (or sagittal) ...

Extracting vertices from scenekit

I'm having a problem with understanding scenekit geometery.
I have the default cube from Blender, and I export as collada (DAE), and can bring it into scenekit.... all good.
Now I want to see the vertices for the cube. In the DAE I can see the following for the "Cube-mesh-positions-array",
"1 1 -1 1 -1 -1 -1 -0.9999998 -1 -0.9999997 1 -1 1 0.9999995 1 0.9999994 -1.000001 1 -1 -0.9999997 1 -1 1 1"
Now what I'd like to do in scenekit, is get the vertices back, using something like the following:
SCNGeometrySource *vertexBuffer = [[cubeNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex] objectAtIndex:0];
If I process the vertexBuffer (I've tried numerous methods of looking at the data), it doesn't seem correct.
Can somebody explain what "SCNGeometrySourceSemanticVertex" is giving me, and how to extract the vertex data properly? What I'd like to see is:
X = "float"
Y = "float"
Z = "float"
Also I was investigating the following class / methods, which looked promising (some good data values here), but the data from gmpe appears empty, is anybody able to explain what the data property of "SCNGeometryElement" contains?
SCNGeometryElement *gmpe = [theCurrentNode.geometry geometryElementAtIndex:0];
Thanks, assistance much appreciated,
D
The geometry source
When you call geometrySourcesForSemantic: you are given back an array of SCNGeometrySource objects with the given semantic in your case the sources for the vertex data).
This data could have been encoded in many different ways and a multiple sources can use the same data with a different stride and offset. The source itself has a bunch of properties for you to be able to decode the data like for example
dataStride
dataOffset
vectorCount
componentsPerVector
bytesPerComponent
You can use combinations of these to figure out which parts of the data to read and make vertices out of them.
Decoding
The stride tells you how many bytes you should step to get to the next vector and the offset tells you how many bytes offset from the start of that vector you should offset before getting to the relevant pars of the data for that vector. The number of bytes you should read for each vector is componentsPerVector * bytesPerComponent
Code to read out all the vertices for a single geometry source would look something like this
// Get the vertex sources
NSArray *vertexSources = [geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
// Get the first source
SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources
NSInteger stride = vertexSource.dataStride; // in bytes
NSInteger offset = vertexSource.dataOffset; // in bytes
NSInteger componentsPerVector = vertexSource.componentsPerVector;
NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
NSInteger vectorCount = vertexSource.vectorCount;
SCNVector3 vertices[vectorCount]; // A new array for vertices
// for each vector, read the bytes
for (NSInteger i=0; i<vectorCount; i++) {
// Assuming that bytes per component is 4 (a float)
// If it was 8 then it would be a double (aka CGFloat)
float vectorData[componentsPerVector];
// The range of bytes for this vector
NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset
bytesPerVector); // and read the lenght of one vector
// Read into the vector data buffer
[vertexSource.data getBytes:&vectorData range:byteRange];
// At this point you can read the data from the float array
float x = vectorData[0];
float y = vectorData[1];
float z = vectorData[2];
// ... Maybe even save it as an SCNVector3 for later use ...
vertices[i] = SCNVector3Make(x, y, z);
// ... or just log it
NSLog(#"x:%f, y:%f, z:%f", x, y, z);
}
The geometry element
This will give you all the vertices but won't tell you how they are used to construct the geometry. For that you need the geometry element that manages the indices for the vertices.
You can get the number of geometry elements for a piece of geometry from the geometryElementCount property. Then you can get the different elements using geometryElementAtIndex:.
The element can tell you if the vertices are used a individual triangles or a triangle strip. It also tells you the bytes per index (the indices may have been ints or shorts which will be necessary to decode its data.
Here is an extension method if the data isn't contiguous (the vector size isn't equal to the stride) which can be the case when the geometry is loaded from a DAE file. It also doesn't use copyByte function.
extension SCNGeometry{
/**
Get the vertices (3d points coordinates) of the geometry.
- returns: An array of SCNVector3 containing the vertices of the geometry.
*/
func vertices() -> [SCNVector3]? {
let sources = self.sources(for: .vertex)
guard let source = sources.first else{return nil}
let stride = source.dataStride / source.bytesPerComponent
let offset = source.dataOffset / source.bytesPerComponent
let vectorCount = source.vectorCount
return source.data.withUnsafeBytes { (buffer : UnsafePointer<Float>) -> [SCNVector3] in
var result = Array<SCNVector3>()
for i in 0...vectorCount - 1 {
let start = i * stride + offset
let x = buffer[start]
let y = buffer[start + 1]
let z = buffer[start + 2]
result.append(SCNVector3(x, y, z))
}
return result
}
}
}
The Swift Version
The Objective-C version and this are essentially identical.
let planeSources = _planeNode?.geometry?.geometrySourcesForSemantic(SCNGeometrySourceSemanticVertex)
if let planeSource = planeSources?.first {
let stride = planeSource.dataStride
let offset = planeSource.dataOffset
let componentsPerVector = planeSource.componentsPerVector
let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent
let vectors = [SCNVector3](count: planeSource.vectorCount, repeatedValue: SCNVector3Zero)
let vertices = vectors.enumerate().map({
(index: Int, element: SCNVector3) -> SCNVector3 in
var vectorData = [Float](count: componentsPerVector, repeatedValue: 0)
let byteRange = NSMakeRange(index * stride + offset, bytesPerVector)
planeSource.data.getBytes(&vectorData, range: byteRange)
return SCNVector3Make(vectorData[0], vectorData[1], vectorData[2])
})
// You have your vertices, now what?
}
Here's a Swift 5.3 version, based on the other answers, and that also supports a bytesPerComponent different from 4 (untested for size different from 4 though):
extension SCNGeometrySource {
var vertices: [SCNVector3] {
let stride = self.dataStride
let offset = self.dataOffset
let componentsPerVector = self.componentsPerVector
let bytesPerVector = componentsPerVector * self.bytesPerComponent
func vectorFromData<FloatingPoint: BinaryFloatingPoint>(_ float: FloatingPoint.Type, index: Int) -> SCNVector3 {
assert(bytesPerComponent == MemoryLayout<FloatingPoint>.size)
let vectorData = UnsafeMutablePointer<FloatingPoint>.allocate(capacity: componentsPerVector)
defer {
vectorData.deallocate()
}
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
let rangeStart = index * stride + offset
self.data.copyBytes(to: buffer, from: rangeStart..<(rangeStart + bytesPerVector))
return SCNVector3(
CGFloat.NativeType(vectorData[0]),
CGFloat.NativeType(vectorData[1]),
CGFloat.NativeType(vectorData[2])
)
}
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: self.vectorCount)
return vectors.indices.map { index -> SCNVector3 in
switch bytesPerComponent {
case 4:
return vectorFromData(Float32.self, index: index)
case 8:
return vectorFromData(Float64.self, index: index)
case 16:
return vectorFromData(Float80.self, index: index)
default:
return SCNVector3Zero
}
}
}
}
// call this function _ = vertices(node: mySceneView.scene!.rootNode)
// I have get the volume in Swift 4.2 :--- this function
func vertices(node:SCNNode) -> [SCNVector3] {
let planeSources1 = node.childNodes.first?.geometry
let planeSources = planeSources1?.sources(for: SCNGeometrySource.Semantic.vertex)
if let planeSource = planeSources?.first {
let stride = planeSource.dataStride
let offset = planeSource.dataOffset
let componentsPerVector = planeSource.componentsPerVector
let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: planeSource.vectorCount)
let vertices = vectors.enumerated().map({
(index: Int, element: SCNVector3) -> SCNVector3 in
let vectorData = UnsafeMutablePointer<Float>.allocate(capacity: componentsPerVector)
let nsByteRange = NSMakeRange(index * stride + offset, bytesPerVector)
let byteRange = Range(nsByteRange)
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
planeSource.data.copyBytes(to: buffer, from: byteRange)
return SCNVector3Make(buffer[0], buffer[1], buffer[2])
})
var totalVolume = Float()
var x1 = Float(),x2 = Float(),x3 = Float(),y1 = Float(),y2 = Float(),y3 = Float(),z1 = Float(),z2 = Float(),z3 = Float()
var i = 0
while i < vertices.count{
x1 = vertices[i].x;
y1 = vertices[i].y;
z1 = vertices[i].z;
x2 = vertices[i + 1].x;
y2 = vertices[i + 1].y;
z2 = vertices[i + 1].z;
x3 = vertices[i + 2].x;
y3 = vertices[i + 2].y;
z3 = vertices[i + 2].z;
totalVolume +=
(-x3 * y2 * z1 +
x2 * y3 * z1 +
x3 * y1 * z2 -
x1 * y3 * z2 -
x2 * y1 * z3 +
x1 * y2 * z3);
i = i + 3
}
totalVolume = totalVolume / 6;
volume = "\(totalVolume)"
print("Volume Volume Volume Volume Volume Volume Volume :\(totalVolume)")
lbl_valume.text = "\(clean(String(totalVolume))) cubic mm"
}
return[]
}
With swift 3.1 you can extract vertices from SCNGeometry in a much faster and shorter way:
func vertices(node:SCNNode) -> [SCNVector3] {
let vertexSources = node.geometry?.getGeometrySources(for: SCNGeometrySource.Semantic.vertex)
if let vertexSource = vertexSources?.first {
let count = vertexSource.data.count / MemoryLayout<SCNVector3>.size
return vertexSource.data.withUnsafeBytes {
[SCNVector3](UnsafeBufferPointer<SCNVector3>(start: $0, count: count))
}
}
return []
}
...
Today i've noted that on osx this not going to work correct. This happens because on iOS SCNVector3 build with Float and on osx CGFloat (only apple good do smth simple so suffering). So I had to tweak the code for osx but this not gonna work as fast as on iOS.
func vertices() -> [SCNVector3] {
let vertexSources = sources(for: SCNGeometrySource.Semantic.vertex)
if let vertexSource = vertexSources.first {
let count = vertexSource.vectorCount * 3
let values = vertexSource.data.withUnsafeBytes {
[Float](UnsafeBufferPointer<Float>(start: $0, count: count))
}
var vectors = [SCNVector3]()
for i in 0..<vertexSource.vectorCount {
let offset = i * 3
vectors.append(SCNVector3Make(
CGFloat(values[offset]),
CGFloat(values[offset + 1]),
CGFloat(values[offset + 2])
))
}
return vectors
}
return []
}
For someone like me want to extract data of face from SCNGeometryElement.
Notice I only consider primtive type is triangle and index size is 2 or 4.
void extractInfoFromGeoElement(NSString* scenePath){
NSURL *url = [NSURL fileURLWithPath:scenePath];
SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:nil];
SCNGeometry *geo = scene.rootNode.childNodes.firstObject.geometry;
SCNGeometryElement *elem = geo.geometryElements.firstObject;
NSInteger componentOfPrimitive = (elem.primitiveType == SCNGeometryPrimitiveTypeTriangles) ? 3 : 0;
if (!componentOfPrimitive) {//TODO: Code deals with triangle primitive only
return;
}
for (int i=0; i<elem.primitiveCount; i++) {
void *idxsPtr = NULL;
int stride = 3*i;
if (elem.bytesPerIndex == 2) {
short *idxsShort = malloc(sizeof(short)*3);
idxsPtr = idxsShort;
}else if (elem.bytesPerIndex == 4){
int *idxsInt = malloc(sizeof(int)*3);
idxsPtr = idxsInt;
}else{
NSLog(#"unknow index type");
return;
}
[elem.data getBytes:idxsPtr range:NSMakeRange(stride*elem.bytesPerIndex, elem.bytesPerIndex*3)];
if (elem.bytesPerIndex == 2) {
NSLog(#"triangle %d : %d, %d, %d\n",i,*(short*)idxsPtr,*((short*)idxsPtr+1),*((short*)idxsPtr+2));
}else{
NSLog(#"triangle %d : %d, %d, %d\n",i,*(int*)idxsPtr,*((int*)idxsPtr+1),*((int*)idxsPtr+2));
}
//Free
free(idxsPtr);
}
}
The Swift 3 version:
// `plane` is some kind of `SCNGeometry`
let planeSources = plane.geometry.sources(for: SCNGeometrySource.Semantic.vertex)
if let planeSource = planeSources.first {
let stride = planeSource.dataStride
let offset = planeSource.dataOffset
let componentsPerVector = planeSource.componentsPerVector
let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: planeSource.vectorCount)
let vertices = vectors.enumerated().map({
(index: Int, element: SCNVector3) -> SCNVector3 in
let vectorData = UnsafeMutablePointer<Float>.allocate(capacity: componentsPerVector)
let nsByteRange = NSMakeRange(index * stride + offset, bytesPerVector)
let byteRange = Range(nsByteRange)
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
planeSource.data.copyBytes(to: buffer, from: byteRange)
let vector = SCNVector3Make(buffer[0], buffer[1], buffer[2])
})
// Use `vertices` here: vertices[0].x, vertices[0].y, vertices[0].z
}
OK, here is another Swift 5.5 version based on Oliver's answer.
extension SCNGeometry{
/**
Get the vertices (3d points coordinates) of the geometry.
- returns: An array of SCNVector3 containing the vertices of the geometry.
*/
func vertices() -> [SCNVector3]? {
let sources = self.sources(for: .vertex)
guard let source = sources.first else{return nil}
let stride = source.dataStride / source.bytesPerComponent
let offset = source.dataOffset / source.bytesPerComponent
let vectorCount = source.vectorCount
return source.data.withUnsafeBytes { dataBytes in
let buffer: UnsafePointer<Float> = dataBytes.baseAddress!.assumingMemoryBound(to: Float.self)
var result = Array<SCNVector3>()
for i in 0...vectorCount - 1 {
let start = i * stride + offset
let x = buffer[start]
let y = buffer[start + 1]
let z = buffer[start + 2]
result.append(SCNVector3(x, y, z))
}
return result
}
}
}
To use it you simply create a standard shape from which you can extract the vertex and rebuild the index.
let g = SCNSphere(radius: 1)
let newNode = SCNNode(geometry: g)
let vectors = newNode.geometry?.vertices()
var indices:[Int32] = []
for i in stride(from: 0, to: vectors!.count, by: 1) {
indices.append(Int32(i))
indices.append(Int32(i+1))
}
return self.createGeometry(
vertices:vectors!, indices: indices,
primitiveType: SCNGeometryPrimitiveType.line)
The createGeometry extension can be found here
It draws this...

Resources