-- lua-tikz3dtools-parametric-objects.lua local mm = require "lua-tikz3dtools-matrix-math" local rtc = require "lua-tikz3dtools-register-tex-cmd" local ENV_ = {} for k, v in pairs(_G) do ENV_[k] = v end for k, v in pairs(mm) do ENV_[k] = v end for k, v in pairs(math) do ENV_[k] = v end local segments = {} local observer_dir = { { 0, 0, -1, 1} } local observer_pos = { { 0, 0, 0, 1} } local function single_string_expression(str) return load(("return %s"):format(str), "expression", "t", ENV_)() end local function single_string_function(str) return load(("return function(u) return %s end"):format(str), "expression", "t", ENV_)() end local function double_string_function(str) return load(("return function(u,v) return %s end"):format(str), "expression", "t", ENV_)() end local function append_label(hash) local x = hash.x local y = hash.y local z = hash.z local text = hash.text local name = hash.name local transformation = hash.transformation x = single_string_expression(x) y = single_string_expression(y) z = single_string_expression(z) transformation = single_string_expression(transformation) local function parametric_point() return { { x, y, z, 1 } } end local A = parametric_point() local the_segment = mm.matrix_multiply(A, transformation) table.insert( segments, { segment = the_segment, text = text, name = name } ) end local function append_point(hash) local x = hash.x local y = hash.y local z = hash.z local draw_options = hash.draw_options local fill_options = hash.fill_options local name = hash.name local transformation = hash.transformation x = single_string_expression(x) y = single_string_expression(y) z = single_string_expression(z) transformation = single_string_expression(transformation) local function parametric_point() return { { x, y, z, 1 } } end local A = parametric_point() local the_segment = mm.matrix_multiply(A, transformation) table.insert( segments, { segment = the_segment, draw_options = draw_options, fill_options = fill_options, name = name } ) end local function append_curve(hash) local u_start = hash.u_start local u_stop = hash.u_stop local u_samples = hash.u_samples local x = hash.x local y = hash.y local z = hash.z local draw_options = hash.draw_options local name = hash.name local transformation = hash.transformation x = single_string_function(x) y = single_string_function(y) z = single_string_function(z) u_start = single_string_expression(u_start) u_stop = single_string_expression(u_stop) u_samples = single_string_expression(u_samples) transformation = single_string_expression(transformation) local u_step = (u_stop - u_start) / (u_samples - 1) local function parametric_curve(u) return { { x(u), y(u), z(u), 1 } } end for i = 0, u_samples - 2 do local u = u_start + i * u_step local A = parametric_curve(u) local B = parametric_curve(u+u_step) local the_segment = mm.matrix_multiply({ A[1], B[1] }, transformation) table.insert( segments, { segment = the_segment, draw_options = draw_options, fill_options = fill_options, name = name } ) end end local function append_surface(hash) local u_start = hash.u_start local u_stop = hash.u_stop local u_samples = hash.u_samples local v_start = hash.v_start local v_stop = hash.v_stop local v_samples = hash.v_samples local x = hash.x local y = hash.y local z = hash.z local draw_options = hash.draw_options local fill_options = hash.fill_options local name = hash.name local transformation = hash.transformation x = double_string_function(x) y = double_string_function(y) z = double_string_function(z) u_start = single_string_expression(u_start) u_stop = single_string_expression(u_stop) u_samples = single_string_expression(u_samples) v_start = single_string_expression(v_start) v_stop = single_string_expression(v_stop) v_samples = single_string_expression(v_samples) transformation = single_string_expression(transformation) local u_step = (u_stop - u_start) / (u_samples - 1) local v_step = (v_stop - v_start) / (v_samples - 1) local function parametric_surface(u, v) return { x(u,v), y(u,v), z(u,v), 1 } end for i = 0, u_samples - 2 do local u = u_start + i * u_step for j = 0, v_samples - 2 do local v = v_start + j * v_step local A = parametric_surface(u, v) local B = parametric_surface(u + u_step, v) local C = parametric_surface(u, v + v_step) local D = parametric_surface(u + u_step, v + v_step) local the_segment1 = mm.matrix_multiply({ A, B, D },transformation) local the_segment2 = mm.matrix_multiply({ A, C, D },transformation) table.insert( segments, { segment = the_segment1, draw_options = draw_options, fill_options = fill_options, name = name } ) table.insert( segments, { segment = the_segment2, draw_options = draw_options, fill_options = fill_options, name = name } ) end end end local function is_point_in_triangle(point,triangle) local P,Q,R = table.unpack(triangle) P,Q,R = {P},{Q},{R} local cross_PQ = mm.cross_product( mm.matrix_subtract(Q,P) ,point ) local cross_QR = mm.cross_product( mm.matrix_subtract(R,Q) ,point ) local cross_RP = mm.cross_product( mm.matrix_subtract(P,R) ,point ) local sign_1 = mm.sign(cross_PQ[1][1]) local sign_2 = mm.sign(cross_QR[1][1]) local sign_3 = mm.sign(cross_RP[1][1]) local sign_4 = mm.sign(cross_PQ[1][2]) local sign_5 = mm.sign(cross_QR[1][2]) local sign_6 = mm.sign(cross_RP[1][2]) local sign_7 = mm.sign(cross_PQ[1][3]) local sign_8 = mm.sign(cross_QR[1][3]) local sign_9 = mm.sign(cross_RP[1][3]) if ( (sign_1 == sign_2 and sign_2 == sign_3) and (sign_4 == sign_5 and sign_5 == sign_6) and (sign_7 == sign_8 and sign_8 == sign_9) ) then return true else return false end end local function compare_triangles(triangle_1,triangle_2) if #triangle_1.segment == 1 or #triangle_2.segment == 1 then local function depth_mid(seg) if #seg == 1 then -- line/curve segment: midpoint of S, E local S = seg[1] local mid = {{ S[1],S[2],S[3],1 }} return mm.dot_product(mid, observer_dir) else -- triangle: centroid of P, Q, R local P, Q = seg[1], seg[2] local cent = {{ (P[1] + Q[1]) / 2, (P[2] + Q[2]) / 2, (P[3] + Q[3]) / 2, 1 }} return mm.dot_product(cent, observer_dir) end end local a = depth_mid(triangle_1.segment) local b = depth_mid(triangle_2.segment) return a > b end -- 1) if *either* is a 2‐point line/curve (#==3), depth‐sort by midpoint along observer if #triangle_1.segment == 2 or #triangle_2.segment == 2 then local function depth_mid(seg) if #seg == 2 then -- line/curve segment: midpoint of S, E local S, E = seg[1], seg[2] local mid = {{ (S[1] + E[1]) / 2, (S[2] + E[2]) / 2, (S[3] + E[3]) / 2, 1 }} return mm.dot_product(mid, observer_dir) else -- triangle: centroid of P, Q, R local P, Q, R = seg[1], seg[2], seg[3] local cent = {{ (P[1] + Q[1] + R[1]) / 3, (P[2] + Q[2] + R[2]) / 3, (P[3] + Q[3] + R[3]) / 3, 1 }} return mm.dot_product(cent, observer_dir) end end local a = depth_mid(triangle_1.segment) local b = depth_mid(triangle_2.segment) return a > b end if #triangle_1.segment > 3 or #triangle_2.segment > 3 then local function depth_mid(segment) local x, y, z = 0, 0, 0 local n = #segment for i = 1, n do local pt = segment[i] x = x + pt[1] y = y + pt[2] z = z + pt[3] end local centroid = {{x / n, y / n, z / n, 1}} return mm.dot_product(centroid, observer_dir) end return depth_mid(triangle_1.segment) > depth_mid(triangle_2.segment) end --- local P_1, Q_1, R_1 = table.unpack(triangle_1.segment) local P_2, Q_2, R_2 = table.unpack(triangle_2.segment) P_1, Q_1, R_1 = {P_1}, {Q_1}, {R_1} P_2, Q_2, R_2 = {P_2}, {Q_2}, {R_2} local observer_basis = mm.get_observer_plane_basis(observer_dir) local P_1_projection = mm.project_point_onto_basis(P_1,observer_basis) local Q_1_projection = mm.project_point_onto_basis(Q_1,observer_basis) local R_1_projection = mm.project_point_onto_basis(R_1,observer_basis) local P_2_projection = mm.project_point_onto_basis(P_2,observer_basis) local Q_2_projection = mm.project_point_onto_basis(Q_2,observer_basis) local R_2_projection = mm.project_point_onto_basis(R_2,observer_basis) -- compare to triangle 1 local P_test = is_point_in_triangle(P_2_projection,{P_1_projection[1],Q_1_projection[1],R_1_projection[1]}) local Q_test = is_point_in_triangle(Q_2_projection,{P_1_projection[1],Q_1_projection[1],R_1_projection[1]}) local R_test = is_point_in_triangle(R_2_projection,{P_1_projection[1],Q_1_projection[1],R_1_projection[1]}) if (P_test or Q_test or R_test) then local test if P_test then test = "P" else if Q_test then test = "Q" else test = "R" end end local normal_1 = mm.cross_product( mm.matrix_subtract(Q_1,P_1) ,mm.matrix_subtract(R_1,P_1) ) if mm.dot_product(normal_1,observer_dir) < 1 then normal_1 = mm.matrix_scale(-1,normal_1) end local signed_distance_to_plane if test == "P" then signed_distance_to_plane = mm.norm( mm.matrix_add( P_2 ,mm.matrix_scale(-1,P_2_projection) ) ) if ( mm.dot_product( mm.matrix_subtract(P_2,P_2_projection) ,normal_1 ) < 1 ) then signed_distance_to_plane = -signed_distance_to_plane end if mm.sign(signed_distance_to_plane) == "positive" then return true else return false end end if test == "Q" then signed_distance_to_plane = mm.norm( mm.matrix_subtract(Q_2,Q_2_projection) ) if ( mm.dot_product( mm.matrix_subtract(Q_2,Q_2_projection) ,normal_1 ) < 0 ) then signed_distance_to_plane = -signed_distance_to_plane end if mm.sign(signed_distance_to_plane) == "positive" then return true else return false end end if test == "R" then signed_distance_to_plane = mm.norm( mm.matrix_subtract(R_2,R_2_projection) ) if ( mm.dot_product( mm.matrix_subtract(R_2,R_2_projection) ,normal_1 ) < 0 ) then signed_distance_to_plane = -signed_distance_to_plane end if mm.sign(signed_distance_to_plane) == "positive" then return true else return false end end else local midpoint_1 = mm.midpoint({P_1[1],Q_1[1],R_1[1]}) local midpoint_2 = mm.midpoint({P_2[1],Q_2[1],R_2[1]}) local dot_product_1 = mm.dot_product(midpoint_1,observer_dir) local dot_product_2 = mm.dot_product(midpoint_2,observer_dir) return dot_product_1 > dot_product_2 end return false -- test end local function get_line(hash) local a1 = hash.a1 local b1 = hash.b1 local c1 = hash.c1 local d1 = hash.d1 local a2 = hash.a2 local b2 = hash.b2 local c2 = hash.c2 local d2 = hash.d2 local xmin = hash.xmin local xmax = hash.xmax local ymin = hash.ymin local ymax = hash.ymax local zmin = hash.zmin local zmax = hash.zmax local function Lyofx(x) return ( ( ( d2 - c2 * d1 / c1 ) - ( a2 - c2 * a1 / c1 ) * x ) / ( b2 - c2 * b1 / c1 ) ) end local function Lzofx(x) return ( ( ( d2 - b2 * d1 / b1 ) - ( a2 - b2 * a1 / b1 ) * x ) / ( c2 - b2 * c1 / b1 ) ) end local function Lzofy(y) return ( ( ( d2 - a2 * d1 / a1 ) - ( b2 - a2 * b1 / a1 ) * y ) / ( c2 - a2 * c1 / a1 ) ) end local function Lxofy(y) return ( ( ( d2 - c2 * d1 / c1 ) - ( b2 - c2 * b1 / c1 ) * y ) / ( a2 - c2 * a1 / c1 ) ) end local function Lyofz(z) return ( ( ( d2 - a2 * d1 / a1 ) - ( c2 - a2 * c1 / a1 ) * z ) / ( b2 - a2 * b1 / a1 ) ) end local function Lxofz(z) return ( ( ( d2 - b2 * d1 / b1 ) - ( c2 - b2 * c1 / b1 ) * z ) / ( a2 - b2 * a1 / b1 ) ) end local startx, starty, startz local endx, endy, endz if not ( math.abs(b2-c2*b1/c1)<0.0001 or math.abs(c2-b2*c1/b1)<0.0001 ) then startx = xmin starty = Lyofx(xmin) startz = Lzofx(xmin) endx = xmax endy = Lyofx(xmax) endz = Lzofx(xmax) -- y coord if endy>ymax then endx = Lxofy(ymax) endy = Lyofx(Lxofy(ymax)) endz = Lzofx(Lxofy(ymax)) end if endyymax then startx = Lxofy(ymax) starty = Lyofx(Lxofy(ymax)) startz = Lzofx(Lxofy(ymax)) end if startyzmax then endx = Lxofz(zmax) endy = Lyofx(Lxofz(zmax)) endz = Lzofx(Lxofz(zmax)) end if endzzmax then startx = Lxofz(zmax) starty = Lyofx(Lxofz(zmax)) startz = Lzofx(Lxofz(zmax)) end if startzxmax then endx = Lxofy(Lyofx(xmax)) endy = Lyofx(xmax) endz = Lzofy(Lyofx(xmax)) end if endxxmax then startx = Lxofy(Lyofx(xmax)) starty = Lyofx(xmax) startz = Lzofy(Lyofx(xmax)) end if startxzmax then endx = Lxofy(Lyofz(zmax)) endy = Lyofz(zmax) endz = Lzofy(Lyofz(zmax)) end if endzzmax then startx = Lxofy(Lyofz(zmax)) starty = Lyofz(zmax) startz = Lzofy(Lyofz(zmax)) end if startzxmax then endx = Lxofz(Lzofx(xmax)) endy = Lyofz(Lzofx(xmax)) endz = Lzofx(xmax) end if endxymax then endx = Lxofz(Lzofy(ymax)) endy = Lyofz(Lzofy(ymax)) endz = Lzofy(ymax) end if endyxmax then startx = Lxofz(Lzofx(xmax)) starty = Lyofz(Lzofx(xmax)) startz = Lzofx(xmax) end if startxymax then startx = Lxofz(Lzofy(ymax)) starty = Lyofz(Lzofy(ymax)) startz = Lzofy(ymax) end if starty xmin2 and ymin1 < ymax2 and ymax1 > ymin2 ) end local function point_in_basis(point, basis) local origin = basis.origin[1] -- {x, y, z, 1} local u = basis.basis[1] -- {x, y, z, 1} local v = basis.basis[2] -- {x, y, z, 1} -- Extract direction vectors u and v (subtract origin) local u_vec = {{ u[1] - origin[1], u[2] - origin[2], u[3] - origin[3] }} local v_vec = {{ v[1] - origin[1], v[2] - origin[2], v[3] - origin[3] }} -- Compute w = point - origin local w = {{ point[1][1] - origin[1], point[1][2] - origin[2], point[1][3] - origin[3] }} -- Matrix A = [u_vec; v_vec] as 3×2 local A = { {u_vec[1][1], v_vec[1][1]}, {u_vec[1][2], v_vec[1][2]}, {u_vec[1][3], v_vec[1][3]} } -- Aᵀ A local AT = mm.transpose(A) local ATA = mm.matrix_multiply(AT, A) -- Inverse of Aᵀ A local ATA_inv = mm.inverse(ATA) -- Aᵀ w local w_matrix = { {w[1][1]}, {w[1][2]}, {w[1][3]} } local ATw = mm.matrix_multiply(AT, w_matrix) -- Solve [α; β] = (Aᵀ A)^−1 · Aᵀ w local result = mm.matrix_multiply(ATA_inv, ATw) local alpha = result[1][1] local beta = result[2][1] return {{alpha, beta, 0, 1}} end local function is_intersecting(triangle, line) local T = triangle local P = {T[1]} local Q = {T[2]} local R = {T[3]} local PQ = matrix_subtract(Q,P) local PR = matrix_subtract(R,P) local O = P local basis = { origin = O, u = PQ, v = PR } local L = line local S = {L[1]} local E = {L[2]} local d = matrix_subtract(E,S) end local function clip_triangles() local broken_segs = {} local used_i = {} for i, seg in ipairs(segments) do table.insert(used_i, i) for j, seg2 in ipairs(segments) do if not used_i[j] then local bb1 = get_bounding_box(seg) local bb2 = get_bounding_box(seg2) local T1 = seg.segment local T2 = seg2.segment if is_overlapping(bb1, bb2) then local bb = combine_bounding_boxes(bb1, bb2) local u1 = mm.matrix_subtract( {T1[2]} ,{T1[1]} ) local v1 = mm.matrix_subtract( {T1[3]} ,{T1[1]} ) local n1 = mm.cross_product(u1, v1) local d1 = mm.dot_product(n1, {T1[1]}) local u2 = mm.matrix_subtract( {T2[2]} ,{T2[1]} ) local v2 = mm.matrix_subtract( {T2[3]} ,{T2[1]} ) local n2 = mm.cross_product(u2, v2) local d2 = mm.dot_product(n2, {T2[1]}) local clipping_line = get_line{ xmin = bb.xmin ,xmax = bb.xmax ,ymin = bb.ymin ,ymax = bb.ymax ,zmin = bb.zmin ,zmax = bb.zmax ,a1 = n1[1][1] ,b1 = n1[1][2] ,c1 = n1[1][3] ,d1 = d1 ,a2 = n2[1][1] ,b2 = n2[1][2] ,c2 = n2[1][3] ,d2 = d2 } end end end end end local function render_segments() table.sort(segments, compare_triangles) for _, segment in ipairs(segments) do local pts = segment.segment local skip = false -- check bounding box for every point for _, P in ipairs(pts) do if math.abs(P[1]) > 100 or math.abs(P[2]) > 100 then skip = true break end end if not skip then if #pts == 2 then -- draw a line segment local S, E = pts[1], pts[2] tex.sprint(string.format( "\\path[%s] (%f,%f) -- (%f,%f);", segment.draw_options, S[1], S[2], E[1], E[2] )) elseif #pts == 3 then -- draw a filled triangle local P, Q, R = pts[1], pts[2], pts[3] tex.sprint(string.format( "\\path[preaction={%s},postaction={%s}] (%f,%f) -- (%f,%f) -- (%f,%f) -- cycle;", segment.fill_options, segment.draw_options, P[1], P[2], Q[1], Q[2], R[1], R[2] )) elseif #pts > 3 then -- draw a general polygon local path = {} for _, P in ipairs(pts) do table.insert(path, string.format("(%f,%f)", P[1], P[2])) end tex.sprint(string.format( "\\path[preaction={%s},postaction={%s}] %s -- cycle;", segment.fill_options or "", segment.draw_options or "", table.concat(path, " -- ") )) elseif #pts == 1 and segment.text then local P = pts[1] tex.sprint(string.format( "\\node at (%f,%f) {%s};", P[1], P[2], segment.text or "" )) elseif #pts == 1 and not segment.text then local P = pts[1] tex.sprint(string.format( "\\path[preaction={%s},postaction={%s}] (%f,%f) circle[radius = 0.06];", segment.fill_options or "", segment.draw_options or "", P[1], P[2] )) end end end -- clear for next frame segments = {} end rtc.register_tex_cmd( "appendlabel", function() append_label{ x = token.get_macro("tikz@td@p@l@x"), y = token.get_macro("tikz@td@p@l@y"), z = token.get_macro("tikz@td@p@l@z"), text = token.get_macro("tikz@td@p@l@text"), name = token.get_macro("tikz@td@p@l@name"), transformation = token.get_macro("tikz@td@p@l@transformation") } end, { } ) rtc.register_tex_cmd( "appendpoint", function() append_point{ x = token.get_macro("tikz@td@p@p@x"), y = token.get_macro("tikz@td@p@p@y"), z = token.get_macro("tikz@td@p@p@z"), draw_options = token.get_macro("tikz@td@p@p@drawoptions"), fill_options = token.get_macro("tikz@td@p@p@filloptions"), name = token.get_macro("tikz@td@p@p@name"), transformation = token.get_macro("tikz@td@p@p@transformation") } end, { } ) rtc.register_tex_cmd( "appendcurve", function() append_curve{ u_start = token.get_macro("tikz@td@p@c@umin"), u_stop = token.get_macro("tikz@td@p@c@umax"), u_samples = token.get_macro("tikz@td@p@c@usamples"), x = token.get_macro("tikz@td@p@c@x"), y = token.get_macro("tikz@td@p@c@y"), z = token.get_macro("tikz@td@p@c@z"), draw_options = token.get_macro("tikz@td@p@c@drawoptions"), name = token.get_macro("tikz@td@p@c@name"), transformation = token.get_macro("tikz@td@p@c@transformation") } end, { } ) rtc.register_tex_cmd( "appendsurface", function() append_surface{ u_start = token.get_macro("tikz@td@p@surf@umin"), u_stop = token.get_macro("tikz@td@p@surf@umax"), u_samples = token.get_macro("tikz@td@p@surf@usamples"), v_start = token.get_macro("tikz@td@p@surf@vmin"), v_stop = token.get_macro("tikz@td@p@surf@vmax"), v_samples = token.get_macro("tikz@td@p@surf@vsamples"), x = token.get_macro("tikz@td@p@surf@x"), y = token.get_macro("tikz@td@p@surf@y"), z = token.get_macro("tikz@td@p@surf@z"), draw_options = token.get_macro("tikz@td@p@surf@drawoptions"), fill_options = token.get_macro("tikz@td@p@surf@filloptions"), name = token.get_macro("tikz@td@p@surf@name"), transformation = token.get_macro("tikz@td@p@surf@transformation") } end, { } ) rtc.register_tex_cmd("rendersegments", function() render_segments() end, { }) local function append_plane(hash) local a = single_string_expression(hash.a) local b = single_string_expression(hash.b) local c = single_string_expression(hash.c) local d = single_string_expression(hash.d) local xmin = single_string_expression(hash.xmin) local xmax = single_string_expression(hash.xmax) local ymin = single_string_expression(hash.ymin) local ymax = single_string_expression(hash.ymax) local zmin = single_string_expression(hash.zmin) local zmax = single_string_expression(hash.zmax) local fill_options = hash.fill_options local draw_options = hash.draw_options local transform = single_string_expression(hash.transformation) local normal = {{a,b,c,1}} local intersections = {} local sorted_intersections = {} local function plane(u,v) local x = (d-normal[1][2]*u-normal[1][3]*v)/normal[1][1] local y = (d-normal[1][1]*u-normal[1][3]*v)/normal[1][2] local z = (d-normal[1][1]*u-normal[1][2]*v)/normal[1][3] local result = {{x,y,z,1}} return result end local function add_intersection(i) if ( i[1][1]>=xmin and i[1][1]<=xmax and i[1][2]>=ymin and i[1][2]<=ymax and i[1][3]>=zmin and i[1][3]<=zmax ) then table.insert(intersections,{{i[1][1],i[1][2],i[1][3],1}}) end end if normal[1][1]~=0 then for y = ymin, ymax, ymax-ymin do for z = zmin, zmax, zmax-zmin do local x = plane(y,z)[1][1] add_intersection({{x,y,z,1}}) end end end if normal[1][2]~=0 then for x = xmin, xmax, xmax-xmin do for z = zmin, zmax, zmax-zmin do local y = plane(x,z)[1][2] add_intersection({{x,y,z,1}}) end end end if normal[1][3]~=0 then for x = xmin, xmax, xmax-xmin do for y = ymin, ymax, ymax-ymin do local z = plane(x,y)[1][3] add_intersection({{x,y,z,1}}) end end end local n = mm.normalize(normal) local u = mm.orthogonal_vector(n) local u = mm.normalize(u) local v = mm.cross_product(n,u) local centroid = {{0,0,0,1}} local number_of_points = 0 for index, value in ipairs(intersections) do centroid[1][1] = centroid[1][1]+value[1][1] centroid[1][2] = centroid[1][2]+value[1][2] centroid[1][3] = centroid[1][3]+value[1][3] number_of_points = number_of_points + 1 end local centroid = {{ centroid[1][1]/number_of_points ,centroid[1][2]/number_of_points ,centroid[1][3]/number_of_points ,1 }} for index, value in ipairs(intersections) do local ax = mm.dot_product( {{ value[1][1]-centroid[1][1] ,value[1][2]-centroid[1][2] ,value[1][3]-centroid[1][3] ,1 }},{{u[1][1],u[1][2],u[1][3],1}} ) local ay = mm.dot_product( {{ value[1][1]-centroid[1][1] ,value[1][2]-centroid[1][2] ,value[1][3]-centroid[1][3] ,1 }},{{v[1][1],v[1][2],v[1][3],1}} ) local anglea = math.atan2(ay,ax) table.insert( sorted_intersections ,{angle = anglea, point = {{value[1][1],value[1][2],value[1][3],1}}, draw_options = draw_options, fill_options = fill_options} ) end table.sort( sorted_intersections ,function(a, b) return a.angle < b.angle end ) local result = { segment = {}, draw_options = draw_options, fill_options = fill_options } for i, plane in ipairs(sorted_intersections) do table.insert(result.segment,plane.point) end for i, point in ipairs(result.segment) do result.segment[i] = mm.matrix_multiply(result.segment[i],transform)[1] end table.insert(segments,result) end rtc.register_tex_cmd( "appendplane", function() append_plane{ a = token.get_macro("tikz@td@cs@p@a"), b = token.get_macro("tikz@td@cs@p@b"), c = token.get_macro("tikz@td@cs@p@c"), d = token.get_macro("tikz@td@cs@p@d"), xmin = token.get_macro("tikz@td@cs@xmin"), xmax = token.get_macro("tikz@td@cs@xmax"), ymin = token.get_macro("tikz@td@cs@ymin"), ymax = token.get_macro("tikz@td@cs@ymax"), zmin = token.get_macro("tikz@td@cs@zmin"), zmax = token.get_macro("tikz@td@cs@zmax"), fill_options = token.get_macro("tikz@td@cs@p@filloptions"), draw_options = token.get_macro("tikz@td@cs@p@drawoptions"), transformation = token.get_macro("tikz@td@cs@transformation") } end, { } )