Skip to content

Commit e03e0f0

Browse files
authored
Add a native mesh rasterization (#36)
* Add native mesh rasterization
1 parent 21fcce4 commit e03e0f0

File tree

5 files changed

+223
-2
lines changed

5 files changed

+223
-2
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
9+
## [2.1.5-RC1] - 2020-03-09
10+
### Added
11+
- Add a native mesh rasterization [#36](https://github.com/PDAL/java/pull/36)
12+
813
### Changed
914
- Move Scala syntax into the pipeline.syntax package [#33](https://github.com/PDAL/java/issues/33)
1015

@@ -88,7 +93,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8893
### Changed
8994
- Moved from the PDAL repo and established own lifecycle.
9095

91-
[Unreleased]: https://github.com/PDAL/java/compare/2.1.4...HEAD
96+
[Unreleased]: https://github.com/PDAL/java/compare/2.1.5-RC1...HEAD
97+
[2.1.5-RC1]: https://github.com/PDAL/java/compare/2.1.4...2.1.5-RC1
9298
[2.1.4]: https://github.com/PDAL/java/compare/2.1.3...2.1.4
9399
[2.1.3]: https://github.com/PDAL/java/compare/2.1.2...2.1.3
94100
[2.1.2]: https://github.com/PDAL/java/compare/2.0.0...2.1.2

core/src/main/scala/io/pdal/PointView.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,23 @@ class PointView extends Native {
145145
def getY(packedPoint: Array[Byte]): Double = getDouble(packedPoint, DimType.Y)
146146
def getZ(packedPoint: Array[Byte]): Double = getDouble(packedPoint, DimType.Z)
147147

148+
def getTriangularMesh(): TriangularMesh = getTriangularMesh("")
149+
150+
def rasterizeTriangularMesh(extent: Array[Double], cols: Int, rows: Int): Array[Double] =
151+
rasterizeTriangularMesh(extent, cols, rows, DimType.Z)
152+
def rasterizeTriangularMesh(extent: Array[Double], cols: Int, rows: Int, dim: String): Array[Double] =
153+
rasterizeTriangularMesh(extent, cols, rows, findDimType(dim))
154+
def rasterizeTriangularMesh(extent: Array[Double], cols: Int, rows: Int, dim: DimType): Array[Double] =
155+
rasterizeTriangularMesh(extent, cols, rows, dim, "")
156+
148157
@native def layout(): PointLayout
149158
@native def size(): Int
150159
@native def empty(): Boolean
151160
@native def getCrsProj4(): String
152161
@native def getCrsWKT(pretty: Boolean): String
153162
@native def getPackedPoint(idx: Long, dims: Array[DimType]): Array[Byte]
154163
@native def getPackedPoints(dims: Array[DimType]): Array[Byte]
155-
@native def getTriangularMesh(name: String = ""): TriangularMesh
164+
@native def getTriangularMesh(name: String): TriangularMesh
165+
@native def rasterizeTriangularMesh(extent: Array[Double], cols: Int, rows: Int, dim: DimType, name: String): Array[Double]
156166
@native def close(): Unit
157167
}

core/src/test/scala/io/pdal/PipelineSpec.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,5 +242,33 @@ class PipelineSpec extends TestEnvironmentSpec {
242242
pv.close()
243243
pvi.close()
244244
}
245+
246+
it("should rasterize mesh") {
247+
val pvi = pipelineDelaunay.getPointViews()
248+
val pv = pvi.next()
249+
250+
val raster = pv.rasterizeTriangularMesh(Array(635619.85, 848899.7, 638982.55, 853535.43), 100, 100)
251+
252+
val (mi, ma) = {
253+
val sorted = raster.filter(!_.isNaN).sorted
254+
sorted.head -> sorted.last
255+
}
256+
257+
mi shouldBe 406.915 +- 1e-3
258+
ma shouldBe 582.022 +- 1e-3
259+
260+
pv.close()
261+
pvi.close()
262+
}
263+
264+
it("should not rasterize mesh if it was not generated") {
265+
val pvi = pipeline.getPointViews()
266+
val pv = pvi.next()
267+
268+
intercept[ExecutionException] { pv.rasterizeTriangularMesh(Array(), 0, 0) }
269+
270+
pv.close()
271+
pvi.close()
272+
}
245273
}
246274
}

native/src/include/io_pdal_PointView.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

native/src/io_pdal_PointView.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#include <stdio.h>
3535
#include <vector>
36+
#include <float.h>
3637
#include "io_pdal_PointView.h"
3738
#include "JavaPipeline.hpp"
3839
#include "JavaTriangularMeshIterator.hpp"
@@ -52,6 +53,8 @@ using pdal::Dimension::Id;
5253
using pdal::PointId;
5354
using pdal::DimTypeList;
5455
using pdal::SpatialReference;
56+
using pdal::TriangularMesh;
57+
using pdal::Triangle;
5558
using pdal::DimType;
5659
using pdal::pdal_error;
5760

@@ -225,6 +228,172 @@ JNIEXPORT jobject JNICALL Java_io_pdal_PointView_getTriangularMesh
225228
return mi;
226229
}
227230

231+
JNIEXPORT jdoubleArray JNICALL Java_io_pdal_PointView_rasterizeTriangularMesh
232+
(JNIEnv *env, jobject obj, jdoubleArray extent, jint cols, jint rows, jobject jDimType, jstring name)
233+
{
234+
std::string cname = std::string(env->GetStringUTFChars(name, 0));
235+
236+
PointViewRawPtr *pvrp = getHandle<PointViewRawPtr>(env, obj);
237+
PointViewPtr pv = pvrp->shared_pointer;
238+
TriangularMesh *mesh = pv->mesh(cname);
239+
240+
if(mesh == NULL)
241+
{
242+
throwExecutionException(env, "No mesh was generated. Check that the appropriate filter is a part of a PDAL Pipeline.");
243+
return env->NewDoubleArray(0);
244+
}
245+
246+
jclass cDimType = env->GetObjectClass(jDimType);
247+
jfieldID fid = env->GetFieldID(cDimType, "id", "Ljava/lang/String;");
248+
jstring jid = reinterpret_cast<jstring>(env->GetObjectField(jDimType, fid));
249+
250+
PointLayoutPtr pl = pv->layout();
251+
DimType dimType = pl->findDimType(std::string(env->GetStringUTFChars(jid, 0)));
252+
Id dimId = dimType.m_id;
253+
254+
int size = mesh->size();
255+
256+
int length = cols * rows;
257+
258+
jdouble *ext = env->GetDoubleArrayElements(extent, 0);
259+
260+
double exmin = ext[0];
261+
double eymin = ext[1];
262+
double exmax = ext[2];
263+
double eymax = ext[3];
264+
265+
env->ReleaseDoubleArrayElements(extent, ext, JNI_ABORT);
266+
267+
double w = (exmax - exmin) / cols;
268+
double h = (eymax - eymin) / rows;
269+
270+
jdoubleArray result = env->NewDoubleArray(length);
271+
272+
double buffer[length];
273+
std::fill(buffer, buffer + sizeof(buffer) / sizeof(double), strtod("NaN", NULL));
274+
275+
for (int id = 0; id < size; id++)
276+
{
277+
const Triangle& tri = (*mesh)[id];
278+
PointId a = tri.m_a;
279+
PointId b = tri.m_b;
280+
PointId c = tri.m_c;
281+
282+
double v1x = pv->getFieldAs<double>(Id::X, a);
283+
double v1y = pv->getFieldAs<double>(Id::Y, a);
284+
double v1z = pv->getFieldAs<double>(dimId, a);
285+
double s1x = pv->getFieldAs<double>(Id::X, c);
286+
double s1y = pv->getFieldAs<double>(Id::Y, c);
287+
288+
double v2x = pv->getFieldAs<double>(Id::X, b);
289+
double v2y = pv->getFieldAs<double>(Id::Y, b);
290+
double v2z = pv->getFieldAs<double>(dimId, b);
291+
double s2x = pv->getFieldAs<double>(Id::X, a);
292+
double s2y = pv->getFieldAs<double>(Id::Y, a);
293+
294+
double v3x = pv->getFieldAs<double>(Id::X, c);
295+
double v3y = pv->getFieldAs<double>(Id::Y, c);
296+
double v3z = pv->getFieldAs<double>(dimId, c);
297+
double s3x = pv->getFieldAs<double>(Id::X, b);
298+
double s3y = pv->getFieldAs<double>(Id::Y, b);
299+
300+
double determinant = (v2y - v3y) * (v1x - v3x) + (v3x - v2x) * (v1y - v3y);
301+
302+
double ymin = std::min(v1y, std::min(v2y, v3y));
303+
double ymax = std::max(v1y, std::max(v2y, v3y));
304+
305+
double scanrow0 = std::max(std::ceil((ymin - eymin) / h - 0.5), 0.0);
306+
double scany = eymin + scanrow0 * h + h / 2.0;
307+
308+
while (scany < eymax && scany < ymax)
309+
{
310+
// get x at y for edge
311+
double xmin = -DBL_MAX;
312+
double xmax = DBL_MAX;
313+
314+
if(s1y != v1y)
315+
{
316+
double t = (scany - v1y) / (s1y - v1y);
317+
double xAtY1 = v1x + t * (s1x - v1x);
318+
319+
if(v1y < s1y)
320+
{
321+
// Lefty
322+
if(xmin < xAtY1) { xmin = xAtY1; }
323+
}
324+
else
325+
{
326+
// Righty
327+
if(xAtY1 < xmax) { xmax = xAtY1; }
328+
}
329+
}
330+
331+
if(s2y != v2y)
332+
{
333+
double t = (scany - v2y) / (s2y - v2y);
334+
double xAtY2 = v2x + t * (s2x - v2x);
335+
336+
if(v2y < s2y)
337+
{
338+
// Lefty
339+
if(xmin < xAtY2) { xmin = xAtY2; }
340+
}
341+
else
342+
{
343+
// Righty
344+
if(xAtY2 < xmax) { xmax = xAtY2; }
345+
}
346+
}
347+
348+
if(s3y != v3y)
349+
{
350+
double t = (scany - v3y) / (s3y - v3y);
351+
double xAtY3 = v3x + t * (s3x - v3x);
352+
353+
if(v3y < s3y)
354+
{
355+
// Lefty
356+
if(xmin < xAtY3) { xmin = xAtY3; }
357+
}
358+
else
359+
{
360+
// Righty
361+
if(xAtY3 < xmax) { xmax = xAtY3; }
362+
}
363+
}
364+
365+
double scancol0 = std::max(std::ceil((xmin - exmin) / w - 0.5), 0.0);
366+
double scanx = exmin + scancol0 * w + w / 2;
367+
368+
while (scanx < exmax && scanx < xmax)
369+
{
370+
int col = (int)((scanx - exmin) / w);
371+
int row = (int)((eymax - scany) / h);
372+
373+
if(0 <= col && col < cols && 0 <= row && row < rows)
374+
{
375+
376+
double lambda1 = ((v2y - v3y) * (scanx - v3x) + (v3x - v2x) * (scany - v3y)) / determinant;
377+
double lambda2 = ((v3y - v1y) * (scanx - v3x) + (v1x - v3x) * (scany - v3y)) / determinant;
378+
double lambda3 = 1.0 - lambda1 - lambda2;
379+
380+
double z = lambda1 * v1z + lambda2 * v2z + lambda3 * v3z;
381+
382+
buffer[row * cols + col] = z;
383+
}
384+
385+
scanx += w;
386+
}
387+
388+
scany += h;
389+
}
390+
}
391+
392+
env->SetDoubleArrayRegion(result, 0, length, buffer);
393+
394+
return result;
395+
}
396+
228397
JNIEXPORT void JNICALL Java_io_pdal_PointView_close
229398
(JNIEnv *env, jobject obj)
230399
{

0 commit comments

Comments
 (0)